diff options
876 files changed, 24785 insertions, 22394 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e8746d3a3f4..c7b1558fe2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,7 @@ option(WITH_IMAGE_TIFF "Enable LibTIFF Support" ON) option(WITH_IMAGE_DDS "Enable DDS Image Support" ON) option(WITH_IMAGE_CINEON "Enable CINEON and DPX Image Support" ON) option(WITH_IMAGE_HDR "Enable HDR Image Support" ON) +option(WITH_IMAGE_WEBP "Enable WebP Image Support" OFF) # Audio/Video format support option(WITH_CODEC_AVI "Enable Blenders own AVI file support (raw/jpeg)" ON) @@ -408,6 +409,8 @@ option(WITH_CYCLES_DEBUG "Build Cycles with options useful for debug option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF) option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF) +option(WITH_CYCLES_HYDRA_RENDER_DELEGATE "Build Cycles Hydra render delegate" OFF) + option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF) option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF) option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF) @@ -443,7 +446,7 @@ if(NOT APPLE) endif() option(WITH_CYCLES_HIP_BINARIES "Build Cycles AMD HIP binaries" OFF) - set(CYCLES_HIP_BINARIES_ARCH gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for") + set(CYCLES_HIP_BINARIES_ARCH gfx900 gfx906 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for") mark_as_advanced(WITH_CYCLES_DEVICE_HIP) mark_as_advanced(CYCLES_HIP_BINARIES_ARCH) endif() @@ -531,6 +534,19 @@ mark_as_advanced( WITH_GPU_SHADER_BUILDER ) +# Metal + +if (APPLE) + option(WITH_METAL_BACKEND "Use Metal for graphics instead of (or as well as) OpenGL on macOS." OFF) + mark_as_advanced(WITH_METAL_BACKEND) +else() + set(WITH_METAL_BACKEND OFF) +endif() + +if (WITH_METAL_BACKEND) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE) +endif() + if(WIN32) option(WITH_GL_ANGLE "Link with the ANGLE library, an OpenGL ES 2.0 implementation based on Direct3D, instead of the system OpenGL library." OFF) mark_as_advanced(WITH_GL_ANGLE) @@ -729,9 +745,10 @@ endif() #----------------------------------------------------------------------------- # Check for conflicting/unsupported configurations -if(NOT WITH_BLENDER AND NOT WITH_CYCLES_STANDALONE) +if(NOT WITH_BLENDER AND NOT WITH_CYCLES_STANDALONE AND NOT WITH_CYCLES_HYDRA_RENDER_DELEGATE) message(FATAL_ERROR "At least one of WITH_BLENDER or WITH_CYCLES_STANDALONE " + "or WITH_CYCLES_HYDRA_RENDER_DELEGATE " "must be enabled, nothing to do!" ) endif() @@ -1275,6 +1292,16 @@ else() endif() #----------------------------------------------------------------------------- +# Configure Metal. +if (WITH_METAL_BACKEND) + add_definitions(-DWITH_METAL_BACKEND) + + # No need to add frameworks here, all the ones we need for Metal and + # Metal-OpenGL Interop are already being added by + # build_files/cmake/platform/platform_apple.cmake +endif() + +#----------------------------------------------------------------------------- # Configure OpenMP. if(WITH_OPENMP) if(NOT OPENMP_CUSTOM) @@ -1668,6 +1695,8 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_DUPLICATE_ENUM -Wno-duplicate-enum) ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_UNDEF -Wno-undef) ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_MISSING_NORETURN -Wno-missing-noreturn) + ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_UNUSED_BUT_SET_VARIABLE -Wno-unused-but-set-variable) + ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_DEPRECATED_DECLARATIONS -Wno-deprecated-declarations) ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_PARAMETER -Wno-unused-parameter) ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_PRIVATE_FIELD -Wno-unused-private-field) @@ -1882,14 +1911,13 @@ if(WITH_BLENDER) # source after intern and extern to gather all # internal and external library information first, for test linking add_subdirectory(source) -elseif(WITH_CYCLES_STANDALONE) +elseif(WITH_CYCLES_STANDALONE OR WITH_CYCLES_HYDRA_RENDER_DELEGATE) add_subdirectory(intern/glew-mx) add_subdirectory(intern/guardedalloc) add_subdirectory(intern/libc_compat) add_subdirectory(intern/sky) add_subdirectory(intern/cycles) - add_subdirectory(extern/clew) if(WITH_CYCLES_LOGGING) if(NOT WITH_SYSTEM_GFLAGS) add_subdirectory(extern/gflags) diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index b92073636f5..5ca46c15d8d 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -1,11 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-or-later +## Update and uncomment this in the release branch +# set(BLENDER_VERSION 3.1) + function(download_source dep) set(TARGET_FILE ${${dep}_FILE}) set(TARGET_HASH_TYPE ${${dep}_HASH_TYPE}) set(TARGET_HASH ${${dep}_HASH}) if(PACKAGE_USE_UPSTREAM_SOURCES) set(TARGET_URI ${${dep}_URI}) + elseif(BLENDER_VERSION) + set(TARGET_URI https://svn.blender.org/svnroot/bf-blender/tags/blender-${BLENDER_VERSION}-release/lib/packages/${TARGET_FILE}) else() set(TARGET_URI https://svn.blender.org/svnroot/bf-blender/trunk/lib/packages/${TARGET_FILE}) endif() diff --git a/build_files/build_environment/cmake/usd.cmake b/build_files/build_environment/cmake/usd.cmake index 26b728b5801..afa9f788b07 100644 --- a/build_files/build_environment/cmake/usd.cmake +++ b/build_files/build_environment/cmake/usd.cmake @@ -52,6 +52,14 @@ add_dependencies( external_boost ) +# Since USD 21.11 the libraries are prefixed with "usd_", i.e. "libusd_m.a" became "libusd_usd_m.a". +# See https://github.com/PixarAnimationStudios/USD/blob/release/CHANGELOG.md#2111---2021-11-01 +if (USD_VERSION VERSION_LESS 21.11) + set(PXR_LIB_PREFIX "") +else() + set(PXR_LIB_PREFIX "usd_") +endif() + if(WIN32) # USD currently demands python be available at build time # and then proceeds not to use it, but still checks that the @@ -65,14 +73,14 @@ if(WIN32) if(BUILD_MODE STREQUAL Release) ExternalProject_Add_Step(external_usd after_install COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/usd/ ${HARVEST_TARGET}/usd - COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Release/usd_m.lib ${HARVEST_TARGET}/usd/lib/libusd_m.lib + COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Release/${PXR_LIB_PREFIX}usd_m.lib ${HARVEST_TARGET}/usd/lib/lib${PXR_LIB_PREFIX}usd_m.lib DEPENDEES install ) endif() if(BUILD_MODE STREQUAL Debug) ExternalProject_Add_Step(external_usd after_install COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/usd/lib ${HARVEST_TARGET}/usd/lib - COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Debug/usd_m_d.lib ${HARVEST_TARGET}/usd/lib/libusd_m_d.lib + COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Debug/${PXR_LIB_PREFIX}usd_m_d.lib ${HARVEST_TARGET}/usd/lib/lib${PXR_LIB_PREFIX}usd_m_d.lib DEPENDEES install ) endif() @@ -84,7 +92,7 @@ else() # case (only the shared library). As a result, we need to grab the `libusd_m.a` # file from the build directory instead of from the install directory. ExternalProject_Add_Step(external_usd after_install - COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/libusd_m.a ${HARVEST_TARGET}/usd/lib/libusd_m.a + COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/lib${PXR_LIB_PREFIX}usd_m.a ${HARVEST_TARGET}/usd/lib/lib${PXR_LIB_PREFIX}usd_m.a DEPENDEES install ) endif() diff --git a/build_files/cmake/Modules/FindUSD.cmake b/build_files/cmake/Modules/FindUSD.cmake index 840fa2d538f..d8f2ee22e6e 100644 --- a/build_files/cmake/Modules/FindUSD.cmake +++ b/build_files/cmake/Modules/FindUSD.cmake @@ -32,9 +32,12 @@ FIND_PATH(USD_INCLUDE_DIR DOC "Universal Scene Description (USD) header files" ) +# Since USD 21.11 the libraries are prefixed with "usd_", i.e. "libusd_m.a" became "libusd_usd_m.a". +# See https://github.com/PixarAnimationStudios/USD/blob/release/CHANGELOG.md#2111---2021-11-01 FIND_LIBRARY(USD_LIBRARY NAMES - usd_m usd_ms + usd_usd_m usd_usd_ms usd_m usd_ms + ${PXR_LIB_PREFIX}usd NAMES_PER_DIR HINTS ${_usd_SEARCH_DIRS} diff --git a/build_files/cmake/Modules/FindWebP.cmake b/build_files/cmake/Modules/FindWebP.cmake new file mode 100644 index 00000000000..741c48ec447 --- /dev/null +++ b/build_files/cmake/Modules/FindWebP.cmake @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2022 Blender Foundation. + +# - Find WebP library +# Find the native WebP includes and library +# This module defines +# WEBP_INCLUDE_DIRS, where to find WebP headers, Set when WebP is found. +# WEBP_LIBRARIES, libraries to link against to use WebP. +# WEBP_ROOT_DIR, The base directory to search for WebP. +# This can also be an environment variable. +# WEBP_FOUND, If false, do not try to use WebP. +# +# also defined, but not for general use are +# WEBP_LIBRARY, where to find the WEBP library. + +# If WEBP_ROOT_DIR was defined in the environment, use it. +IF(NOT WEBP_ROOT_DIR AND NOT $ENV{WEBP_ROOT_DIR} STREQUAL "") + SET(WEBP_ROOT_DIR $ENV{WEBP_ROOT_DIR}) +ENDIF() + +SET(_webp_SEARCH_DIRS + ${WEBP_ROOT_DIR} + /opt/lib/webp +) + +FIND_PATH(WEBP_INCLUDE_DIR + NAMES + webp/types.h + HINTS + ${_webp_SEARCH_DIRS} + PATH_SUFFIXES + include +) + +SET(_webp_FIND_COMPONENTS + webp + webpmux + webpdemux +) + +SET(_webp_LIBRARIES) +FOREACH(COMPONENT ${_webp_FIND_COMPONENTS}) + STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) + + FIND_LIBRARY(WEBP_${UPPERCOMPONENT}_LIBRARY + NAMES + ${COMPONENT} + NAMES_PER_DIR + HINTS + ${_webp_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib lib/static + ) + LIST(APPEND _webp_LIBRARIES "${WEBP_${UPPERCOMPONENT}_LIBRARY}") +ENDFOREACH() + +IF(${WEBP_WEBP_LIBRARY_NOTFOUND}) + set(WEBP_FOUND FALSE) +ELSE() + # handle the QUIETLY and REQUIRED arguments and set WEBP_FOUND to TRUE if + # all listed variables are TRUE + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WebP DEFAULT_MSG _webp_LIBRARIES WEBP_INCLUDE_DIR) + + IF(WEBP_FOUND) + get_filename_component(WEBP_LIBRARY_DIR ${WEBP_WEBP_LIBRARY} DIRECTORY) + SET(WEBP_INCLUDE_DIRS ${WEBP_INCLUDE_DIR}) + SET(WEBP_LIBRARIES ${_webp_LIBRARIES}) + ELSE() + SET(WEBPL_PUGIXML_FOUND FALSE) + ENDIF() +ENDIF() + +MARK_AS_ADVANCED( + WEBP_INCLUDE_DIR + WEBP_LIBRARY_DIR +) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index bf3e56922c9..5508e8f2104 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -879,7 +879,7 @@ function(delayed_install destination) foreach(f ${files}) - if(IS_ABSOLUTE ${f}) + if(IS_ABSOLUTE ${f} OR "${base}" STREQUAL "") set_property(GLOBAL APPEND PROPERTY DELAYED_INSTALL_FILES ${f}) else() set_property(GLOBAL APPEND PROPERTY DELAYED_INSTALL_FILES ${base}/${f}) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index b09f2f8917b..43ce23081af 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -232,6 +232,15 @@ if(WITH_IMAGE_TIFF) endif() endif() +if(WITH_IMAGE_WEBP) + set(WEBP_ROOT_DIR ${LIBDIR}/webp) + find_package(WebP) + if(NOT WEBP_FOUND) + message(WARNING "WebP not found, disabling WITH_IMAGE_WEBP") + set(WITH_IMAGE_WEBP OFF) + endif() +endif() + if(WITH_BOOST) set(Boost_NO_BOOST_CMAKE ON) set(BOOST_ROOT ${LIBDIR}/boost) diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 0a7119802c8..cc168476d5d 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -368,6 +368,14 @@ if(WITH_PUGIXML) endif() endif() +if(WITH_IMAGE_WEBP) + set(WEBP_ROOT_DIR ${LIBDIR}/webp) + find_package_wrapper(WebP) + if(NOT WEBP_FOUND) + set(WITH_IMAGE_WEBP OFF) + endif() +endif() + if(WITH_OPENIMAGEIO) find_package_wrapper(OpenImageIO) set(OPENIMAGEIO_LIBRARIES diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index e2e49ca0bcd..9418f74994b 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -39,12 +39,12 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang") set(WITH_WINDOWS_STRIPPED_PDB OFF) endif() else() - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16 + if(WITH_BLENDER AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16 message(FATAL_ERROR "Compiler is unsupported, MSVC 2019 16.9.16 or newer is required for building blender.") endif() endif() -if(NOT WITH_PYTHON_MODULE) +if(WITH_BLENDER AND NOT WITH_PYTHON_MODULE) set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT blender) endif() @@ -238,7 +238,6 @@ else() endif() if(NOT DEFINED LIBDIR) - # Setup 64bit and 64bit windows systems if(CMAKE_CL_64) message(STATUS "64 bit compiler detected.") @@ -252,6 +251,9 @@ if(NOT DEFINED LIBDIR) elseif(MSVC_VERSION GREATER 1919) message(STATUS "Visual Studio 2019 detected.") set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) + elseif(MSVC_VERSION GREATER 1909) + message(STATUS "Visual Studio 2017 detected.") + set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) endif() else() message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}") @@ -300,9 +302,8 @@ set(ZLIB_INCLUDE_DIR ${LIBDIR}/zlib/include) set(ZLIB_LIBRARY ${LIBDIR}/zlib/lib/libz_st.lib) set(ZLIB_DIR ${LIBDIR}/zlib) -windows_find_package(zlib) # we want to find before finding things that depend on it like png -windows_find_package(png) - +windows_find_package(ZLIB) # we want to find before finding things that depend on it like png +windows_find_package(PNG) if(NOT PNG_FOUND) warn_hardcoded_paths(libpng) set(PNG_PNG_INCLUDE_DIR ${LIBDIR}/png/include) @@ -313,9 +314,9 @@ if(NOT PNG_FOUND) endif() set(JPEG_NAMES ${JPEG_NAMES} libjpeg) -windows_find_package(jpeg REQUIRED) +windows_find_package(JPEG REQUIRED) if(NOT JPEG_FOUND) - warn_hardcoded_paths(jpeg) + warn_hardcoded_paths(libjpeg) set(JPEG_INCLUDE_DIR ${LIBDIR}/jpeg/include) set(JPEG_LIBRARIES ${LIBDIR}/jpeg/lib/libjpeg.lib) endif() @@ -333,7 +334,7 @@ set(FREETYPE_LIBRARIES ${LIBDIR}/brotli/lib/brotlidec-static.lib ${LIBDIR}/brotli/lib/brotlicommon-static.lib ) -windows_find_package(freetype REQUIRED) +windows_find_package(Freetype REQUIRED) if(WITH_FFTW3) set(FFTW3 ${LIBDIR}/fftw3) @@ -342,6 +343,14 @@ if(WITH_FFTW3) set(FFTW3_LIBPATH ${FFTW3}/lib) endif() +windows_find_package(WebP) +if(NOT WEBP_FOUND) + set(WEBP_INCLUDE_DIRS ${LIBDIR}/webp/include) + set(WEBP_ROOT_DIR ${LIBDIR}/webp) + set(WEBP_LIBRARIES ${LIBDIR}/webp/lib/webp.lib ${LIBDIR}/webp/lib/webpdemux.lib ${LIBDIR}/webp/lib/webpmux.lib) + set(WEBP_FOUND ON) +endif() + if(WITH_OPENCOLLADA) set(OPENCOLLADA ${LIBDIR}/opencollada) @@ -389,9 +398,9 @@ if(WITH_CODEC_FFMPEG) ${LIBDIR}/ffmpeg/include ${LIBDIR}/ffmpeg/include/msvc ) - windows_find_package(FFMPEG) + windows_find_package(FFmpeg) if(NOT FFMPEG_FOUND) - warn_hardcoded_paths(ffmpeg) + warn_hardcoded_paths(FFmpeg) set(FFMPEG_LIBRARIES ${LIBDIR}/ffmpeg/lib/avcodec.lib ${LIBDIR}/ffmpeg/lib/avformat.lib @@ -403,10 +412,10 @@ if(WITH_CODEC_FFMPEG) endif() if(WITH_IMAGE_OPENEXR) - set(OPENEXR_ROOT_DIR ${LIBDIR}/openexr) - set(OPENEXR_VERSION "2.1") - windows_find_package(OPENEXR REQUIRED) + windows_find_package(OpenEXR REQUIRED) if(NOT OPENEXR_FOUND) + set(OPENEXR_ROOT_DIR ${LIBDIR}/openexr) + set(OPENEXR_VERSION "2.1") warn_hardcoded_paths(OpenEXR) set(OPENEXR ${LIBDIR}/openexr) set(OPENEXR_INCLUDE_DIR ${OPENEXR}/include) @@ -624,21 +633,23 @@ if(WITH_IMAGE_OPENJPEG) endif() if(WITH_OPENSUBDIV) - set(OPENSUBDIV_INCLUDE_DIRS ${LIBDIR}/opensubdiv/include) - set(OPENSUBDIV_LIBPATH ${LIBDIR}/opensubdiv/lib) - set(OPENSUBDIV_LIBRARIES - optimized ${OPENSUBDIV_LIBPATH}/osdCPU.lib - optimized ${OPENSUBDIV_LIBPATH}/osdGPU.lib - debug ${OPENSUBDIV_LIBPATH}/osdCPU_d.lib - debug ${OPENSUBDIV_LIBPATH}/osdGPU_d.lib - ) - set(OPENSUBDIV_HAS_OPENMP TRUE) - set(OPENSUBDIV_HAS_TBB FALSE) - set(OPENSUBDIV_HAS_OPENCL TRUE) - set(OPENSUBDIV_HAS_CUDA FALSE) - set(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK TRUE) - set(OPENSUBDIV_HAS_GLSL_COMPUTE TRUE) windows_find_package(OpenSubdiv) + if (NOT OpenSubdiv_FOUND) + set(OPENSUBDIV_INCLUDE_DIRS ${LIBDIR}/opensubdiv/include) + set(OPENSUBDIV_LIBPATH ${LIBDIR}/opensubdiv/lib) + set(OPENSUBDIV_LIBRARIES + optimized ${OPENSUBDIV_LIBPATH}/osdCPU.lib + optimized ${OPENSUBDIV_LIBPATH}/osdGPU.lib + debug ${OPENSUBDIV_LIBPATH}/osdCPU_d.lib + debug ${OPENSUBDIV_LIBPATH}/osdGPU_d.lib + ) + set(OPENSUBDIV_HAS_OPENMP TRUE) + set(OPENSUBDIV_HAS_TBB FALSE) + set(OPENSUBDIV_HAS_OPENCL TRUE) + set(OPENSUBDIV_HAS_CUDA FALSE) + set(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK TRUE) + set(OPENSUBDIV_HAS_GLSL_COMPUTE TRUE) + endif() endif() if(WITH_SDL) @@ -659,12 +670,15 @@ if(WITH_SYSTEM_AUDASPACE) endif() if(WITH_TBB) - set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib) - set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include) - set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) - if(WITH_TBB_MALLOC_PROXY) - set(TBB_MALLOC_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbbmalloc.lib debug ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib) - add_definitions(-DWITH_TBB_MALLOC) + windows_find_package(TBB) + if (NOT TBB_FOUND) + set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib) + set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include) + set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) + if(WITH_TBB_MALLOC_PROXY) + set(TBB_MALLOC_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbbmalloc.lib debug ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib) + add_definitions(-DWITH_TBB_MALLOC) + endif() endif() endif() diff --git a/doc/license/LGPL2.1-license.txt b/doc/license/LGPL2.1-license.txt new file mode 100644 index 00000000000..e5ab03e1238 --- /dev/null +++ b/doc/license/LGPL2.1-license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/doc/license/SPDX-license-identifiers.txt b/doc/license/SPDX-license-identifiers.txt index 41736ff8ce5..705169b1af7 100644 --- a/doc/license/SPDX-license-identifiers.txt +++ b/doc/license/SPDX-license-identifiers.txt @@ -5,5 +5,6 @@ BSD-2-Clause BSD-2-Clause-license.txt https://spdx.org/licenses/BS BSD-3-Clause BSD-3-Clause-license.txt https://spdx.org/licenses/BSD-3-Clause.html GPL-2.0-or-later GPL-license.txt https://spdx.org/licenses/GPL-2.0-or-later.html GPL-3.0-or-later GPL3-license.txt https://spdx.org/licenses/GPL-3.0-or-later.html +LGPL-2.1-or-later LGPL2.1-license.txt https://spdx.org/licenses/LGPL-2.1-or-later.html MIT MIT-license.txt https://spdx.org/licenses/MIT.html Zlib Zlib-license.txt https://spdx.org/licenses/Zlib.html diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index ad2b5ab4d7b..1cc3dccf426 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -2,8 +2,12 @@ # Copyright 2011-2022 Blender Foundation # Standalone or with Blender -if(NOT WITH_BLENDER AND WITH_CYCLES_STANDALONE) - set(CYCLES_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}) +if(NOT WITH_BLENDER) + if(WITH_CYCLES_STANDALONE OR NOT WITH_CYCLES_HYDRA_RENDER_DELEGATE) + set(CYCLES_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}) + else() + set(CYCLES_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/hdCycles/resources) + endif() else() set(WITH_CYCLES_BLENDER ON) # WINDOWS_PYTHON_DEBUG needs to write into the user addons folder since it will @@ -335,6 +339,11 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang") unset(_has_no_error_unused_macros) endif() +if(WITH_CYCLES_HYDRA_RENDER_DELEGATE AND NOT WITH_USD) + message(STATUS "USD not found, disabling WITH_CYCLES_HYDRA_RENDER_DELEGATE") + set(WITH_CYCLES_HYDRA_RENDER_DELEGATE OFF) +endif() + if(WITH_CYCLES_CUDA_BINARIES AND (NOT WITH_CYCLES_CUBIN_COMPILER)) if(MSVC) set(MAX_MSVC 1800) @@ -395,6 +404,10 @@ if(WITH_GTESTS) add_subdirectory(test) endif() -if(NOT WITH_BLENDER AND WITH_CYCLES_STANDALONE) +if(WITH_CYCLES_HYDRA_RENDER_DELEGATE) + add_subdirectory(hydra) +endif() + +if(NOT WITH_BLENDER) delayed_do_install(${CMAKE_BINARY_DIR}/bin) endif() diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index 3248ef0dcda..484d99c5ca7 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -22,6 +22,16 @@ set(LIBRARIES cycles_util ) +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + list(APPEND INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ) + list(APPEND LIB + ${ALEMBIC_LIBRARIES} + ) +endif() + if(WITH_CYCLES_OSL) list(APPEND LIBRARIES cycles_kernel_osl) endif() diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 50a983022a3..723f4fd77b9 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -9,6 +9,7 @@ #include "graph/node_xml.h" +#include "scene/alembic.h" #include "scene/background.h" #include "scene/camera.h" #include "scene/film.h" @@ -192,6 +193,31 @@ static void xml_read_camera(XMLReadState &state, xml_node node) cam->update(state.scene); } +/* Alembic */ + +#ifdef WITH_ALEMBIC +static void xml_read_alembic(XMLReadState &state, xml_node graph_node) +{ + AlembicProcedural *proc = state.scene->create_node<AlembicProcedural>(); + xml_read_node(state, proc, graph_node); + + for (xml_node node = graph_node.first_child(); node; node = node.next_sibling()) { + if (string_iequals(node.name(), "object")) { + string path; + if (xml_read_string(&path, node, "path")) { + ustring object_path(path, 0); + AlembicObject *object = static_cast<AlembicObject *>( + proc->get_or_create_object(object_path)); + + array<Node *> used_shaders = object->get_used_shaders(); + used_shaders.push_back_slow(state.shader); + object->set_used_shaders(used_shaders); + } + } + } +} +#endif + /* Shader */ static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node graph_node) @@ -647,6 +673,11 @@ static void xml_read_scene(XMLReadState &state, xml_node scene_node) if (xml_read_string(&src, node, "src")) xml_read_include(state, src); } +#ifdef WITH_ALEMBIC + else if (string_iequals(node.name(), "alembic")) { + xml_read_alembic(state, node); + } +#endif else fprintf(stderr, "Unknown node \"%s\".\n", node.name()); } diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py index 5578e83b19a..74b28b8ea21 100644 --- a/intern/cycles/blender/addon/__init__.py +++ b/intern/cycles/blender/addon/__init__.py @@ -83,6 +83,17 @@ class CyclesRender(bpy.types.RenderEngine): # viewport render def view_update(self, context, depsgraph): if not self.session: + # When starting a new render session in viewport (by switching + # viewport to Rendered shading) unpause the render. The way to think + # of it is: artist requests render, so we start to render. + # Do it for both original and evaluated scene so that Cycles + # immediately reacts to un-paused render. + cscene = context.scene.cycles + cscene_eval = depsgraph.scene_eval.cycles + if cscene.preview_pause or cscene_eval.preview_pause: + cscene.preview_pause = False + cscene_eval.preview_pause = False + engine.create(self, context.blend_data, context.region, context.space_data, context.region_data) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 8896f620b9f..24cc5735c96 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -348,8 +348,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): scrambling_distance: FloatProperty( name="Scrambling Distance", default=1.0, - min=0.0, max=1.0, - description="Reduce randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts if set too low. Only works when not using adaptive sampling", + min=0.0, soft_max=1.0, + description="Reduce randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts if set too low", ) preview_scrambling_distance: BoolProperty( name="Scrambling Distance viewport", @@ -360,7 +360,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): auto_scrambling_distance: BoolProperty( name="Automatic Scrambling Distance", default=False, - description="Automatically reduce the randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts. Only works when not using adaptive sampling", + description="Automatically reduce the randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts", ) use_layer_samples: EnumProperty( @@ -1480,7 +1480,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): col.label(text="and NVIDIA driver version 470 or newer", icon='BLANK1') elif device_type == 'HIP': import sys - col.label(text="Requires discrete AMD GPU with RDNA architecture", icon='BLANK1') + col.label(text="Requires discrete AMD GPU with Vega architecture", icon='BLANK1') if sys.platform[:3] == "win": col.label(text="and AMD Radeon Pro 21.Q4 driver or newer", icon='BLANK1') elif device_type == 'METAL': diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 2b74a1b7ccf..1f50f3da7ae 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -289,11 +289,8 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): layout.separator() heading = layout.column(align=True, heading="Scrambling Distance") - heading.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) heading.prop(cscene, "auto_scrambling_distance", text="Automatic") - sub = heading.row() - sub.active = not cscene.use_preview_adaptive_sampling - sub.prop(cscene, "preview_scrambling_distance", text="Viewport") + heading.prop(cscene, "preview_scrambling_distance", text="Viewport") heading.prop(cscene, "scrambling_distance", text="Multiplier") layout.separator() @@ -1031,7 +1028,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): def poll(cls, context): ob = context.object if CyclesButtonsPanel.poll(context) and ob: - if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'HAIR', 'POINTCLOUD'}: + if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'CURVES', 'POINTCLOUD'}: return True if ob.instance_type == 'COLLECTION' and ob.instance_collection: return True @@ -1070,7 +1067,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): def has_geometry_visibility(ob): - return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT', 'VOLUME', 'POINTCLOUD', 'HAIR'}) or + return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT', 'VOLUME', 'POINTCLOUD', 'CURVES'}) or (ob.instance_type == 'COLLECTION' and ob.instance_collection)) diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp index 054142a9ca4..3a95746d149 100644 --- a/intern/cycles/blender/object.cpp +++ b/intern/cycles/blender/object.cpp @@ -422,7 +422,7 @@ static float4 lookup_instance_property(BL::DepsgraphObjectInstance &b_instance, return value; } - return make_float4(0.0f); + return zero_float4(); } bool BlenderSync::sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object) diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index ec50ad9db9a..224cbea85f3 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -271,6 +271,7 @@ static ShaderNode *add_node(Scene *scene, curves->set_min_x(min_x); curves->set_max_x(max_x); curves->set_curves(curve_mapping_curves); + curves->set_extrapolate(mapping.extend() == mapping.extend_EXTRAPOLATED); node = curves; } if (b_node.is_a(&RNA_ShaderNodeVectorCurve)) { @@ -284,6 +285,7 @@ static ShaderNode *add_node(Scene *scene, curves->set_min_x(min_x); curves->set_max_x(max_x); curves->set_curves(curve_mapping_curves); + curves->set_extrapolate(mapping.extend() == mapping.extend_EXTRAPOLATED); node = curves; } else if (b_node.is_a(&RNA_ShaderNodeFloatCurve)) { @@ -297,6 +299,7 @@ static ShaderNode *add_node(Scene *scene, curve->set_min_x(min_x); curve->set_max_x(max_x); curve->set_curve(curve_mapping_curve); + curve->set_extrapolate(mapping.extend() == mapping.extend_EXTRAPOLATED); node = curve; } else if (b_node.is_a(&RNA_ShaderNodeValToRGB)) { diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index d4949a5ff30..8af2ee7a435 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -346,31 +346,48 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_SOBOL); integrator->set_sampling_pattern(sampling_pattern); + int samples = 1; bool use_adaptive_sampling = false; if (preview) { + samples = get_int(cscene, "preview_samples"); use_adaptive_sampling = RNA_boolean_get(&cscene, "use_preview_adaptive_sampling"); integrator->set_use_adaptive_sampling(use_adaptive_sampling); integrator->set_adaptive_threshold(get_float(cscene, "preview_adaptive_threshold")); integrator->set_adaptive_min_samples(get_int(cscene, "preview_adaptive_min_samples")); } else { + samples = get_int(cscene, "samples"); use_adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling"); integrator->set_use_adaptive_sampling(use_adaptive_sampling); integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold")); integrator->set_adaptive_min_samples(get_int(cscene, "adaptive_min_samples")); } - int samples = get_int(cscene, "samples"); float scrambling_distance = get_float(cscene, "scrambling_distance"); bool auto_scrambling_distance = get_boolean(cscene, "auto_scrambling_distance"); if (auto_scrambling_distance) { + if (samples == 0) { + /* If samples is 0, then viewport rendering is set to render infinitely. In that case we + * override the samples value with 4096 so the Automatic Scrambling Distance algorithm + * picks a Scrambling Distance value with a good balance of performance and correlation + * artifacts when rendering to high sample counts. */ + samples = 4096; + } + + if (use_adaptive_sampling) { + /* If Adaptive Sampling is enabled, use "min_samples" in the Automatic Scrambling Distance + * algorithm to avoid artifacts common with Adaptive Sampling + Scrambling Distance. */ + const AdaptiveSampling adaptive_sampling = integrator->get_adaptive_sampling(); + samples = min(samples, adaptive_sampling.min_samples); + } scrambling_distance *= 4.0f / sqrtf(samples); } - /* only use scrambling distance in the viewport if user wants to and disable with AS */ + /* Only use scrambling distance in the viewport if user wants to. */ bool preview_scrambling_distance = get_boolean(cscene, "preview_scrambling_distance"); - if ((preview && !preview_scrambling_distance) || use_adaptive_sampling) + if (preview && !preview_scrambling_distance) { scrambling_distance = 1.0f; + } if (scrambling_distance != 1.0f) { VLOG(3) << "Using scrambling distance: " << scrambling_distance; diff --git a/intern/cycles/bvh/binning.cpp b/intern/cycles/bvh/binning.cpp index b04fc069c54..5ac7f9c782a 100644 --- a/intern/cycles/bvh/binning.cpp +++ b/intern/cycles/bvh/binning.cpp @@ -203,7 +203,7 @@ BVHObjectBinning::BVHObjectBinning(const BVHRange &job, bestSAH = min(sah, bestSAH); } - int4 mask = float3_to_float4(cent_bounds_.size()) <= make_float4(0.0f); + int4 mask = float3_to_float4(cent_bounds_.size()) <= zero_float4(); bestSAH = insert<3>(select(mask, make_float4(FLT_MAX), bestSAH), FLT_MAX); /* find best dimension */ diff --git a/intern/cycles/cmake/macros.cmake b/intern/cycles/cmake/macros.cmake index 0f2e1b50434..e69e31f8e52 100644 --- a/intern/cycles/cmake/macros.cmake +++ b/intern/cycles/cmake/macros.cmake @@ -101,6 +101,7 @@ macro(cycles_target_link_libraries target) ${PNG_LIBRARIES} ${JPEG_LIBRARIES} ${TIFF_LIBRARY} + ${WEBP_LIBRARIES} ${OPENJPEG_LIBRARIES} ${OPENEXR_LIBRARIES} ${OPENEXR_LIBRARIES} # For circular dependencies between libs. diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index 670285fb310..d4e128093fa 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -197,7 +197,8 @@ cycles_add_library(cycles_device "${LIB}" ${SRC}) source_group("cpu" FILES ${SRC_CPU}) source_group("cuda" FILES ${SRC_CUDA}) source_group("dummy" FILES ${SRC_DUMMY}) +source_group("hip" FILES ${SRC_HIP}) source_group("multi" FILES ${SRC_MULTI}) source_group("metal" FILES ${SRC_METAL}) source_group("optix" FILES ${SRC_OPTIX}) -source_group("common" FILES ${SRC} ${SRC_HEADERS}) +source_group("common" FILES ${SRC_BASE} ${SRC_HEADERS}) diff --git a/intern/cycles/device/hip/util.h b/intern/cycles/device/hip/util.h index adb68a2d44c..4e4906171d1 100644 --- a/intern/cycles/device/hip/util.h +++ b/intern/cycles/device/hip/util.h @@ -51,7 +51,7 @@ static inline bool hipSupportsDevice(const int hipDevId) hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); - return (major > 10) || (major == 10 && minor >= 1); + return (major >= 9); } CCL_NAMESPACE_END diff --git a/intern/cycles/hydra/CMakeLists.txt b/intern/cycles/hydra/CMakeLists.txt new file mode 100644 index 00000000000..703bd955135 --- /dev/null +++ b/intern/cycles/hydra/CMakeLists.txt @@ -0,0 +1,174 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Blender Foundation + +##################################################################### +# Cycles Hydra render delegate +##################################################################### + +set(INC + .. +) +set(INC_SYS + ${USD_INCLUDE_DIRS} + ${GLEW_INCLUDE_DIR} +) + +set(INC_HD_CYCLES + attribute.h + camera.h + config.h + curves.h + display_driver.h + field.h + geometry.h + geometry.inl + instancer.h + light.h + material.h + mesh.h + node_util.h + output_driver.h + pointcloud.h + render_buffer.h + render_delegate.h + render_pass.h + session.h + volume.h +) + +set(SRC_HD_CYCLES + attribute.cpp + curves.cpp + camera.cpp + display_driver.cpp + field.cpp + instancer.cpp + light.cpp + material.cpp + mesh.cpp + node_util.cpp + output_driver.cpp + pointcloud.cpp + render_buffer.cpp + render_delegate.cpp + render_pass.cpp + session.cpp + volume.cpp +) + +add_definitions(${GL_DEFINITIONS}) + +if(WITH_OPENVDB) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENVDB_LIBRARIES} + ) +endif() + +include_directories(${INC}) +include_directories(SYSTEM ${INC_SYS}) + +add_library(hdCyclesStatic STATIC + ${SRC_HD_CYCLES} + ${INC_HD_CYCLES} +) + +target_compile_options(hdCyclesStatic + PRIVATE + $<$<CXX_COMPILER_ID:MSVC>:/wd4003 /wd4244 /wd4506> + $<$<CXX_COMPILER_ID:GNU>:-Wno-float-conversion -Wno-double-promotion -Wno-deprecated> +) + +target_compile_definitions(hdCyclesStatic + PRIVATE + GLOG_NO_ABBREVIATED_SEVERITIES=1 + OSL_DEBUG=$<CONFIG:DEBUG> + TBB_USE_DEBUG=$<CONFIG:DEBUG> + $<$<CXX_COMPILER_ID:MSVC>:NOMINMAX=1> +) + +target_link_libraries(hdCyclesStatic + PRIVATE + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hd${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}plug${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}tf${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}trace${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}vt${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}work${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}sdf${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}cameraUtil${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hf${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}pxOsd${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}gf${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}arch${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hgi${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}glf${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hdx${CMAKE_LINK_LIBRARY_SUFFIX} + ${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}usdGeom${CMAKE_LINK_LIBRARY_SUFFIX} + cycles_scene + cycles_session + cycles_graph +) + +if(USD_PYTHON_LIBRARIES) + target_link_libraries(hdCyclesStatic + PRIVATE + ${USD_PYTHON_LIBRARIES} + ) +endif() + +set(HdCyclesPluginName hdCycles) +add_library(${HdCyclesPluginName} SHARED + plugin.h + plugin.cpp +) + +set_target_properties(${HdCyclesPluginName} + PROPERTIES PREFIX "" +) + +target_compile_definitions(${HdCyclesPluginName} + PRIVATE + MFB_PACKAGE_NAME=${HdCyclesPluginName} + MFB_ALT_PACKAGE_NAME=${HdCyclesPluginName} + GLOG_NO_ABBREVIATED_SEVERITIES=1 + OSL_DEBUG=$<CONFIG:DEBUG> + TBB_USE_DEBUG=$<CONFIG:DEBUG> + $<$<CXX_COMPILER_ID:MSVC>:NOMINMAX=1> +) + +target_link_libraries(${HdCyclesPluginName} + hdCyclesStatic +) + +target_link_directories(${HdCyclesPluginName} + BEFORE + PRIVATE + ${USD_LIBRARY_DIR} +) + +cycles_target_link_libraries(${HdCyclesPluginName}) + +if(WITH_CYCLES_BLENDER) + set(CYCLES_HYDRA_INSTALL_PATH "../") +else() + set(CYCLES_HYDRA_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}) + # Put the root plugInfo.json one level up + delayed_install("${CMAKE_CURRENT_SOURCE_DIR}" "plugInfo.json" ${CMAKE_INSTALL_PREFIX}) +endif() + +delayed_install("" $<TARGET_FILE:${HdCyclesPluginName}> ${CYCLES_HYDRA_INSTALL_PATH}) + +set(PLUG_INFO_ROOT "..") +set(PLUG_INFO_LIBRARY_PATH "../${HdCyclesPluginName}${CMAKE_SHARED_LIBRARY_SUFFIX}") +set(PLUG_INFO_RESOURCE_PATH "resources") + +configure_file(resources/plugInfo.json + ${CMAKE_CURRENT_BINARY_DIR}/resources/plugInfo.json + @ONLY +) + +delayed_install("${CMAKE_CURRENT_BINARY_DIR}/resources" "plugInfo.json" "${CYCLES_HYDRA_INSTALL_PATH}/${HdCyclesPluginName}/resources") diff --git a/intern/cycles/hydra/attribute.cpp b/intern/cycles/hydra/attribute.cpp new file mode 100644 index 00000000000..f22665345e6 --- /dev/null +++ b/intern/cycles/hydra/attribute.cpp @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/attribute.h" +#include "scene/attribute.h" +#include "scene/geometry.h" +#include "scene/scene.h" + +#include <pxr/base/gf/vec2f.h> +#include <pxr/base/gf/vec3f.h> +#include <pxr/base/gf/vec4f.h> +#include <pxr/base/vt/array.h> +#include <pxr/imaging/hd/tokens.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +void ApplyPrimvars(AttributeSet &attributes, + const ustring &name, + VtValue value, + AttributeElement elem, + AttributeStandard std) +{ + const void *data = HdGetValueData(value); + size_t size = value.GetArraySize(); + + const HdType valueType = HdGetValueTupleType(value).type; + + TypeDesc attrType = CCL_NS::TypeUnknown; + switch (valueType) { + case HdTypeFloat: + attrType = CCL_NS::TypeFloat; + size *= sizeof(float); + break; + case HdTypeFloatVec2: + attrType = CCL_NS::TypeFloat2; + size *= sizeof(float2); + static_assert(sizeof(GfVec2f) == sizeof(float2)); + break; + case HdTypeFloatVec3: { + attrType = CCL_NS::TypeVector; + size *= sizeof(float3); + // The Cycles "float3" data type is padded to "float4", so need to convert the array + const auto &valueData = value.Get<VtVec3fArray>(); + VtArray<float3> valueConverted; + valueConverted.reserve(valueData.size()); + for (const GfVec3f &vec : valueData) { + valueConverted.push_back(make_float3(vec[0], vec[1], vec[2])); + } + data = valueConverted.data(); + value = std::move(valueConverted); + break; + } + case HdTypeFloatVec4: + attrType = CCL_NS::TypeFloat4; + size *= sizeof(float4); + static_assert(sizeof(GfVec4f) == sizeof(float4)); + break; + default: + TF_WARN("Unsupported attribute type %d", static_cast<int>(valueType)); + return; + } + + Attribute *const attr = attributes.add(name, attrType, elem); + attr->std = std; + + assert(size == attr->buffer.size()); + std::memcpy(attr->data(), data, size); +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/attribute.h b/intern/cycles/hydra/attribute.h new file mode 100644 index 00000000000..3277f97f44d --- /dev/null +++ b/intern/cycles/hydra/attribute.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "scene/attribute.h" + +#include <pxr/base/vt/value.h> +#include <pxr/imaging/hd/types.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +void ApplyPrimvars(CCL_NS::AttributeSet &attributes, + const CCL_NS::ustring &name, + PXR_NS::VtValue value, + CCL_NS::AttributeElement elem, + CCL_NS::AttributeStandard std); + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/camera.cpp b/intern/cycles/hydra/camera.cpp new file mode 100644 index 00000000000..05f1c32d3c4 --- /dev/null +++ b/intern/cycles/hydra/camera.cpp @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/camera.h" +#include "scene/camera.h" + +#include <pxr/base/gf/frustum.h> +#include <pxr/imaging/hd/sceneDelegate.h> +#include <pxr/usd/usdGeom/tokens.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +extern Transform convert_transform(const GfMatrix4d &matrix); + +HdCyclesCamera::HdCyclesCamera(const SdfPath &sprimId) : HdCamera(sprimId) +{ +#if PXR_VERSION >= 2102 + // Synchronize default values + _horizontalAperture = _data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT; + _verticalAperture = _data.GetVerticalAperture() * GfCamera::APERTURE_UNIT; + _horizontalApertureOffset = _data.GetHorizontalApertureOffset() * GfCamera::APERTURE_UNIT; + _verticalApertureOffset = _data.GetVerticalApertureOffset() * GfCamera::APERTURE_UNIT; + _focalLength = _data.GetFocalLength() * GfCamera::FOCAL_LENGTH_UNIT; + _clippingRange = _data.GetClippingRange(); + _fStop = _data.GetFStop(); + _focusDistance = _data.GetFocusDistance(); +#endif +} + +HdCyclesCamera::~HdCyclesCamera() +{ +} + +HdDirtyBits HdCyclesCamera::GetInitialDirtyBitsMask() const +{ + return DirtyBits::AllDirty; +} + +void HdCyclesCamera::Sync(HdSceneDelegate *sceneDelegate, + HdRenderParam *renderParam, + HdDirtyBits *dirtyBits) +{ + if (*dirtyBits == DirtyBits::Clean) { + return; + } + + VtValue value; + const SdfPath &id = GetId(); + +#if PXR_VERSION >= 2102 + if (*dirtyBits & DirtyBits::DirtyTransform) { + sceneDelegate->SampleTransform(id, &_transformSamples); + + for (size_t i = 0; i < _transformSamples.count; ++i) { + if (_transformSamples.times[i] == 0.0f) { + _transform = _transformSamples.values[i]; + _data.SetTransform(_transform); + break; + } + } + } +#else + if (*dirtyBits & DirtyBits::DirtyViewMatrix) { + sceneDelegate->SampleTransform(id, &_transformSamples); + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->worldToViewMatrix); + if (!value.IsEmpty()) { + _worldToViewMatrix = value.Get<GfMatrix4d>(); + _worldToViewInverseMatrix = _worldToViewMatrix.GetInverse(); + _data.SetTransform(_worldToViewInverseMatrix); + } + } +#endif + + if (*dirtyBits & DirtyBits::DirtyProjMatrix) { + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->projectionMatrix); + if (!value.IsEmpty()) { + _projectionMatrix = value.Get<GfMatrix4d>(); + const float focalLength = _data.GetFocalLength(); // Get default focal length +#if PXR_VERSION >= 2102 + _data.SetFromViewAndProjectionMatrix(GetViewMatrix(), _projectionMatrix, focalLength); +#else + if (_projectionMatrix[2][3] < -0.5) { + _data.SetProjection(GfCamera::Perspective); + + const float horizontalAperture = (2.0 * focalLength) / _projectionMatrix[0][0]; + _data.SetHorizontalAperture(horizontalAperture); + _data.SetHorizontalApertureOffset(0.5 * horizontalAperture * _projectionMatrix[2][0]); + const float verticalAperture = (2.0 * focalLength) / _projectionMatrix[1][1]; + _data.SetVerticalAperture(verticalAperture); + _data.SetVerticalApertureOffset(0.5 * verticalAperture * _projectionMatrix[2][1]); + + _data.SetClippingRange( + GfRange1f(_projectionMatrix[3][2] / (_projectionMatrix[2][2] - 1.0), + _projectionMatrix[3][2] / (_projectionMatrix[2][2] + 1.0))); + } + else { + _data.SetProjection(GfCamera::Orthographic); + + const float horizontalAperture = (2.0 / GfCamera::APERTURE_UNIT) / _projectionMatrix[0][0]; + _data.SetHorizontalAperture(horizontalAperture); + _data.SetHorizontalApertureOffset(-0.5 * horizontalAperture * _projectionMatrix[3][0]); + const float verticalAperture = (2.0 / GfCamera::APERTURE_UNIT) / _projectionMatrix[1][1]; + _data.SetVerticalAperture(verticalAperture); + _data.SetVerticalApertureOffset(-0.5 * verticalAperture * _projectionMatrix[3][1]); + + const double nearMinusFarHalf = 1.0 / _projectionMatrix[2][2]; + const double nearPlusFarHalf = nearMinusFarHalf * _projectionMatrix[3][2]; + _data.SetClippingRange( + GfRange1f(nearPlusFarHalf + nearMinusFarHalf, nearPlusFarHalf - nearMinusFarHalf)); + } +#endif + } + } + + if (*dirtyBits & DirtyBits::DirtyWindowPolicy) { + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->windowPolicy); + if (!value.IsEmpty()) { + _windowPolicy = value.Get<CameraUtilConformWindowPolicy>(); + } + } + + if (*dirtyBits & DirtyBits::DirtyClipPlanes) { + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->clipPlanes); + if (!value.IsEmpty()) { + _clipPlanes = value.Get<std::vector<GfVec4d>>(); + } + } + + if (*dirtyBits & DirtyBits::DirtyParams) { +#if PXR_VERSION >= 2102 + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->projection); + if (!value.IsEmpty()) { + _projection = value.Get<Projection>(); + _data.SetProjection(_projection != Orthographic ? GfCamera::Perspective : + GfCamera::Orthographic); + } +#else + value = sceneDelegate->GetCameraParamValue(id, UsdGeomTokens->projection); + if (!value.IsEmpty()) { + _data.SetProjection(value.Get<TfToken>() != UsdGeomTokens->orthographic ? + GfCamera::Perspective : + GfCamera::Orthographic); + } +#endif + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->horizontalAperture); + if (!value.IsEmpty()) { + const auto horizontalAperture = value.Get<float>(); +#if PXR_VERSION >= 2102 + _horizontalAperture = horizontalAperture; +#endif + _data.SetHorizontalAperture(horizontalAperture / GfCamera::APERTURE_UNIT); + } + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->verticalAperture); + if (!value.IsEmpty()) { + const auto verticalAperture = value.Get<float>(); +#if PXR_VERSION >= 2102 + _verticalAperture = verticalAperture; +#endif + _data.SetVerticalAperture(verticalAperture / GfCamera::APERTURE_UNIT); + } + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->horizontalApertureOffset); + if (!value.IsEmpty()) { + const auto horizontalApertureOffset = value.Get<float>(); +#if PXR_VERSION >= 2102 + _horizontalApertureOffset = horizontalApertureOffset; +#endif + _data.SetHorizontalApertureOffset(horizontalApertureOffset / GfCamera::APERTURE_UNIT); + } + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->verticalApertureOffset); + if (!value.IsEmpty()) { + const auto verticalApertureOffset = value.Get<float>(); +#if PXR_VERSION >= 2102 + _verticalApertureOffset = verticalApertureOffset; +#endif + _data.SetVerticalApertureOffset(verticalApertureOffset / GfCamera::APERTURE_UNIT); + } + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->focalLength); + if (!value.IsEmpty()) { + const auto focalLength = value.Get<float>(); +#if PXR_VERSION >= 2102 + _focalLength = focalLength; +#endif + _data.SetFocalLength(focalLength / GfCamera::FOCAL_LENGTH_UNIT); + } + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->clippingRange); + if (!value.IsEmpty()) { + const auto clippingRange = value.Get<GfRange1f>(); +#if PXR_VERSION >= 2102 + _clippingRange = clippingRange; +#endif + _data.SetClippingRange(clippingRange); + } + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->fStop); + if (!value.IsEmpty()) { + const auto fStop = value.Get<float>(); +#if PXR_VERSION >= 2102 + _fStop = fStop; +#endif + _data.SetFStop(fStop); + } + + value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->focusDistance); + if (!value.IsEmpty()) { + const auto focusDistance = value.Get<float>(); +#if PXR_VERSION >= 2102 + _focusDistance = focusDistance; +#endif + _data.SetFocusDistance(focusDistance); + } + } + + *dirtyBits = DirtyBits::Clean; +} + +void HdCyclesCamera::Finalize(HdRenderParam *renderParam) +{ + HdCamera::Finalize(renderParam); +} + +void HdCyclesCamera::ApplyCameraSettings(Camera *cam) const +{ + ApplyCameraSettings(_data, _windowPolicy, cam); + + array<Transform> motion(_transformSamples.count); + for (size_t i = 0; i < _transformSamples.count; ++i) + motion[i] = convert_transform(_transformSamples.values[i]) * + transform_scale(1.0f, 1.0f, -1.0f); + cam->set_motion(motion); +} + +void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow, + CameraUtilConformWindowPolicy windowPolicy, + Camera *cam) +{ + const float width = cam->get_full_width(); + const float height = cam->get_full_height(); + + auto data = dataUnconformedWindow; + CameraUtilConformWindow(&data, windowPolicy, width / height); + + static_assert(GfCamera::Perspective == CAMERA_PERSPECTIVE && + GfCamera::Orthographic == CAMERA_ORTHOGRAPHIC); + cam->set_camera_type(static_cast<CameraType>(data.GetProjection())); + + auto viewplane = data.GetFrustum().GetWindow(); + auto focalLength = 1.0f; + if (data.GetProjection() == GfCamera::Perspective) { + viewplane *= 2.0 / viewplane.GetSize()[1]; // Normalize viewplane + focalLength = data.GetFocalLength() * 1e-3f; + + cam->set_fov(GfDegreesToRadians(data.GetFieldOfView(GfCamera::FOVVertical))); + } + + cam->set_sensorwidth(data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT); + cam->set_sensorheight(data.GetVerticalAperture() * GfCamera::APERTURE_UNIT); + + cam->set_nearclip(data.GetClippingRange().GetMin()); + cam->set_farclip(data.GetClippingRange().GetMax()); + + cam->set_viewplane_left(viewplane.GetMin()[0]); + cam->set_viewplane_right(viewplane.GetMax()[0]); + cam->set_viewplane_bottom(viewplane.GetMin()[1]); + cam->set_viewplane_top(viewplane.GetMax()[1]); + + if (data.GetFStop() != 0.0f) { + cam->set_focaldistance(data.GetFocusDistance()); + cam->set_aperturesize(focalLength / (2.0f * data.GetFStop())); + } + + cam->set_matrix(convert_transform(data.GetTransform()) * transform_scale(1.0f, 1.0f, -1.0f)); +} + +void HdCyclesCamera::ApplyCameraSettings(const GfMatrix4d &worldToViewMatrix, + const GfMatrix4d &projectionMatrix, + const std::vector<GfVec4d> &clipPlanes, + Camera *cam) +{ +#if PXR_VERSION >= 2102 + GfCamera data; + data.SetFromViewAndProjectionMatrix(worldToViewMatrix, projectionMatrix); + + ApplyCameraSettings(data, CameraUtilFit, cam); +#else + TF_CODING_ERROR("Not implemented"); +#endif +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/camera.h b/intern/cycles/hydra/camera.h new file mode 100644 index 00000000000..8b7fed5a6bb --- /dev/null +++ b/intern/cycles/hydra/camera.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/base/gf/camera.h> +#include <pxr/imaging/hd/camera.h> +#include <pxr/imaging/hd/timeSampleArray.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesCamera final : public PXR_NS::HdCamera { + public: + HdCyclesCamera(const PXR_NS::SdfPath &sprimId); + ~HdCyclesCamera() override; + + void ApplyCameraSettings(CCL_NS::Camera *targetCamera) const; + + static void ApplyCameraSettings(const PXR_NS::GfCamera &cameraData, + PXR_NS::CameraUtilConformWindowPolicy windowPolicy, + CCL_NS::Camera *targetCamera); + static void ApplyCameraSettings(const PXR_NS::GfMatrix4d &worldToViewMatrix, + const PXR_NS::GfMatrix4d &projectionMatrix, + const std::vector<PXR_NS::GfVec4d> &clipPlanes, + CCL_NS::Camera *targetCamera); + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + void Sync(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdRenderParam *renderParam, + PXR_NS::HdDirtyBits *dirtyBits) override; + + void Finalize(PXR_NS::HdRenderParam *renderParam) override; + + private: + PXR_NS::GfCamera _data; + PXR_NS::HdTimeSampleArray<PXR_NS::GfMatrix4d, 2> _transformSamples; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/config.h b/intern/cycles/hydra/config.h new file mode 100644 index 00000000000..034be302d9f --- /dev/null +++ b/intern/cycles/hydra/config.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include <pxr/pxr.h> + +#define CCL_NS ccl +#define CCL_NAMESPACE_USING_DIRECTIVE using namespace CCL_NS; + +#define HD_CYCLES_NS HdCycles +#define HDCYCLES_NAMESPACE_OPEN_SCOPE \ + namespace HD_CYCLES_NS { \ + CCL_NAMESPACE_USING_DIRECTIVE; \ + PXR_NAMESPACE_USING_DIRECTIVE; +#define HDCYCLES_NAMESPACE_CLOSE_SCOPE } + +namespace HD_CYCLES_NS { +class HdCyclesCamera; +class HdCyclesDelegate; +class HdCyclesSession; +class HdCyclesRenderBuffer; +} // namespace HD_CYCLES_NS + +namespace CCL_NS { +class AttributeSet; +class BufferParams; +class Camera; +class Geometry; +class Hair; +class Light; +class Mesh; +class Object; +class ParticleSystem; +class Pass; +class PointCloud; +class Scene; +class Session; +class SessionParams; +class Shader; +class ShaderGraph; +class Volume; +} // namespace CCL_NS diff --git a/intern/cycles/hydra/curves.cpp b/intern/cycles/hydra/curves.cpp new file mode 100644 index 00000000000..2007a5d09ca --- /dev/null +++ b/intern/cycles/hydra/curves.cpp @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/curves.h" +#include "hydra/geometry.inl" +#include "scene/hair.h" + +#include <pxr/imaging/hd/extComputationUtils.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +HdCyclesCurves::HdCyclesCurves(const SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const SdfPath &instancerId +#endif + ) + : HdCyclesGeometry(rprimId +#if PXR_VERSION < 2102 + , + instancerId +#endif + ) +{ +} + +HdCyclesCurves::~HdCyclesCurves() +{ +} + +HdDirtyBits HdCyclesCurves::GetInitialDirtyBitsMask() const +{ + HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask(); + bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths | + HdChangeTracker::DirtyPrimvar | HdChangeTracker::DirtyTopology; + return bits; +} + +HdDirtyBits HdCyclesCurves::_PropagateDirtyBits(HdDirtyBits bits) const +{ + if (bits & (HdChangeTracker::DirtyTopology)) { + // Changing topology clears the geometry, so need to populate everything again + bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths | + HdChangeTracker::DirtyPrimvar; + } + + return bits; +} + +void HdCyclesCurves::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild) +{ + if (HdChangeTracker::IsTopologyDirty(dirtyBits, GetId())) { + PopulateTopology(sceneDelegate); + } + + if (dirtyBits & HdChangeTracker::DirtyPoints) { + PopulatePoints(sceneDelegate); + } + + if (dirtyBits & HdChangeTracker::DirtyWidths) { + PopulateWidths(sceneDelegate); + } + + if (dirtyBits & HdChangeTracker::DirtyPrimvar) { + PopulatePrimvars(sceneDelegate); + } + + rebuild = (_geom->curve_keys_is_modified()) || (_geom->curve_radius_is_modified()); +} + +void HdCyclesCurves::PopulatePoints(HdSceneDelegate *sceneDelegate) +{ + VtValue value; + + for (const HdExtComputationPrimvarDescriptor &desc : + sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), HdInterpolationVertex)) { + if (desc.name == HdTokens->points) { + auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate); + const auto valueStoreIt = valueStore.find(desc.name); + if (valueStoreIt != valueStore.end()) { + value = std::move(valueStoreIt->second); + } + break; + } + } + + if (value.IsEmpty()) { + value = GetPrimvar(sceneDelegate, HdTokens->points); + } + + if (!value.IsHolding<VtVec3fArray>()) { + TF_WARN("Invalid points data for %s", GetId().GetText()); + return; + } + + const auto &points = value.UncheckedGet<VtVec3fArray>(); + + array<float3> pointsDataCycles; + pointsDataCycles.reserve(points.size()); + + for (const GfVec3f &point : points) { + pointsDataCycles.push_back_reserved(make_float3(point[0], point[1], point[2])); + } + + _geom->set_curve_keys(pointsDataCycles); +} + +void HdCyclesCurves::PopulateWidths(HdSceneDelegate *sceneDelegate) +{ + VtValue value = GetPrimvar(sceneDelegate, HdTokens->widths); + const HdInterpolation interpolation = GetPrimvarInterpolation(sceneDelegate, HdTokens->widths); + + if (!value.IsHolding<VtFloatArray>()) { + TF_WARN("Invalid widths data for %s", GetId().GetText()); + return; + } + + const auto &widths = value.UncheckedGet<VtFloatArray>(); + + array<float> radiusDataCycles; + radiusDataCycles.reserve(widths.size()); + + if (interpolation == HdInterpolationConstant) { + TF_VERIFY(widths.size() == 1); + + const float constantRadius = widths[0] * 0.5f; + + for (size_t i = 0; i < _geom->num_keys(); ++i) { + radiusDataCycles.push_back_reserved(constantRadius); + } + } + else if (interpolation == HdInterpolationVertex) { + TF_VERIFY(widths.size() == _geom->num_keys()); + + for (size_t i = 0; i < _geom->num_keys(); ++i) { + radiusDataCycles.push_back_reserved(widths[i] * 0.5f); + } + } + + _geom->set_curve_radius(radiusDataCycles); +} + +void HdCyclesCurves::PopulatePrimvars(HdSceneDelegate *sceneDelegate) +{ + Scene *const scene = (Scene *)_geom->get_owner(); + + const std::pair<HdInterpolation, AttributeElement> interpolations[] = { + std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_CURVE_KEY), + std::make_pair(HdInterpolationVarying, ATTR_ELEMENT_CURVE_KEY), + std::make_pair(HdInterpolationUniform, ATTR_ELEMENT_CURVE), + std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT), + }; + + for (const auto &interpolation : interpolations) { + for (const HdPrimvarDescriptor &desc : + GetPrimvarDescriptors(sceneDelegate, interpolation.first)) { + // Skip special primvars that are handled separately + if (desc.name == HdTokens->points || desc.name == HdTokens->widths) { + continue; + } + + VtValue value = GetPrimvar(sceneDelegate, desc.name); + if (value.IsEmpty()) { + continue; + } + + const ustring name(desc.name.GetString()); + + AttributeStandard std = ATTR_STD_NONE; + if (desc.role == HdPrimvarRoleTokens->textureCoordinate) { + std = ATTR_STD_UV; + } + else if (desc.name == HdTokens->displayColor && + interpolation.first == HdInterpolationConstant) { + if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) { + const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0]; + _instances[0]->set_color(make_float3(color[0], color[1], color[2])); + } + } + + // Skip attributes that are not needed + if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) || + _geom->need_attribute(scene, name)) { + ApplyPrimvars(_geom->attributes, name, value, interpolation.second, std); + } + } + } +} + +void HdCyclesCurves::PopulateTopology(HdSceneDelegate *sceneDelegate) +{ + // Clear geometry before populating it again with updated topology + _geom->clear(true); + + HdBasisCurvesTopology topology = GetBasisCurvesTopology(sceneDelegate); + + _geom->reserve_curves(topology.GetNumCurves(), topology.CalculateNeededNumberOfControlPoints()); + + const VtIntArray vertCounts = topology.GetCurveVertexCounts(); + + for (int curve = 0, key = 0; curve < topology.GetNumCurves(); ++curve) { + // Always reference shader at index zero, which is the primitive material + _geom->add_curve(key, 0); + + key += vertCounts[curve]; + } +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/curves.h b/intern/cycles/hydra/curves.h new file mode 100644 index 00000000000..1eb4a51c0db --- /dev/null +++ b/intern/cycles/hydra/curves.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "hydra/geometry.h" + +#include <pxr/imaging/hd/basisCurves.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesCurves final : public HdCyclesGeometry<PXR_NS::HdBasisCurves, CCL_NS::Hair> { + public: + HdCyclesCurves( + const PXR_NS::SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const PXR_NS::SdfPath &instancerId = {} +#endif + ); + ~HdCyclesCurves() override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + private: + PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override; + + void Populate(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdDirtyBits dirtyBits, + bool &rebuild) override; + + void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate); + void PopulateWidths(PXR_NS::HdSceneDelegate *sceneDelegate); + + void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate); + + void PopulateTopology(PXR_NS::HdSceneDelegate *sceneDelegate); +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/display_driver.cpp b/intern/cycles/hydra/display_driver.cpp new file mode 100644 index 00000000000..6f6ca35cd31 --- /dev/null +++ b/intern/cycles/hydra/display_driver.cpp @@ -0,0 +1,240 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#ifdef _WIN32 +// Include first to avoid "NOGDI" definition set in Cycles headers +# include <Windows.h> +#endif + +#include "hydra/display_driver.h" +#include "hydra/render_buffer.h" +#include "hydra/session.h" + +#include <GL/glew.h> +#include <pxr/imaging/hgiGL/texture.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +HdCyclesDisplayDriver::HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi) + : _renderParam(renderParam), _hgi(hgi) +{ +#ifdef _WIN32 + hdc_ = GetDC(CreateWindowA("STATIC", + "HdCycles", + WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, + 0, + 64, + 64, + NULL, + NULL, + GetModuleHandle(NULL), + NULL)); + + int pixelFormat = GetPixelFormat(wglGetCurrentDC()); + PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd)}; + DescribePixelFormat((HDC)hdc_, pixelFormat, sizeof(pfd), &pfd); + SetPixelFormat((HDC)hdc_, pixelFormat, &pfd); + + TF_VERIFY(gl_context_ = wglCreateContext((HDC)hdc_)); + TF_VERIFY(wglShareLists(wglGetCurrentContext(), (HGLRC)gl_context_)); +#endif + + glewInit(); + + glGenBuffers(1, &gl_pbo_id_); +} + +HdCyclesDisplayDriver::~HdCyclesDisplayDriver() +{ + if (texture_) { + _hgi->DestroyTexture(&texture_); + } + + glDeleteBuffers(1, &gl_pbo_id_); + +#ifdef _WIN32 + TF_VERIFY(wglDeleteContext((HGLRC)gl_context_)); + DestroyWindow(WindowFromDC((HDC)hdc_)); +#endif +} + +void HdCyclesDisplayDriver::next_tile_begin() +{ +} + +bool HdCyclesDisplayDriver::update_begin(const Params ¶ms, + int texture_width, + int texture_height) +{ +#ifdef _WIN32 + if (!hdc_ || !gl_context_) { + return false; + } +#endif + + graphics_interop_activate(); + + if (gl_render_sync_) { + glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); + } + + if (pbo_size_.x != params.full_size.x || pbo_size_.y != params.full_size.y) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_); + glBufferData(GL_PIXEL_UNPACK_BUFFER, + sizeof(half4) * params.full_size.x * params.full_size.y, + 0, + GL_DYNAMIC_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + pbo_size_ = params.full_size; + } + + need_update_ = true; + + return true; +} + +void HdCyclesDisplayDriver::update_end() +{ + gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + glFlush(); + + graphics_interop_deactivate(); +} + +void HdCyclesDisplayDriver::flush() +{ + graphics_interop_activate(); + + if (gl_upload_sync_) { + glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); + } + + if (gl_render_sync_) { + glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); + } + + graphics_interop_deactivate(); +} + +half4 *HdCyclesDisplayDriver::map_texture_buffer() +{ + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_); + + const auto mapped_rgba_pixels = static_cast<half4 *>( + glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); + + if (need_clear_ && mapped_rgba_pixels) { + memset(mapped_rgba_pixels, 0, sizeof(half4) * pbo_size_.x * pbo_size_.y); + need_clear_ = false; + } + + return mapped_rgba_pixels; +} + +void HdCyclesDisplayDriver::unmap_texture_buffer() +{ + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +DisplayDriver::GraphicsInterop HdCyclesDisplayDriver::graphics_interop_get() +{ + GraphicsInterop interop_dst; + interop_dst.buffer_width = pbo_size_.x; + interop_dst.buffer_height = pbo_size_.y; + interop_dst.opengl_pbo_id = gl_pbo_id_; + + interop_dst.need_clear = need_clear_; + need_clear_ = false; + + return interop_dst; +} + +void HdCyclesDisplayDriver::graphics_interop_activate() +{ + mutex_.lock(); + +#ifdef _WIN32 + // Do not change context if this is called in the main thread + if (wglGetCurrentContext() == nullptr) { + TF_VERIFY(wglMakeCurrent((HDC)hdc_, (HGLRC)gl_context_)); + } +#endif +} + +void HdCyclesDisplayDriver::graphics_interop_deactivate() +{ +#ifdef _WIN32 + if (wglGetCurrentContext() == gl_context_) { + TF_VERIFY(wglMakeCurrent(nullptr, nullptr)); + } +#endif + + mutex_.unlock(); +} + +void HdCyclesDisplayDriver::clear() +{ + need_clear_ = true; +} + +void HdCyclesDisplayDriver::draw(const Params ¶ms) +{ + const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>( + _renderParam->GetDisplayAovBinding().renderBuffer); + if (!renderBuffer || // Ensure this render buffer matches the texture dimensions + (renderBuffer->GetWidth() != params.size.x || renderBuffer->GetHeight() != params.size.y)) { + return; + } + + // Cycles 'DisplayDriver' only supports 'half4' format + TF_VERIFY(renderBuffer->GetFormat() == HdFormatFloat16Vec4); + + const thread_scoped_lock lock(mutex_); + + const GfVec3i dimensions(params.size.x, params.size.y, 1); + if (!texture_ || texture_->GetDescriptor().dimensions != dimensions) { + if (texture_) { + _hgi->DestroyTexture(&texture_); + } + + HgiTextureDesc texDesc; + texDesc.usage = 0; + texDesc.format = HgiFormatFloat16Vec4; + texDesc.type = HgiTextureType2D; + texDesc.dimensions = dimensions; + texDesc.sampleCount = HgiSampleCount1; + + texture_ = _hgi->CreateTexture(texDesc); + + renderBuffer->SetResource(VtValue(texture_)); + } + + HgiGLTexture *const texture = dynamic_cast<HgiGLTexture *>(texture_.Get()); + if (!texture || !need_update_ || pbo_size_.x != params.size.x || pbo_size_.y != params.size.y) { + return; + } + + if (gl_upload_sync_) { + glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); + } + + glBindTexture(GL_TEXTURE_2D, texture->GetTextureId()); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pbo_size_.x, pbo_size_.y, GL_RGBA, GL_HALF_FLOAT, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + glFlush(); + + need_update_ = false; +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/display_driver.h b/intern/cycles/hydra/display_driver.h new file mode 100644 index 00000000000..668f7d76eed --- /dev/null +++ b/intern/cycles/hydra/display_driver.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "session/display_driver.h" +#include "util/thread.h" + +#include <pxr/imaging/hgi/hgi.h> +#include <pxr/imaging/hgi/texture.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesDisplayDriver final : public CCL_NS::DisplayDriver { + public: + HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi); + ~HdCyclesDisplayDriver(); + + private: + void next_tile_begin() override; + + bool update_begin(const Params ¶ms, int texture_width, int texture_height) override; + void update_end() override; + + void flush() override; + + CCL_NS::half4 *map_texture_buffer() override; + void unmap_texture_buffer() override; + + GraphicsInterop graphics_interop_get() override; + + void graphics_interop_activate() override; + void graphics_interop_deactivate() override; + + void clear() override; + + void draw(const Params ¶ms) override; + + HdCyclesSession *const _renderParam; + Hgi *const _hgi; + +#ifdef _WIN32 + void *hdc_ = nullptr; + void *gl_context_ = nullptr; +#endif + + CCL_NS::thread_mutex mutex_; + + PXR_NS::HgiTextureHandle texture_; + unsigned int gl_pbo_id_ = 0; + CCL_NS::int2 pbo_size_ = CCL_NS::make_int2(0, 0); + bool need_update_ = false; + std::atomic_bool need_clear_ = false; + + void *gl_render_sync_ = nullptr; + void *gl_upload_sync_ = nullptr; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/field.cpp b/intern/cycles/hydra/field.cpp new file mode 100644 index 00000000000..8b92ddb6f1f --- /dev/null +++ b/intern/cycles/hydra/field.cpp @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/field.h" +#include "hydra/session.h" +#include "scene/image_vdb.h" +#include "scene/scene.h" + +#include <pxr/imaging/hd/sceneDelegate.h> +#include <pxr/usd/sdf/assetPath.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +// clang-format off +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (fieldName) +); +// clang-format on + +#ifdef WITH_OPENVDB +class HdCyclesVolumeLoader : public VDBImageLoader { + public: + HdCyclesVolumeLoader(const std::string &filePath, const std::string &gridName) + : VDBImageLoader(gridName) + { + openvdb::io::File file(filePath); + file.setCopyMaxBytes(0); + if (file.open()) { + grid = file.readGrid(gridName); + } + } +}; +#endif + +HdCyclesField::HdCyclesField(const SdfPath &bprimId, const TfToken &typeId) : HdField(bprimId) +{ +} + +HdCyclesField::~HdCyclesField() +{ +} + +HdDirtyBits HdCyclesField::GetInitialDirtyBitsMask() const +{ + return DirtyBits::DirtyParams; +} + +void HdCyclesField::Sync(HdSceneDelegate *sceneDelegate, + HdRenderParam *renderParam, + HdDirtyBits *dirtyBits) +{ +#ifdef WITH_OPENVDB + VtValue value; + const SdfPath &id = GetId(); + + if (*dirtyBits & DirtyBits::DirtyParams) { + value = sceneDelegate->Get(id, HdFieldTokens->filePath); + if (value.IsHolding<SdfAssetPath>()) { + std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath(); + if (filename.empty()) { + filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath(); + } + +# if PXR_VERSION >= 2108 + value = sceneDelegate->Get(id, HdFieldTokens->fieldName); +# else + value = sceneDelegate->Get(id, _tokens->fieldName); +# endif + if (value.IsHolding<TfToken>()) { + ImageLoader *const loader = new HdCyclesVolumeLoader( + filename, value.UncheckedGet<TfToken>().GetString()); + + const SceneLock lock(renderParam); + + ImageParams params; + params.frame = 0.0f; + + _handle = lock.scene->image_manager->add_image(loader, params, false); + } + } + } +#endif + + *dirtyBits = DirtyBits::Clean; +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/field.h b/intern/cycles/hydra/field.h new file mode 100644 index 00000000000..14cd9468761 --- /dev/null +++ b/intern/cycles/hydra/field.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "scene/image.h" + +#include <pxr/imaging/hd/field.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesField final : public PXR_NS::HdField { + public: + HdCyclesField(const PXR_NS::SdfPath &bprimId, const PXR_NS::TfToken &typeId); + ~HdCyclesField() override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + void Sync(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdRenderParam *renderParam, + PXR_NS::HdDirtyBits *dirtyBits) override; + + CCL_NS::ImageHandle GetImageHandle() const + { + return _handle; + } + + private: + CCL_NS::ImageHandle _handle; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/geometry.h b/intern/cycles/hydra/geometry.h new file mode 100644 index 00000000000..1a516ed691d --- /dev/null +++ b/intern/cycles/hydra/geometry.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/imaging/hd/rprim.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +template<typename Base, typename CyclesBase> class HdCyclesGeometry : public Base { + public: + HdCyclesGeometry(const PXR_NS::SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const PXR_NS::SdfPath &instancerId +#endif + ); + + void Sync(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdRenderParam *renderParam, + PXR_NS::HdDirtyBits *dirtyBits, + const PXR_NS::TfToken &reprToken) override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + virtual void Finalize(PXR_NS::HdRenderParam *renderParam) override; + + protected: + void _InitRepr(const PXR_NS::TfToken &reprToken, PXR_NS::HdDirtyBits *dirtyBits) override; + + PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override; + + virtual void Populate(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdDirtyBits dirtyBits, + bool &rebuild) = 0; + + PXR_NS::HdInterpolation GetPrimvarInterpolation(PXR_NS::HdSceneDelegate *sceneDelegate, + const PXR_NS::TfToken &name) const; + + CyclesBase *_geom = nullptr; + std::vector<CCL_NS::Object *> _instances; + + private: + void Initialize(PXR_NS::HdRenderParam *renderParam); + + void InitializeInstance(int index); + + PXR_NS::GfMatrix4d _geomTransform; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/geometry.inl b/intern/cycles/hydra/geometry.inl new file mode 100644 index 00000000000..007fc6f2667 --- /dev/null +++ b/intern/cycles/hydra/geometry.inl @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/attribute.h" +#include "hydra/geometry.h" +#include "hydra/instancer.h" +#include "hydra/material.h" +#include "hydra/session.h" +#include "scene/geometry.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "util/hash.h" + +#include <pxr/imaging/hd/sceneDelegate.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +extern Transform convert_transform(const GfMatrix4d &matrix); + +template<typename Base, typename CyclesBase> +HdCyclesGeometry<Base, CyclesBase>::HdCyclesGeometry(const SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const SdfPath &instancerId +#endif + ) + : Base(rprimId +#if PXR_VERSION < 2102 + + , + instancerId +#endif + ), + _geomTransform(1.0) +{ +} + +template<typename Base, typename CyclesBase> +void HdCyclesGeometry<Base, CyclesBase>::_InitRepr(const TfToken &reprToken, + HdDirtyBits *dirtyBits) +{ + TF_UNUSED(reprToken); + TF_UNUSED(dirtyBits); +} + +template<typename Base, typename CyclesBase> +HdDirtyBits HdCyclesGeometry<Base, CyclesBase>::GetInitialDirtyBitsMask() const +{ + return HdChangeTracker::DirtyPrimID | HdChangeTracker::DirtyTransform | + HdChangeTracker::DirtyMaterialId | HdChangeTracker::DirtyVisibility | + HdChangeTracker::DirtyInstancer; +} + +template<typename Base, typename CyclesBase> +HdDirtyBits HdCyclesGeometry<Base, CyclesBase>::_PropagateDirtyBits(HdDirtyBits bits) const +{ + return bits; +} + +template<typename Base, typename CyclesBase> +void HdCyclesGeometry<Base, CyclesBase>::Sync(HdSceneDelegate *sceneDelegate, + HdRenderParam *renderParam, + HdDirtyBits *dirtyBits, + const TfToken &reprToken) +{ + TF_UNUSED(reprToken); + + if (*dirtyBits == HdChangeTracker::Clean) { + return; + } + + Initialize(renderParam); + +#if PXR_VERSION >= 2102 + Base::_UpdateInstancer(sceneDelegate, dirtyBits); + HdInstancer::_SyncInstancerAndParents(sceneDelegate->GetRenderIndex(), Base::GetInstancerId()); +#endif + Base::_UpdateVisibility(sceneDelegate, dirtyBits); + + const SceneLock lock(renderParam); + + if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { +#if HD_API_VERSION >= 37 && PXR_VERSION >= 2105 + Base::SetMaterialId(sceneDelegate->GetMaterialId(Base::GetId())); +#else + Base::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(), + sceneDelegate->GetMaterialId(Base::GetId())); +#endif + + const auto material = static_cast<const HdCyclesMaterial *>( + sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, + Base::GetMaterialId())); + + array<Node *> usedShaders(1); + if (material && material->GetCyclesShader()) { + usedShaders[0] = material->GetCyclesShader(); + } + else { + usedShaders[0] = lock.scene->default_surface; + } + + for (Node *shader : usedShaders) { + static_cast<Shader *>(shader)->tag_used(lock.scene); + } + + _geom->set_used_shaders(usedShaders); + } + + const SdfPath &id = Base::GetId(); + + if (HdChangeTracker::IsPrimIdDirty(*dirtyBits, id)) { + // This needs to be corrected in the AOV + _instances[0]->set_pass_id(Base::GetPrimId() + 1); + } + + if (HdChangeTracker::IsTransformDirty(*dirtyBits, id)) { + _geomTransform = sceneDelegate->GetTransform(id); + } + + if (HdChangeTracker::IsTransformDirty(*dirtyBits, id) || + HdChangeTracker::IsInstancerDirty(*dirtyBits, id)) { + const auto instancer = static_cast<HdCyclesInstancer *>( + sceneDelegate->GetRenderIndex().GetInstancer(Base::GetInstancerId())); + + // Make sure the first object attribute is the instanceId + assert(_instances[0]->attributes.size() >= 1 && + _instances[0]->attributes.front().name() == HdAovTokens->instanceId.GetString()); + + VtMatrix4dArray transforms; + if (instancer) { + transforms = instancer->ComputeInstanceTransforms(id); + _instances[0]->attributes.front() = ParamValue(HdAovTokens->instanceId.GetString(), +0.0f); + } + else { + // Default to a single instance with an identity transform + transforms.push_back(GfMatrix4d(1.0)); + _instances[0]->attributes.front() = ParamValue(HdAovTokens->instanceId.GetString(), -1.0f); + } + + const size_t oldSize = _instances.size(); + const size_t newSize = transforms.size(); + + // Resize instance list + for (size_t i = newSize; i < oldSize; ++i) { + lock.scene->delete_node(_instances[i]); + } + _instances.resize(newSize); + for (size_t i = oldSize; i < newSize; ++i) { + _instances[i] = lock.scene->create_node<Object>(); + InitializeInstance(static_cast<int>(i)); + } + + // Update transforms of all instances + for (size_t i = 0; i < transforms.size(); ++i) { + const Transform tfm = convert_transform(_geomTransform * transforms[i]); + _instances[i]->set_tfm(tfm); + } + } + + if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) { + for (Object *instance : _instances) { + instance->set_visibility(Base::IsVisible() ? ~0 : 0); + } + } + + // Must happen after material ID update, so that attribute decisions can be made + // based on it (e.g. check whether an attribute is actually needed) + bool rebuild = false; + Populate(sceneDelegate, *dirtyBits, rebuild); + + if (_geom->is_modified() || rebuild) { + _geom->tag_update(lock.scene, rebuild); + } + + for (Object *instance : _instances) { + instance->tag_update(lock.scene); + } + + *dirtyBits = HdChangeTracker::Clean; +} + +template<typename Base, typename CyclesBase> +void HdCyclesGeometry<Base, CyclesBase>::Finalize(HdRenderParam *renderParam) +{ + if (!_geom && _instances.empty()) { + return; + } + + const SceneLock lock(renderParam); + + lock.scene->delete_node(_geom); + _geom = nullptr; + + lock.scene->delete_nodes(set<Object *>(_instances.begin(), _instances.end())); + _instances.clear(); + _instances.shrink_to_fit(); +} + +template<typename Base, typename CyclesBase> +void HdCyclesGeometry<Base, CyclesBase>::Initialize(HdRenderParam *renderParam) +{ + if (_geom) { + return; + } + + const SceneLock lock(renderParam); + + // Create geometry + _geom = lock.scene->create_node<CyclesBase>(); + _geom->name = Base::GetId().GetString(); + + // Create default instance + _instances.push_back(lock.scene->create_node<Object>()); + InitializeInstance(0); +} + +template<typename Base, typename CyclesBase> +void HdCyclesGeometry<Base, CyclesBase>::InitializeInstance(int index) +{ + Object *instance = _instances[index]; + instance->set_geometry(_geom); + + instance->attributes.emplace_back(HdAovTokens->instanceId.GetString(), + _instances.size() == 1 ? -1.0f : static_cast<float>(index)); + + instance->set_color(make_float3(0.8f, 0.8f, 0.8f)); + instance->set_random_id(hash_uint2(hash_string(_geom->name.c_str()), index)); +} + +template<typename Base, typename CyclesBase> +HdInterpolation HdCyclesGeometry<Base, CyclesBase>::GetPrimvarInterpolation( + HdSceneDelegate *sceneDelegate, const TfToken &name) const +{ + for (int i = 0; i < HdInterpolationCount; ++i) { + for (const HdPrimvarDescriptor &desc : + Base::GetPrimvarDescriptors(sceneDelegate, static_cast<HdInterpolation>(i))) { + if (desc.name == name) { + return static_cast<HdInterpolation>(i); + } + } + } + + return HdInterpolationCount; +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/instancer.cpp b/intern/cycles/hydra/instancer.cpp new file mode 100644 index 00000000000..ade5141cd19 --- /dev/null +++ b/intern/cycles/hydra/instancer.cpp @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/instancer.h" + +#include <pxr/base/gf/quatd.h> +#include <pxr/imaging/hd/sceneDelegate.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +HdCyclesInstancer::HdCyclesInstancer(HdSceneDelegate *delegate, + const SdfPath &instancerId +#if PXR_VERSION <= 2011 + , + const SdfPath &parentId +#endif + ) + : HdInstancer(delegate, + instancerId +#if PXR_VERSION <= 2011 + , + parentId +#endif + ) +{ +} + +HdCyclesInstancer::~HdCyclesInstancer() +{ +} + +#if PXR_VERSION > 2011 +void HdCyclesInstancer::Sync(HdSceneDelegate *sceneDelegate, + HdRenderParam *renderParam, + HdDirtyBits *dirtyBits) +{ + _UpdateInstancer(sceneDelegate, dirtyBits); + + if (HdChangeTracker::IsAnyPrimvarDirty(*dirtyBits, GetId())) { + SyncPrimvars(); + } +} +#endif + +void HdCyclesInstancer::SyncPrimvars() +{ + HdSceneDelegate *const sceneDelegate = GetDelegate(); + const HdDirtyBits dirtyBits = + sceneDelegate->GetRenderIndex().GetChangeTracker().GetInstancerDirtyBits(GetId()); + + for (const HdPrimvarDescriptor &desc : + sceneDelegate->GetPrimvarDescriptors(GetId(), HdInterpolationInstance)) { + if (!HdChangeTracker::IsPrimvarDirty(dirtyBits, GetId(), desc.name)) { + continue; + } + + const VtValue value = sceneDelegate->Get(GetId(), desc.name); + if (value.IsEmpty()) { + continue; + } + + if (desc.name == HdInstancerTokens->translate) { + _translate = value.Get<VtVec3fArray>(); + } + else if (desc.name == HdInstancerTokens->rotate) { + _rotate = value.Get<VtVec4fArray>(); + } + else if (desc.name == HdInstancerTokens->scale) { + _scale = value.Get<VtVec3fArray>(); + } + else if (desc.name == HdInstancerTokens->instanceTransform) { + _instanceTransform = value.Get<VtMatrix4dArray>(); + } + } + + sceneDelegate->GetRenderIndex().GetChangeTracker().MarkInstancerClean(GetId()); +} + +VtMatrix4dArray HdCyclesInstancer::ComputeInstanceTransforms(const SdfPath &prototypeId) +{ +#if PXR_VERSION <= 2011 + SyncPrimvars(); +#endif + + const VtIntArray instanceIndices = GetDelegate()->GetInstanceIndices(GetId(), prototypeId); + const GfMatrix4d instanceTransform = GetDelegate()->GetInstancerTransform(GetId()); + + VtMatrix4dArray transforms; + transforms.reserve(instanceIndices.size()); + + for (int index : instanceIndices) { + GfMatrix4d transform = instanceTransform; + + if (index < _translate.size()) { + GfMatrix4d translateMat(1); + translateMat.SetTranslate(_translate[index]); + transform *= translateMat; + } + + if (index < _rotate.size()) { + GfMatrix4d rotateMat(1); + const GfVec4f &quat = _rotate[index]; + rotateMat.SetRotate(GfQuatd(quat[0], quat[1], quat[2], quat[3])); + transform *= rotateMat; + } + + if (index < _scale.size()) { + GfMatrix4d scaleMat(1); + scaleMat.SetScale(_scale[index]); + transform *= scaleMat; + } + + if (index < _instanceTransform.size()) { + transform *= _instanceTransform[index]; + } + + transforms.push_back(transform); + } + + VtMatrix4dArray resultTransforms; + + if (const auto instancer = static_cast<HdCyclesInstancer *>( + GetDelegate()->GetRenderIndex().GetInstancer(GetParentId()))) { + for (const GfMatrix4d &parentTransform : instancer->ComputeInstanceTransforms(GetId())) { + for (const GfMatrix4d &localTransform : transforms) { + resultTransforms.push_back(parentTransform * localTransform); + } + } + } + else { + resultTransforms = std::move(transforms); + } + + return resultTransforms; +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/instancer.h b/intern/cycles/hydra/instancer.h new file mode 100644 index 00000000000..ade1dfba8ee --- /dev/null +++ b/intern/cycles/hydra/instancer.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/base/gf/matrix4d.h> +#include <pxr/base/gf/vec3f.h> +#include <pxr/base/gf/vec4f.h> +#include <pxr/base/vt/array.h> +#include <pxr/imaging/hd/instancer.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesInstancer final : public PXR_NS::HdInstancer { + public: + HdCyclesInstancer(PXR_NS::HdSceneDelegate *delegate, + const PXR_NS::SdfPath &instancerId +#if PXR_VERSION <= 2011 + , + const PXR_NS::SdfPath &parentId +#endif + ); + ~HdCyclesInstancer() override; + +#if PXR_VERSION > 2011 + void Sync(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdRenderParam *renderParam, + PXR_NS::HdDirtyBits *dirtyBits) override; +#endif + + PXR_NS::VtMatrix4dArray ComputeInstanceTransforms(const PXR_NS::SdfPath &prototypeId); + + private: + void SyncPrimvars(); + + PXR_NS::VtVec3fArray _translate; + PXR_NS::VtVec4fArray _rotate; + PXR_NS::VtVec3fArray _scale; + PXR_NS::VtMatrix4dArray _instanceTransform; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/light.cpp b/intern/cycles/hydra/light.cpp new file mode 100644 index 00000000000..b691da0d6a6 --- /dev/null +++ b/intern/cycles/hydra/light.cpp @@ -0,0 +1,399 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/light.h" +#include "hydra/session.h" +#include "scene/light.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "util/hash.h" + +#include <pxr/imaging/hd/sceneDelegate.h> +#include <pxr/usd/sdf/assetPath.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +extern Transform convert_transform(const GfMatrix4d &matrix); + +// clang-format off +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (visibleInPrimaryRay) +); +// clang-format on + +HdCyclesLight::HdCyclesLight(const SdfPath &sprimId, const TfToken &lightType) + : HdLight(sprimId), _lightType(lightType) +{ +} + +HdCyclesLight::~HdCyclesLight() +{ +} + +HdDirtyBits HdCyclesLight::GetInitialDirtyBitsMask() const +{ + return DirtyBits::DirtyTransform | DirtyBits::DirtyParams; +} + +void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate, + HdRenderParam *renderParam, + HdDirtyBits *dirtyBits) +{ + if (*dirtyBits == DirtyBits::Clean) { + return; + } + + Initialize(renderParam); + + const SceneLock lock(renderParam); + + VtValue value; + const SdfPath &id = GetId(); + + if (*dirtyBits & DirtyBits::DirtyTransform) { +#if PXR_VERSION >= 2011 + const Transform tfm = convert_transform(sceneDelegate->GetTransform(id)); +#else + const Transform tfm = convert_transform( + sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get<GfMatrix4d>()); +#endif + _light->set_tfm(tfm); + + _light->set_co(transform_get_column(&tfm, 3)); + _light->set_dir(-transform_get_column(&tfm, 2)); + + if (_lightType == HdPrimTypeTokens->diskLight || _lightType == HdPrimTypeTokens->rectLight) { + _light->set_axisu(transform_get_column(&tfm, 0)); + _light->set_axisv(transform_get_column(&tfm, 1)); + } + } + + if (*dirtyBits & DirtyBits::DirtyParams) { + float3 strength = make_float3(1.0f, 1.0f, 1.0f); + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->color); + if (!value.IsEmpty()) { + const auto color = value.Get<GfVec3f>(); + strength = make_float3(color[0], color[1], color[2]); + } + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->exposure); + if (!value.IsEmpty()) { + strength *= exp2(value.Get<float>()); + } + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->intensity); + if (!value.IsEmpty()) { + strength *= value.Get<float>(); + } + + // Cycles lights are normalized by default, so need to scale intensity if Hydra light is not + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->normalize); + const bool normalize = value.IsHolding<bool>() && value.UncheckedGet<bool>(); + + value = sceneDelegate->GetLightParamValue(id, _tokens->visibleInPrimaryRay); + if (!value.IsEmpty()) { + _light->set_use_camera(value.Get<bool>()); + } + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shadowEnable); + if (!value.IsEmpty()) { + _light->set_cast_shadow(value.Get<bool>()); + } + + if (_lightType == HdPrimTypeTokens->distantLight) { + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->angle); + if (!value.IsEmpty()) { + _light->set_angle(GfDegreesToRadians(value.Get<float>())); + } + } + else if (_lightType == HdPrimTypeTokens->diskLight) { + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius); + if (!value.IsEmpty()) { + const float size = value.Get<float>() * 2.0f; + _light->set_sizeu(size); + _light->set_sizev(size); + } + + if (!normalize) { + const float radius = _light->get_sizeu() * 0.5f; + strength *= M_PI * radius * radius; + } + } + else if (_lightType == HdPrimTypeTokens->rectLight) { + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->width); + if (!value.IsEmpty()) { + _light->set_sizeu(value.Get<float>()); + } + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->height); + if (!value.IsEmpty()) { + _light->set_sizev(value.Get<float>()); + } + + if (!normalize) { + strength *= _light->get_sizeu() * _light->get_sizeu(); + } + } + else if (_lightType == HdPrimTypeTokens->sphereLight) { + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius); + if (!value.IsEmpty()) { + _light->set_size(value.Get<float>()); + } + + bool shaping = false; + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingConeAngle); + if (!value.IsEmpty()) { + _light->set_spot_angle(GfDegreesToRadians(value.Get<float>()) * 2.0f); + shaping = true; + } + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingConeSoftness); + if (!value.IsEmpty()) { + _light->set_spot_smooth(value.Get<float>()); + shaping = true; + } + + _light->set_light_type(shaping ? LIGHT_SPOT : LIGHT_POINT); + + if (!normalize) { + const float radius = _light->get_size(); + strength *= M_PI * radius * radius * 4.0f; + } + } + + const bool visible = sceneDelegate->GetVisible(id); + // Disable invisible lights by zeroing the strength + // So 'LightManager::test_enabled_lights' updates the enabled flag correctly + if (!visible) { + strength = zero_float3(); + } + + _light->set_strength(strength); + _light->set_is_enabled(visible); + + PopulateShaderGraph(sceneDelegate); + } + // Need to update shader graph when transform changes in case transform was baked into it + else if (_light->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight || + _light->get_shader()->has_surface_spatial_varying)) { + PopulateShaderGraph(sceneDelegate); + } + + if (_light->is_modified()) { + _light->tag_update(lock.scene); + } + + *dirtyBits = DirtyBits::Clean; +} + +void HdCyclesLight::PopulateShaderGraph(HdSceneDelegate *sceneDelegate) +{ + auto graph = new ShaderGraph(); + ShaderNode *outputNode = nullptr; + + if (_lightType == HdPrimTypeTokens->domeLight) { + BackgroundNode *bgNode = graph->create_node<BackgroundNode>(); + // Bake strength into shader graph, since only the shader is used for background lights + bgNode->set_color(_light->get_strength()); + graph->add(bgNode); + + graph->connect(bgNode->output("Background"), graph->output()->input("Surface")); + + outputNode = bgNode; + } + else { + EmissionNode *emissionNode = graph->create_node<EmissionNode>(); + emissionNode->set_color(one_float3()); + emissionNode->set_strength(1.0f); + graph->add(emissionNode); + + graph->connect(emissionNode->output("Emission"), graph->output()->input("Surface")); + + outputNode = emissionNode; + } + + VtValue value; + const SdfPath &id = GetId(); + bool hasSpatialVarying = false; + bool hasColorTemperature = false; + + if (sceneDelegate != nullptr) { + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->enableColorTemperature); + const bool enableColorTemperature = value.IsHolding<bool>() && value.UncheckedGet<bool>(); + + if (enableColorTemperature) { + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->colorTemperature); + if (value.IsHolding<float>()) { + BlackbodyNode *blackbodyNode = graph->create_node<BlackbodyNode>(); + blackbodyNode->set_temperature(value.UncheckedGet<float>()); + graph->add(blackbodyNode); + + if (_lightType == HdPrimTypeTokens->domeLight) { + VectorMathNode *mathNode = graph->create_node<VectorMathNode>(); + mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY); + mathNode->set_vector2(_light->get_strength()); + graph->add(mathNode); + + graph->connect(blackbodyNode->output("Color"), mathNode->input("Vector1")); + graph->connect(mathNode->output("Vector"), outputNode->input("Color")); + } + else { + graph->connect(blackbodyNode->output("Color"), outputNode->input("Color")); + } + + hasColorTemperature = true; + } + } + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingIesFile); + if (value.IsHolding<SdfAssetPath>()) { + std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath(); + if (filename.empty()) { + filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath(); + } + + TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>(); + coordNode->set_ob_tfm(_light->get_tfm()); + coordNode->set_use_transform(true); + graph->add(coordNode); + + IESLightNode *iesNode = graph->create_node<IESLightNode>(); + iesNode->set_filename(ustring(filename)); + + graph->connect(coordNode->output("Normal"), iesNode->input("Vector")); + graph->connect(iesNode->output("Fac"), outputNode->input("Strength")); + + hasSpatialVarying = true; + } + + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->textureFile); + if (value.IsHolding<SdfAssetPath>()) { + std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath(); + if (filename.empty()) { + filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath(); + } + + ImageSlotTextureNode *textureNode = nullptr; + if (_lightType == HdPrimTypeTokens->domeLight) { + Transform tfm = _light->get_tfm(); + transform_set_column(&tfm, 3, zero_float3()); // Remove translation + + TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>(); + coordNode->set_ob_tfm(tfm); + coordNode->set_use_transform(true); + graph->add(coordNode); + + textureNode = graph->create_node<EnvironmentTextureNode>(); + static_cast<EnvironmentTextureNode *>(textureNode)->set_filename(ustring(filename)); + graph->add(textureNode); + + graph->connect(coordNode->output("Object"), textureNode->input("Vector")); + + hasSpatialVarying = true; + } + else { + GeometryNode *coordNode = graph->create_node<GeometryNode>(); + graph->add(coordNode); + + textureNode = graph->create_node<ImageTextureNode>(); + static_cast<ImageTextureNode *>(textureNode)->set_filename(ustring(filename)); + graph->add(textureNode); + + graph->connect(coordNode->output("Parametric"), textureNode->input("Vector")); + } + + if (hasColorTemperature) { + VectorMathNode *mathNode = graph->create_node<VectorMathNode>(); + mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY); + graph->add(mathNode); + + graph->connect(textureNode->output("Color"), mathNode->input("Vector1")); + ShaderInput *const outputNodeInput = outputNode->input("Color"); + graph->connect(outputNodeInput->link, mathNode->input("Vector2")); + graph->disconnect(outputNodeInput); + graph->connect(mathNode->output("Vector"), outputNodeInput); + } + else if (_lightType == HdPrimTypeTokens->domeLight) { + VectorMathNode *mathNode = graph->create_node<VectorMathNode>(); + mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY); + mathNode->set_vector2(_light->get_strength()); + graph->add(mathNode); + + graph->connect(textureNode->output("Color"), mathNode->input("Vector1")); + graph->connect(mathNode->output("Vector"), outputNode->input("Color")); + } + else { + graph->connect(textureNode->output("Color"), outputNode->input("Color")); + } + } + } + + Shader *const shader = _light->get_shader(); + shader->set_graph(graph); + shader->tag_update((Scene *)_light->get_owner()); + + shader->has_surface_spatial_varying = hasSpatialVarying; +} + +void HdCyclesLight::Finalize(HdRenderParam *renderParam) +{ + if (!_light) { + return; + } + + const SceneLock lock(renderParam); + + lock.scene->delete_node(_light); + _light = nullptr; +} + +void HdCyclesLight::Initialize(HdRenderParam *renderParam) +{ + if (_light) { + return; + } + + const SceneLock lock(renderParam); + + _light = lock.scene->create_node<Light>(); + _light->name = GetId().GetString(); + + _light->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0)); + + if (_lightType == HdPrimTypeTokens->domeLight) { + _light->set_light_type(LIGHT_BACKGROUND); + } + else if (_lightType == HdPrimTypeTokens->distantLight) { + _light->set_light_type(LIGHT_DISTANT); + } + else if (_lightType == HdPrimTypeTokens->diskLight) { + _light->set_light_type(LIGHT_AREA); + _light->set_round(true); + _light->set_size(1.0f); + } + else if (_lightType == HdPrimTypeTokens->rectLight) { + _light->set_light_type(LIGHT_AREA); + _light->set_round(false); + _light->set_size(1.0f); + } + else if (_lightType == HdPrimTypeTokens->sphereLight) { + _light->set_light_type(LIGHT_POINT); + _light->set_size(1.0f); + } + + _light->set_use_mis(true); + _light->set_use_camera(false); + + Shader *const shader = lock.scene->create_node<Shader>(); + _light->set_shader(shader); + + // Create default shader graph + PopulateShaderGraph(nullptr); +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/light.h b/intern/cycles/hydra/light.h new file mode 100644 index 00000000000..9230bc5730e --- /dev/null +++ b/intern/cycles/hydra/light.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/imaging/hd/light.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesLight final : public PXR_NS::HdLight { + public: + HdCyclesLight(const PXR_NS::SdfPath &sprimId, const PXR_NS::TfToken &lightType); + ~HdCyclesLight() override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + void Sync(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdRenderParam *renderParam, + PXR_NS::HdDirtyBits *dirtyBits) override; + + void Finalize(PXR_NS::HdRenderParam *renderParam) override; + + private: + void Initialize(PXR_NS::HdRenderParam *renderParam); + + void PopulateShaderGraph(PXR_NS::HdSceneDelegate *sceneDelegate); + + CCL_NS::Light *_light = nullptr; + PXR_NS::TfToken _lightType; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/material.cpp b/intern/cycles/hydra/material.cpp new file mode 100644 index 00000000000..a595102a605 --- /dev/null +++ b/intern/cycles/hydra/material.cpp @@ -0,0 +1,589 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/material.h" +#include "hydra/node_util.h" +#include "hydra/session.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" + +#include <pxr/imaging/hd/sceneDelegate.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +// clang-format off +TF_DEFINE_PRIVATE_TOKENS(CyclesMaterialTokens, + ((cyclesSurface, "cycles:surface")) + ((cyclesDisplacement, "cycles:displacement")) + ((cyclesVolume, "cycles:volume")) + (UsdPreviewSurface) + (UsdUVTexture) + (UsdPrimvarReader_float) + (UsdPrimvarReader_float2) + (UsdPrimvarReader_float3) + (UsdPrimvarReader_float4) + (UsdPrimvarReader_int) + (UsdTransform2d) + (a) + (rgb) + (r) + (g) + (b) + (result) + (st) + (wrapS) + (wrapT) + (periodic) +); +// clang-format on + +namespace { + +// Simple class to handle remapping of USDPreviewSurface nodes and parameters to Cycles equivalents +class UsdToCyclesMapping { + using ParamMap = std::unordered_map<TfToken, ustring, TfToken::HashFunctor>; + + public: + UsdToCyclesMapping(const char *nodeType, ParamMap paramMap) + : _nodeType(nodeType), _paramMap(std::move(paramMap)) + { + } + + ustring nodeType() const + { + return _nodeType; + } + + virtual std::string parameterName(const TfToken &name, + const ShaderInput *inputConnection, + VtValue *value = nullptr) const + { + // UsdNode.name -> Node.input + // These all follow a simple pattern that we can just remap + // based on the name or 'Node.input' type + if (inputConnection) { + if (name == CyclesMaterialTokens->a) { + return "alpha"; + } + if (name == CyclesMaterialTokens->rgb) { + return "color"; + } + // TODO: Is there a better mapping than 'color'? + if (name == CyclesMaterialTokens->r || name == CyclesMaterialTokens->g || + name == CyclesMaterialTokens->b) { + return "color"; + } + + if (name == CyclesMaterialTokens->result) { + switch (inputConnection->socket_type.type) { + case SocketType::BOOLEAN: + case SocketType::FLOAT: + case SocketType::INT: + case SocketType::UINT: + return "alpha"; + case SocketType::COLOR: + case SocketType::VECTOR: + case SocketType::POINT: + case SocketType::NORMAL: + default: + return "color"; + } + } + } + + // Simple mapping case + const auto it = _paramMap.find(name); + return it != _paramMap.end() ? it->second.string() : name.GetString(); + } + + private: + const ustring _nodeType; + ParamMap _paramMap; +}; + +class UsdToCyclesTexture : public UsdToCyclesMapping { + public: + using UsdToCyclesMapping::UsdToCyclesMapping; + + std::string parameterName(const TfToken &name, + const ShaderInput *inputConnection, + VtValue *value) const override + { + if (value) { + // Remap UsdUVTexture.wrapS and UsdUVTexture.wrapT to cycles_image_texture.extension + if (name == CyclesMaterialTokens->wrapS || name == CyclesMaterialTokens->wrapT) { + std::string valueString = VtValue::Cast<std::string>(*value).Get<std::string>(); + + // A value of 'repeat' in USD is equivalent to 'periodic' in Cycles + if (valueString == "repeat") { + *value = VtValue(CyclesMaterialTokens->periodic); + } + + return "extension"; + } + } + + return UsdToCyclesMapping::parameterName(name, inputConnection, value); + } +}; + +class UsdToCycles { + const UsdToCyclesMapping UsdPreviewSurface = { + "principled_bsdf", + { + {TfToken("diffuseColor"), ustring("base_color")}, + {TfToken("emissiveColor"), ustring("emission")}, + {TfToken("specularColor"), ustring("specular")}, + {TfToken("clearcoatRoughness"), ustring("clearcoat_roughness")}, + {TfToken("opacity"), ustring("alpha")}, + // opacityThreshold + // occlusion + // displacement + }}; + const UsdToCyclesTexture UsdUVTexture = { + "image_texture", + { + {CyclesMaterialTokens->st, ustring("vector")}, + {CyclesMaterialTokens->wrapS, ustring("extension")}, + {CyclesMaterialTokens->wrapT, ustring("extension")}, + {TfToken("file"), ustring("filename")}, + {TfToken("sourceColorSpace"), ustring("colorspace")}, + }}; + const UsdToCyclesMapping UsdPrimvarReader = {"attribute", + {{TfToken("varname"), ustring("attribute")}}}; + + public: + const UsdToCyclesMapping *findUsd(const TfToken &usdNodeType) + { + if (usdNodeType == CyclesMaterialTokens->UsdPreviewSurface) { + return &UsdPreviewSurface; + } + if (usdNodeType == CyclesMaterialTokens->UsdUVTexture) { + return &UsdUVTexture; + } + if (usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float || + usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float2 || + usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float3 || + usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float4 || + usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_int) { + return &UsdPrimvarReader; + } + + return nullptr; + } + const UsdToCyclesMapping *findCycles(const ustring &cyclesNodeType) + { + return nullptr; + } +}; +TfStaticData<UsdToCycles> sUsdToCyles; + +} // namespace + +struct HdCyclesMaterial::NodeDesc { + ShaderNode *node; + const UsdToCyclesMapping *mapping; +}; + +HdCyclesMaterial::HdCyclesMaterial(const SdfPath &sprimId) : HdMaterial(sprimId) +{ +} + +HdCyclesMaterial::~HdCyclesMaterial() +{ +} + +HdDirtyBits HdCyclesMaterial::GetInitialDirtyBitsMask() const +{ + return DirtyBits::DirtyResource | DirtyBits::DirtyParams; +} + +void HdCyclesMaterial::Sync(HdSceneDelegate *sceneDelegate, + HdRenderParam *renderParam, + HdDirtyBits *dirtyBits) +{ + if (*dirtyBits == DirtyBits::Clean) { + return; + } + + Initialize(renderParam); + + const SceneLock lock(renderParam); + + const bool dirtyParams = (*dirtyBits & DirtyBits::DirtyParams); + const bool dirtyResource = (*dirtyBits & DirtyBits::DirtyResource); + + VtValue value; + const SdfPath &id = GetId(); + + if (dirtyResource || dirtyParams) { + value = sceneDelegate->GetMaterialResource(id); + +#if 1 + const HdMaterialNetwork2 *network = nullptr; + std::unique_ptr<HdMaterialNetwork2> networkConverted; + if (value.IsHolding<HdMaterialNetwork2>()) { + network = &value.UncheckedGet<HdMaterialNetwork2>(); + } + else if (value.IsHolding<HdMaterialNetworkMap>()) { + const auto &networkOld = value.UncheckedGet<HdMaterialNetworkMap>(); + // In the case of only parameter updates, there is no need to waste time converting to a + // HdMaterialNetwork2, as supporting HdMaterialNetworkMap for parameters only is trivial. + if (!_nodes.empty() && !dirtyResource) { + for (const auto &networkEntry : networkOld.map) { + UpdateParameters(networkEntry.second); + } + _shader->tag_modified(); + } + else { + networkConverted = std::make_unique<HdMaterialNetwork2>(); + HdMaterialNetwork2ConvertFromHdMaterialNetworkMap(networkOld, networkConverted.get()); + network = networkConverted.get(); + } + } + else { + TF_RUNTIME_ERROR("Could not get a HdMaterialNetwork2."); + } + + if (network) { + if (!_nodes.empty() && !dirtyResource) { + UpdateParameters(*network); + _shader->tag_modified(); + } + else { + PopulateShaderGraph(*network); + } + } +#endif + } + + if (_shader->is_modified()) { + _shader->tag_update(lock.scene); + } + + *dirtyBits = DirtyBits::Clean; +} + +void HdCyclesMaterial::UpdateParameters(NodeDesc &nodeDesc, + const std::map<TfToken, VtValue> ¶meters, + const SdfPath &nodePath) +{ + for (const std::pair<TfToken, VtValue> ¶m : parameters) { + VtValue value = param.second; + + // See if the parameter name is in USDPreviewSurface terms, and needs to be converted + const UsdToCyclesMapping *inputMapping = nodeDesc.mapping; + const std::string inputName = inputMapping ? + inputMapping->parameterName(param.first, nullptr, &value) : + param.first.GetString(); + + // Find the input to write the parameter value to + const SocketType *input = nullptr; + for (const SocketType &socket : nodeDesc.node->type->inputs) { + if (string_iequals(socket.name.string(), inputName) || socket.ui_name == inputName) { + input = &socket; + break; + } + } + + if (!input) { + TF_WARN("Could not find parameter '%s' on node '%s' ('%s')", + param.first.GetText(), + nodePath.GetText(), + nodeDesc.node->name.c_str()); + continue; + } + + SetNodeValue(nodeDesc.node, *input, value); + } +} + +void HdCyclesMaterial::UpdateParameters(const HdMaterialNetwork &network) +{ + for (const HdMaterialNode &nodeEntry : network.nodes) { + const SdfPath &nodePath = nodeEntry.path; + + const auto nodeIt = _nodes.find(nodePath); + if (nodeIt == _nodes.end()) { + TF_RUNTIME_ERROR("Could not update parameters on missing node '%s'", nodePath.GetText()); + continue; + } + + UpdateParameters(nodeIt->second, nodeEntry.parameters, nodePath); + } +} + +void HdCyclesMaterial::UpdateParameters(const HdMaterialNetwork2 &network) +{ + for (const std::pair<SdfPath, HdMaterialNode2> &nodeEntry : network.nodes) { + const SdfPath &nodePath = nodeEntry.first; + + const auto nodeIt = _nodes.find(nodePath); + if (nodeIt == _nodes.end()) { + TF_RUNTIME_ERROR("Could not update parameters on missing node '%s'", nodePath.GetText()); + continue; + } + + UpdateParameters(nodeIt->second, nodeEntry.second.parameters, nodePath); + } +} + +void HdCyclesMaterial::UpdateConnections(NodeDesc &nodeDesc, + const HdMaterialNode2 &matNode, + const SdfPath &nodePath, + ShaderGraph *shaderGraph) +{ + for (const std::pair<TfToken, std::vector<HdMaterialConnection2>> &connection : + matNode.inputConnections) { + const TfToken &dstSocketName = connection.first; + + const UsdToCyclesMapping *inputMapping = nodeDesc.mapping; + const std::string inputName = inputMapping ? + inputMapping->parameterName(dstSocketName, nullptr) : + dstSocketName.GetString(); + + // Find the input to connect to on the passed in node + ShaderInput *input = nullptr; + for (ShaderInput *in : nodeDesc.node->inputs) { + if (string_iequals(in->socket_type.name.string(), inputName)) { + input = in; + break; + } + } + + if (!input) { + TF_WARN("Ignoring connection on '%s.%s', input '%s' was not found", + nodePath.GetText(), + dstSocketName.GetText(), + dstSocketName.GetText()); + continue; + } + + // Now find the output to connect from + const auto &connectedNodes = connection.second; + if (connectedNodes.empty()) { + continue; + } + + // TODO: Hydra allows multiple connections of the same input + // Unsure how to handle this in Cycles, so just use the first + if (connectedNodes.size() > 1) { + TF_WARN( + "Ignoring multiple connections to '%s.%s'", nodePath.GetText(), dstSocketName.GetText()); + } + + const SdfPath &upstreamNodePath = connectedNodes.front().upstreamNode; + const TfToken &upstreamOutputName = connectedNodes.front().upstreamOutputName; + + const auto srcNodeIt = _nodes.find(upstreamNodePath); + if (srcNodeIt == _nodes.end()) { + TF_WARN("Ignoring connection from '%s.%s' to '%s.%s', node '%s' was not found", + upstreamNodePath.GetText(), + upstreamOutputName.GetText(), + nodePath.GetText(), + dstSocketName.GetText(), + upstreamNodePath.GetText()); + continue; + } + + const UsdToCyclesMapping *outputMapping = srcNodeIt->second.mapping; + const std::string outputName = outputMapping ? + outputMapping->parameterName(upstreamOutputName, input) : + upstreamOutputName.GetString(); + + ShaderOutput *output = nullptr; + for (ShaderOutput *out : srcNodeIt->second.node->outputs) { + if (string_iequals(out->socket_type.name.string(), outputName)) { + output = out; + break; + } + } + + if (!output) { + TF_WARN("Ignoring connection from '%s.%s' to '%s.%s', output '%s' was not found", + upstreamNodePath.GetText(), + upstreamOutputName.GetText(), + nodePath.GetText(), + dstSocketName.GetText(), + upstreamOutputName.GetText()); + continue; + } + + shaderGraph->connect(output, input); + } +} + +void HdCyclesMaterial::PopulateShaderGraph(const HdMaterialNetwork2 &networkMap) +{ + _nodes.clear(); + + auto graph = new ShaderGraph(); + + // Iterate all the nodes first and build a complete but unconnected graph with parameters set + for (const std::pair<SdfPath, HdMaterialNode2> &nodeEntry : networkMap.nodes) { + NodeDesc nodeDesc = {}; + const SdfPath &nodePath = nodeEntry.first; + + const auto nodeIt = _nodes.find(nodePath); + // Create new node only if it does not exist yet + if (nodeIt != _nodes.end()) { + nodeDesc = nodeIt->second; + } + else { + // E.g. cycles_principled_bsdf or UsdPreviewSurface + const std::string &nodeTypeId = nodeEntry.second.nodeTypeId.GetString(); + + ustring cyclesType(nodeTypeId); + // Interpret a node type ID prefixed with cycles_<type> or cycles:<type> as a node of <type> + if (nodeTypeId.rfind("cycles", 0) == 0) { + cyclesType = nodeTypeId.substr(7); + nodeDesc.mapping = sUsdToCyles->findCycles(cyclesType); + } + else { + // Check if any remapping is needed (e.g. for USDPreviewSurface to Cycles nodes) + nodeDesc.mapping = sUsdToCyles->findUsd(nodeEntry.second.nodeTypeId); + if (nodeDesc.mapping) { + cyclesType = nodeDesc.mapping->nodeType(); + } + } + + // If it's a native Cycles' node-type, just do the lookup now. + if (const NodeType *nodeType = NodeType::find(cyclesType)) { + nodeDesc.node = static_cast<ShaderNode *>(nodeType->create(nodeType)); + nodeDesc.node->set_owner(graph); + + graph->add(nodeDesc.node); + + _nodes.emplace(nodePath, nodeDesc); + } + else { + TF_RUNTIME_ERROR("Could not create node '%s'", nodePath.GetText()); + continue; + } + } + + UpdateParameters(nodeDesc, nodeEntry.second.parameters, nodePath); + } + + // Now that all nodes have been constructed, iterate the network again and build up any + // connections between nodes + for (const std::pair<SdfPath, HdMaterialNode2> &nodeEntry : networkMap.nodes) { + const SdfPath &nodePath = nodeEntry.first; + + const auto nodeIt = _nodes.find(nodePath); + if (nodeIt == _nodes.end()) { + TF_RUNTIME_ERROR("Could not find node '%s' to connect", nodePath.GetText()); + continue; + } + + UpdateConnections(nodeIt->second, nodeEntry.second, nodePath, graph); + } + + // Finally connect the terminals to the graph output (Surface, Volume, Displacement) + for (const std::pair<TfToken, HdMaterialConnection2> &terminalEntry : networkMap.terminals) { + const TfToken &terminalName = terminalEntry.first; + const HdMaterialConnection2 &connection = terminalEntry.second; + + const auto nodeIt = _nodes.find(connection.upstreamNode); + if (nodeIt == _nodes.end()) { + TF_RUNTIME_ERROR("Could not find terminal node '%s'", connection.upstreamNode.GetText()); + continue; + } + + ShaderNode *const node = nodeIt->second.node; + + const char *inputName = nullptr; + const char *outputName = nullptr; + if (terminalName == HdMaterialTerminalTokens->surface || + terminalName == CyclesMaterialTokens->cyclesSurface) { + inputName = "Surface"; + // Find default output name based on the node if none is provided + if (node->type->name == "add_closure" || node->type->name == "mix_closure") { + outputName = "Closure"; + } + else if (node->type->name == "emission") { + outputName = "Emission"; + } + else { + outputName = "BSDF"; + } + } + else if (terminalName == HdMaterialTerminalTokens->displacement || + terminalName == CyclesMaterialTokens->cyclesDisplacement) { + inputName = outputName = "Displacement"; + } + else if (terminalName == HdMaterialTerminalTokens->volume || + terminalName == CyclesMaterialTokens->cyclesVolume) { + inputName = outputName = "Volume"; + } + + if (!connection.upstreamOutputName.IsEmpty()) { + outputName = connection.upstreamOutputName.GetText(); + } + + ShaderInput *const input = inputName ? graph->output()->input(inputName) : nullptr; + if (!input) { + TF_RUNTIME_ERROR("Could not find terminal input '%s.%s'", + connection.upstreamNode.GetText(), + inputName ? inputName : "<null>"); + continue; + } + + ShaderOutput *const output = outputName ? node->output(outputName) : nullptr; + if (!output) { + TF_RUNTIME_ERROR("Could not find terminal output '%s.%s'", + connection.upstreamNode.GetText(), + outputName ? outputName : "<null>"); + continue; + } + + graph->connect(output, input); + } + + // Create the instanceId AOV output + { + const ustring instanceId(HdAovTokens->instanceId.GetString()); + + OutputAOVNode *aovNode = graph->create_node<OutputAOVNode>(); + aovNode->set_name(instanceId); + graph->add(aovNode); + + AttributeNode *instanceIdNode = graph->create_node<AttributeNode>(); + instanceIdNode->set_attribute(instanceId); + graph->add(instanceIdNode); + + graph->connect(instanceIdNode->output("Fac"), aovNode->input("Value")); + } + + _shader->set_graph(graph); +} + +void HdCyclesMaterial::Finalize(HdRenderParam *renderParam) +{ + if (!_shader) { + return; + } + + const SceneLock lock(renderParam); + + _nodes.clear(); + + lock.scene->delete_node(_shader); + _shader = nullptr; +} + +void HdCyclesMaterial::Initialize(HdRenderParam *renderParam) +{ + if (_shader) { + return; + } + + const SceneLock lock(renderParam); + + _shader = lock.scene->create_node<Shader>(); +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/material.h b/intern/cycles/hydra/material.h new file mode 100644 index 00000000000..15925671bb8 --- /dev/null +++ b/intern/cycles/hydra/material.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/imaging/hd/material.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesMaterial final : public PXR_NS::HdMaterial { + public: + HdCyclesMaterial(const PXR_NS::SdfPath &sprimId); + ~HdCyclesMaterial() override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + void Sync(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdRenderParam *renderParam, + PXR_NS::HdDirtyBits *dirtyBits) override; + +#if PXR_VERSION < 2011 + void Reload() override + { + } +#endif + + void Finalize(PXR_NS::HdRenderParam *renderParam) override; + + CCL_NS::Shader *GetCyclesShader() const + { + return _shader; + } + + struct NodeDesc; + + private: + void Initialize(PXR_NS::HdRenderParam *renderParam); + + void UpdateParameters(NodeDesc &nodeDesc, + const std::map<PXR_NS::TfToken, PXR_NS::VtValue> ¶meters, + const PXR_NS::SdfPath &nodePath); + + void UpdateParameters(const PXR_NS::HdMaterialNetwork &network); + void UpdateParameters(const PXR_NS::HdMaterialNetwork2 &network); + + void UpdateConnections(NodeDesc &nodeDesc, + const PXR_NS::HdMaterialNode2 &matNode, + const PXR_NS::SdfPath &nodePath, + CCL_NS::ShaderGraph *shaderGraph); + + void PopulateShaderGraph(const PXR_NS::HdMaterialNetwork2 &network); + + CCL_NS::Shader *_shader = nullptr; + std::unordered_map<PXR_NS::SdfPath, NodeDesc, PXR_NS::SdfPath::Hash> _nodes; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/mesh.cpp b/intern/cycles/hydra/mesh.cpp new file mode 100644 index 00000000000..155843458ef --- /dev/null +++ b/intern/cycles/hydra/mesh.cpp @@ -0,0 +1,524 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/mesh.h" +#include "hydra/geometry.inl" +#include "scene/mesh.h" + +#include <pxr/base/gf/vec2f.h> +#include <pxr/imaging/hd/extComputationUtils.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +namespace { + +template<typename T> +VtValue ComputeTriangulatedUniformPrimvar(VtValue value, const VtIntArray &primitiveParams) +{ + T output; + output.reserve(primitiveParams.size()); + const T &input = value.Get<T>(); + + for (size_t i = 0; i < primitiveParams.size(); ++i) { + const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(primitiveParams[i]); + + output.push_back(input[faceIndex]); + } + + return VtValue(output); +} + +VtValue ComputeTriangulatedUniformPrimvar(VtValue value, + const HdType valueType, + const VtIntArray &primitiveParams) +{ + switch (valueType) { + case HdTypeFloat: + return ComputeTriangulatedUniformPrimvar<VtFloatArray>(value, primitiveParams); + case HdTypeFloatVec2: + return ComputeTriangulatedUniformPrimvar<VtVec2fArray>(value, primitiveParams); + case HdTypeFloatVec3: + return ComputeTriangulatedUniformPrimvar<VtVec3fArray>(value, primitiveParams); + case HdTypeFloatVec4: + return ComputeTriangulatedUniformPrimvar<VtVec4fArray>(value, primitiveParams); + default: + TF_RUNTIME_ERROR("Unsupported attribute type %d", static_cast<int>(valueType)); + return VtValue(); + } +} + +VtValue ComputeTriangulatedFaceVaryingPrimvar(VtValue value, + const HdType valueType, + HdMeshUtil &meshUtil) +{ + if (meshUtil.ComputeTriangulatedFaceVaryingPrimvar( + HdGetValueData(value), value.GetArraySize(), valueType, &value)) { + return value; + } + + return VtValue(); +} + +} // namespace + +Transform convert_transform(const GfMatrix4d &matrix) +{ + return make_transform(matrix[0][0], + matrix[1][0], + matrix[2][0], + matrix[3][0], + matrix[0][1], + matrix[1][1], + matrix[2][1], + matrix[3][1], + matrix[0][2], + matrix[1][2], + matrix[2][2], + matrix[3][2]); +} + +HdCyclesMesh::HdCyclesMesh(const SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const SdfPath &instancerId +#endif + ) + : HdCyclesGeometry(rprimId +#if PXR_VERSION < 2102 + , + instancerId +#endif + ), + _util(&_topology, rprimId) +{ +} + +HdCyclesMesh::~HdCyclesMesh() +{ +} + +HdDirtyBits HdCyclesMesh::GetInitialDirtyBitsMask() const +{ + HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask(); + bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyNormals | + HdChangeTracker::DirtyPrimvar | HdChangeTracker::DirtyTopology | + HdChangeTracker::DirtyDisplayStyle | HdChangeTracker::DirtySubdivTags; + return bits; +} + +HdDirtyBits HdCyclesMesh::_PropagateDirtyBits(HdDirtyBits bits) const +{ + if (bits & (HdChangeTracker::DirtyMaterialId)) { + // Update used shaders from geometry subsets if any exist in the topology + bits |= HdChangeTracker::DirtyTopology; + } + + if (bits & (HdChangeTracker::DirtyTopology | HdChangeTracker::DirtyDisplayStyle | + HdChangeTracker::DirtySubdivTags)) { + // Do full topology update when display style or subdivision changes + bits |= HdChangeTracker::DirtyTopology | HdChangeTracker::DirtyDisplayStyle | + HdChangeTracker::DirtySubdivTags; + } + + if (bits & (HdChangeTracker::DirtyTopology)) { + // Changing topology clears the geometry, so need to populate everything again + bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyNormals | + HdChangeTracker::DirtyPrimvar; + } + + return bits; +} + +void HdCyclesMesh::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild) +{ + if (HdChangeTracker::IsTopologyDirty(dirtyBits, GetId())) { + PopulateTopology(sceneDelegate); + } + + if (dirtyBits & HdChangeTracker::DirtyPoints) { + PopulatePoints(sceneDelegate); + } + + // Must happen after topology update, so that normals attribute size can be calculated + if (dirtyBits & HdChangeTracker::DirtyNormals) { + PopulateNormals(sceneDelegate); + } + + // Must happen after topology update, so that appropriate attribute set can be selected + if (dirtyBits & HdChangeTracker::DirtyPrimvar) { + PopulatePrimvars(sceneDelegate); + } + + rebuild = (_geom->triangles_is_modified()) || (_geom->subd_start_corner_is_modified()) || + (_geom->subd_num_corners_is_modified()) || (_geom->subd_shader_is_modified()) || + (_geom->subd_smooth_is_modified()) || (_geom->subd_ptex_offset_is_modified()) || + (_geom->subd_face_corners_is_modified()); +} + +void HdCyclesMesh::PopulatePoints(HdSceneDelegate *sceneDelegate) +{ + VtValue value; + + for (const HdExtComputationPrimvarDescriptor &desc : + sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), HdInterpolationVertex)) { + if (desc.name == HdTokens->points) { + auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate); + const auto valueStoreIt = valueStore.find(desc.name); + if (valueStoreIt != valueStore.end()) { + value = std::move(valueStoreIt->second); + } + break; + } + } + + if (value.IsEmpty()) { + value = GetPoints(sceneDelegate); + } + + if (!value.IsHolding<VtVec3fArray>()) { + TF_WARN("Invalid points data for %s", GetId().GetText()); + return; + } + + const auto &points = value.UncheckedGet<VtVec3fArray>(); + + TF_VERIFY(points.size() >= static_cast<size_t>(_topology.GetNumPoints())); + + array<float3> pointsDataCycles; + pointsDataCycles.reserve(points.size()); + for (const GfVec3f &point : points) { + pointsDataCycles.push_back_reserved(make_float3(point[0], point[1], point[2])); + } + + _geom->set_verts(pointsDataCycles); +} + +void HdCyclesMesh::PopulateNormals(HdSceneDelegate *sceneDelegate) +{ + _geom->attributes.remove(ATTR_STD_FACE_NORMAL); + _geom->attributes.remove(ATTR_STD_VERTEX_NORMAL); + + // Authored normals should only exist on triangle meshes + if (_geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE) { + return; + } + + VtValue value; + HdInterpolation interpolation = HdInterpolationCount; + + for (int i = 0; i < HdInterpolationCount && interpolation == HdInterpolationCount; ++i) { + for (const HdExtComputationPrimvarDescriptor &desc : + sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), + static_cast<HdInterpolation>(i))) { + if (desc.name == HdTokens->normals) { + auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate); + const auto valueStoreIt = valueStore.find(desc.name); + if (valueStoreIt != valueStore.end()) { + value = std::move(valueStoreIt->second); + interpolation = static_cast<HdInterpolation>(i); + } + break; + } + } + } + + if (value.IsEmpty()) { + interpolation = GetPrimvarInterpolation(sceneDelegate, HdTokens->normals); + if (interpolation == HdInterpolationCount) { + return; // Ignore missing normals + } + + value = GetNormals(sceneDelegate); + } + + if (!value.IsHolding<VtVec3fArray>()) { + TF_WARN("Invalid normals data for %s", GetId().GetText()); + return; + } + + const auto &normals = value.UncheckedGet<VtVec3fArray>(); + + if (interpolation == HdInterpolationConstant) { + TF_VERIFY(normals.size() == 1); + + const GfVec3f constantNormal = normals[0]; + + float3 *const N = _geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3(); + for (size_t i = 0; i < _geom->get_verts().size(); ++i) { + N[i] = make_float3(constantNormal[0], constantNormal[1], constantNormal[2]); + } + } + else if (interpolation == HdInterpolationUniform) { + TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumFaces())); + + float3 *const N = _geom->attributes.add(ATTR_STD_FACE_NORMAL)->data_float3(); + for (size_t i = 0; i < _geom->num_triangles(); ++i) { + const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(_primitiveParams[i]); + + N[i] = make_float3(normals[faceIndex][0], normals[faceIndex][1], normals[faceIndex][2]); + } + } + else if (interpolation == HdInterpolationVertex || interpolation == HdInterpolationVarying) { + TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumPoints()) && + static_cast<size_t>(_topology.GetNumPoints()) == _geom->get_verts().size()); + + float3 *const N = _geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3(); + for (size_t i = 0; i < _geom->get_verts().size(); ++i) { + N[i] = make_float3(normals[i][0], normals[i][1], normals[i][2]); + } + } + else if (interpolation == HdInterpolationFaceVarying) { + TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumFaceVaryings())); + + if (!_util.ComputeTriangulatedFaceVaryingPrimvar( + normals.data(), normals.size(), HdTypeFloatVec3, &value)) { + return; + } + + const auto &normalsTriangulated = value.UncheckedGet<VtVec3fArray>(); + + // Cycles has no standard attribute for face-varying normals, so this is a lossy transformation + float3 *const N = _geom->attributes.add(ATTR_STD_FACE_NORMAL)->data_float3(); + for (size_t i = 0; i < _geom->num_triangles(); ++i) { + GfVec3f averageNormal = normalsTriangulated[i * 3] + normalsTriangulated[i * 3 + 1] + + normalsTriangulated[i * 3 + 2]; + GfNormalize(&averageNormal); + + N[i] = make_float3(averageNormal[0], averageNormal[1], averageNormal[2]); + } + } +} + +void HdCyclesMesh::PopulatePrimvars(HdSceneDelegate *sceneDelegate) +{ + Scene *const scene = (Scene *)_geom->get_owner(); + + const bool subdivision = _geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE; + AttributeSet &attributes = subdivision ? _geom->subd_attributes : _geom->attributes; + + const std::pair<HdInterpolation, AttributeElement> interpolations[] = { + std::make_pair(HdInterpolationFaceVarying, ATTR_ELEMENT_CORNER), + std::make_pair(HdInterpolationUniform, ATTR_ELEMENT_FACE), + std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_VERTEX), + std::make_pair(HdInterpolationVarying, ATTR_ELEMENT_VERTEX), + std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT), + }; + + for (const auto &interpolation : interpolations) { + for (const HdPrimvarDescriptor &desc : + GetPrimvarDescriptors(sceneDelegate, interpolation.first)) { + // Skip special primvars that are handled separately + if (desc.name == HdTokens->points || desc.name == HdTokens->normals) { + continue; + } + + VtValue value = GetPrimvar(sceneDelegate, desc.name); + if (value.IsEmpty()) { + continue; + } + + const ustring name(desc.name.GetString()); + + AttributeStandard std = ATTR_STD_NONE; + if (desc.role == HdPrimvarRoleTokens->textureCoordinate) { + std = ATTR_STD_UV; + } + else if (interpolation.first == HdInterpolationVertex) { + if (desc.name == HdTokens->displayColor || desc.role == HdPrimvarRoleTokens->color) { + std = ATTR_STD_VERTEX_COLOR; + } + else if (desc.name == HdTokens->normals) { + std = ATTR_STD_VERTEX_NORMAL; + } + } + else if (desc.name == HdTokens->displayColor && + interpolation.first == HdInterpolationConstant) { + if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) { + const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0]; + _instances[0]->set_color(make_float3(color[0], color[1], color[2])); + } + } + + // Skip attributes that are not needed + if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) || + _geom->need_attribute(scene, name)) { + const HdType valueType = HdGetValueTupleType(value).type; + + if (!subdivision) { + // Adjust attributes for polygons that were triangulated + if (interpolation.first == HdInterpolationUniform) { + value = ComputeTriangulatedUniformPrimvar(value, valueType, _primitiveParams); + if (value.IsEmpty()) { + continue; + } + } + else if (interpolation.first == HdInterpolationFaceVarying) { + value = ComputeTriangulatedFaceVaryingPrimvar(value, valueType, _util); + if (value.IsEmpty()) { + continue; + } + } + } + + ApplyPrimvars(attributes, name, value, interpolation.second, std); + } + } + } +} + +void HdCyclesMesh::PopulateTopology(HdSceneDelegate *sceneDelegate) +{ + // Clear geometry before populating it again with updated topology + _geom->clear(true); + + const HdDisplayStyle displayStyle = GetDisplayStyle(sceneDelegate); + _topology = HdMeshTopology(GetMeshTopology(sceneDelegate), displayStyle.refineLevel); + + const TfToken subdivScheme = _topology.GetScheme(); + if (subdivScheme == PxOsdOpenSubdivTokens->bilinear && _topology.GetRefineLevel() > 0) { + _geom->set_subdivision_type(Mesh::SUBDIVISION_LINEAR); + } + else if (subdivScheme == PxOsdOpenSubdivTokens->catmullClark && _topology.GetRefineLevel() > 0) { + _geom->set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK); + } + else { + _geom->set_subdivision_type(Mesh::SUBDIVISION_NONE); + } + + const bool smooth = !displayStyle.flatShadingEnabled; + const bool subdivision = _geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE; + + // Initialize lookup table from polygon face to material shader index + VtIntArray faceShaders(_topology.GetNumFaces(), 0); + + HdGeomSubsets const &geomSubsets = _topology.GetGeomSubsets(); + if (!geomSubsets.empty()) { + array<Node *> usedShaders = std::move(_geom->get_used_shaders()); + // Remove any previous materials except for the material assigned to the prim + usedShaders.resize(1); + + std::unordered_map<SdfPath, int, SdfPath::Hash> materials; + + for (const HdGeomSubset &geomSubset : geomSubsets) { + TF_VERIFY(geomSubset.type == HdGeomSubset::TypeFaceSet); + + int shader = 0; + const auto it = materials.find(geomSubset.materialId); + if (it != materials.end()) { + shader = it->second; + } + else { + const auto material = static_cast<const HdCyclesMaterial *>( + sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, + geomSubset.materialId)); + + if (material && material->GetCyclesShader()) { + shader = static_cast<int>(usedShaders.size()); + usedShaders.push_back_slow(material->GetCyclesShader()); + + materials.emplace(geomSubset.materialId, shader); + } + } + + for (int face : geomSubset.indices) { + faceShaders[face] = shader; + } + } + + _geom->set_used_shaders(usedShaders); + } + + const VtIntArray vertIndx = _topology.GetFaceVertexIndices(); + const VtIntArray vertCounts = _topology.GetFaceVertexCounts(); + + if (!subdivision) { + VtVec3iArray triangles; + _util.ComputeTriangleIndices(&triangles, &_primitiveParams); + + _geom->reserve_mesh(_topology.GetNumPoints(), triangles.size()); + + for (size_t i = 0; i < _primitiveParams.size(); ++i) { + const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(_primitiveParams[i]); + + const GfVec3i triangle = triangles[i]; + _geom->add_triangle(triangle[0], triangle[1], triangle[2], faceShaders[faceIndex], smooth); + } + } + else { + PxOsdSubdivTags subdivTags = GetSubdivTags(sceneDelegate); + _topology.SetSubdivTags(subdivTags); + + size_t numNgons = 0; + size_t numCorners = 0; + for (int vertCount : vertCounts) { + numNgons += (vertCount == 4) ? 0 : 1; + numCorners += vertCount; + } + + _geom->reserve_subd_faces(_topology.GetNumFaces(), numNgons, numCorners); + + // TODO: Handle hole indices + size_t faceIndex = 0; + size_t indexOffset = 0; + for (int vertCount : vertCounts) { + _geom->add_subd_face(&vertIndx[indexOffset], vertCount, faceShaders[faceIndex], smooth); + + faceIndex++; + indexOffset += vertCount; + } + + const VtIntArray creaseLengths = subdivTags.GetCreaseLengths(); + if (!creaseLengths.empty()) { + size_t numCreases = 0; + for (int creaseLength : creaseLengths) { + numCreases += creaseLength - 1; + } + + _geom->reserve_subd_creases(numCreases); + + const VtIntArray creaseIndices = subdivTags.GetCreaseIndices(); + const VtFloatArray creaseWeights = subdivTags.GetCreaseWeights(); + + indexOffset = 0; + size_t creaseLengthOffset = 0; + size_t createWeightOffset = 0; + for (int creaseLength : creaseLengths) { + for (int j = 0; j < creaseLength - 1; ++j, ++createWeightOffset) { + const int v0 = creaseIndices[indexOffset + j]; + const int v1 = creaseIndices[indexOffset + j + 1]; + + float weight = creaseWeights.size() == creaseLengths.size() ? + creaseWeights[creaseLengthOffset] : + creaseWeights[createWeightOffset]; + + _geom->add_edge_crease(v0, v1, weight); + } + + indexOffset += creaseLength; + creaseLengthOffset++; + } + + const VtIntArray cornerIndices = subdivTags.GetCornerIndices(); + const VtFloatArray cornerWeights = subdivTags.GetCornerWeights(); + + for (size_t i = 0; i < cornerIndices.size(); ++i) { + _geom->add_vertex_crease(cornerIndices[i], cornerWeights[i]); + } + } + + _geom->set_subd_dicing_rate(1.0f); + _geom->set_subd_max_level(_topology.GetRefineLevel()); + _geom->set_subd_objecttoworld(_instances[0]->get_tfm()); + } +} + +void HdCyclesMesh::Finalize(PXR_NS::HdRenderParam *renderParam) +{ + _topology = HdMeshTopology(); + _primitiveParams.clear(); + + HdCyclesGeometry<PXR_NS::HdMesh, Mesh>::Finalize(renderParam); +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/mesh.h b/intern/cycles/hydra/mesh.h new file mode 100644 index 00000000000..8ec108534a1 --- /dev/null +++ b/intern/cycles/hydra/mesh.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "hydra/geometry.h" + +#include <pxr/imaging/hd/mesh.h> +#include <pxr/imaging/hd/meshUtil.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesMesh final : public HdCyclesGeometry<PXR_NS::HdMesh, CCL_NS::Mesh> { + public: + HdCyclesMesh( + const PXR_NS::SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const PXR_NS::SdfPath &instancerId = {} +#endif + ); + ~HdCyclesMesh() override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + void Finalize(PXR_NS::HdRenderParam *renderParam) override; + + private: + PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override; + + void Populate(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdDirtyBits dirtyBits, + bool &rebuild) override; + + void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate); + void PopulateNormals(PXR_NS::HdSceneDelegate *sceneDelegate); + + void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate); + + void PopulateTopology(PXR_NS::HdSceneDelegate *sceneDelegate); + + PXR_NS::HdMeshUtil _util; + PXR_NS::HdMeshTopology _topology; + PXR_NS::VtIntArray _primitiveParams; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/node_util.cpp b/intern/cycles/hydra/node_util.cpp new file mode 100644 index 00000000000..c7e49688f5c --- /dev/null +++ b/intern/cycles/hydra/node_util.cpp @@ -0,0 +1,561 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/node_util.h" +#include "util/transform.h" + +#include <pxr/base/gf/matrix3d.h> +#include <pxr/base/gf/matrix3f.h> +#include <pxr/base/gf/matrix4d.h> +#include <pxr/base/gf/matrix4f.h> +#include <pxr/base/gf/vec2f.h> +#include <pxr/base/gf/vec3f.h> +#include <pxr/base/vt/array.h> +#include <pxr/usd/sdf/assetPath.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +namespace { + +template<typename DstType> DstType convertToCycles(const VtValue &value) +{ + if (value.IsHolding<DstType>()) { + return value.UncheckedGet<DstType>(); + } + + VtValue castedValue = VtValue::Cast<DstType>(value); + if (castedValue.IsHolding<DstType>()) { + return castedValue.UncheckedGet<DstType>(); + } + + TF_WARN("Could not convert VtValue to Cycles type"); + return DstType(0); +} + +template<> float2 convertToCycles<float2>(const VtValue &value) +{ + const GfVec2f convertedValue = convertToCycles<GfVec2f>(value); + return make_float2(convertedValue[0], convertedValue[1]); +} + +template<> float3 convertToCycles<float3>(const VtValue &value) +{ + if (value.IsHolding<GfVec3f>()) { + const GfVec3f convertedValue = value.UncheckedGet<GfVec3f>(); + return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]); + } + if (value.IsHolding<GfVec4f>()) { + const GfVec4f convertedValue = value.UncheckedGet<GfVec4f>(); + return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]); + } + + if (value.CanCast<GfVec3f>()) { + const GfVec3f convertedValue = VtValue::Cast<GfVec3f>(value).UncheckedGet<GfVec3f>(); + return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]); + } + if (value.CanCast<GfVec4f>()) { + const GfVec4f convertedValue = VtValue::Cast<GfVec4f>(value).UncheckedGet<GfVec4f>(); + return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]); + } + + TF_WARN("Could not convert VtValue to float3"); + return zero_float3(); +} + +template<> ustring convertToCycles<ustring>(const VtValue &value) +{ + if (value.IsHolding<TfToken>()) { + return ustring(value.UncheckedGet<TfToken>().GetString()); + } + if (value.IsHolding<std::string>()) { + return ustring(value.UncheckedGet<std::string>()); + } + if (value.IsHolding<SdfAssetPath>()) { + const SdfAssetPath &path = value.UncheckedGet<SdfAssetPath>(); + return ustring(path.GetResolvedPath()); + } + + if (value.CanCast<TfToken>()) { + return convertToCycles<ustring>(VtValue::Cast<TfToken>(value)); + } + if (value.CanCast<std::string>()) { + return convertToCycles<ustring>(VtValue::Cast<std::string>(value)); + } + if (value.CanCast<SdfAssetPath>()) { + return convertToCycles<ustring>(VtValue::Cast<SdfAssetPath>(value)); + } + + TF_WARN("Could not convert VtValue to ustring"); + return ustring(); +} + +template<typename Matrix> +Transform convertMatrixToCycles( + const typename std::enable_if<Matrix::numRows == 3 && Matrix::numColumns == 3, Matrix>::type + &matrix) +{ + return make_transform(matrix[0][0], + matrix[1][0], + matrix[2][0], + 0, + matrix[0][1], + matrix[1][1], + matrix[2][1], + 0, + matrix[0][2], + matrix[1][2], + matrix[2][2], + 0); +} + +template<typename Matrix> +Transform convertMatrixToCycles( + const typename std::enable_if<Matrix::numRows == 4 && Matrix::numColumns == 4, Matrix>::type + &matrix) +{ + return make_transform(matrix[0][0], + matrix[1][0], + matrix[2][0], + matrix[3][0], + matrix[0][1], + matrix[1][1], + matrix[2][1], + matrix[3][1], + matrix[0][2], + matrix[1][2], + matrix[2][2], + matrix[3][2]); +} + +template<> Transform convertToCycles<Transform>(const VtValue &value) +{ + if (value.IsHolding<GfMatrix4f>()) { + return convertMatrixToCycles<GfMatrix4f>(value.UncheckedGet<GfMatrix4f>()); + } + if (value.IsHolding<GfMatrix3f>()) { + return convertMatrixToCycles<GfMatrix3f>(value.UncheckedGet<GfMatrix3f>()); + } + if (value.IsHolding<GfMatrix4d>()) { + return convertMatrixToCycles<GfMatrix4d>(value.UncheckedGet<GfMatrix4d>()); + } + if (value.IsHolding<GfMatrix3d>()) { + return convertMatrixToCycles<GfMatrix3d>(value.UncheckedGet<GfMatrix3d>()); + } + + if (value.CanCast<GfMatrix4f>()) { + return convertToCycles<Transform>(VtValue::Cast<GfMatrix4f>(value)); + } + if (value.CanCast<GfMatrix3f>()) { + return convertToCycles<Transform>(VtValue::Cast<GfMatrix3f>(value)); + } + if (value.CanCast<GfMatrix4d>()) { + return convertToCycles<Transform>(VtValue::Cast<GfMatrix4d>(value)); + } + if (value.CanCast<GfMatrix3d>()) { + return convertToCycles<Transform>(VtValue::Cast<GfMatrix3d>(value)); + } + + TF_WARN("Could not convert VtValue to Transform"); + return transform_identity(); +} + +template<typename DstType, typename SrcType = DstType> +array<DstType> convertToCyclesArray(const VtValue &value) +{ + static_assert(sizeof(DstType) == sizeof(SrcType), + "Size mismatch between VtArray and array base type"); + + using SrcArray = VtArray<SrcType>; + + if (value.IsHolding<SrcArray>()) { + const auto &valueData = value.UncheckedGet<SrcArray>(); + array<DstType> cyclesArray; + cyclesArray.resize(valueData.size()); + std::memcpy(cyclesArray.data(), valueData.data(), valueData.size() * sizeof(DstType)); + return cyclesArray; + } + + if (value.CanCast<SrcArray>()) { + VtValue castedValue = VtValue::Cast<SrcArray>(value); + const auto &valueData = castedValue.UncheckedGet<SrcArray>(); + array<DstType> cyclesArray; + cyclesArray.resize(valueData.size()); + std::memcpy(cyclesArray.data(), valueData.data(), valueData.size() * sizeof(DstType)); + return cyclesArray; + } + + return array<DstType>(); +} + +template<> array<float3> convertToCyclesArray<float3, GfVec3f>(const VtValue &value) +{ + if (value.IsHolding<VtVec3fArray>()) { + const auto &valueData = value.UncheckedGet<VtVec3fArray>(); + array<float3> cyclesArray; + cyclesArray.reserve(valueData.size()); + for (const GfVec3f &vec : valueData) { + cyclesArray.push_back_reserved(make_float3(vec[0], vec[1], vec[2])); + } + return cyclesArray; + } + if (value.IsHolding<VtVec4fArray>()) { + const auto &valueData = value.UncheckedGet<VtVec4fArray>(); + array<float3> cyclesArray; + cyclesArray.reserve(valueData.size()); + for (const GfVec4f &vec : valueData) { + cyclesArray.push_back_reserved(make_float3(vec[0], vec[1], vec[2])); + } + return cyclesArray; + } + + if (value.CanCast<VtVec3fArray>()) { + return convertToCyclesArray<float3, GfVec3f>(VtValue::Cast<VtVec3fArray>(value)); + } + if (value.CanCast<VtVec4fArray>()) { + return convertToCyclesArray<float3, GfVec3f>(VtValue::Cast<VtVec4fArray>(value)); + } + + return array<float3>(); +} + +template<> array<ustring> convertToCyclesArray<ustring, void>(const VtValue &value) +{ + using SdfPathArray = VtArray<SdfAssetPath>; + + if (value.IsHolding<VtStringArray>()) { + const auto &valueData = value.UncheckedGet<VtStringArray>(); + array<ustring> cyclesArray; + cyclesArray.reserve(valueData.size()); + for (const auto &element : valueData) { + cyclesArray.push_back_reserved(ustring(element)); + } + return cyclesArray; + } + if (value.IsHolding<VtTokenArray>()) { + const auto &valueData = value.UncheckedGet<VtTokenArray>(); + array<ustring> cyclesArray; + cyclesArray.reserve(valueData.size()); + for (const auto &element : valueData) { + cyclesArray.push_back_reserved(ustring(element.GetString())); + } + return cyclesArray; + } + if (value.IsHolding<SdfPathArray>()) { + const auto &valueData = value.UncheckedGet<SdfPathArray>(); + array<ustring> cyclesArray; + cyclesArray.reserve(valueData.size()); + for (const auto &element : valueData) { + cyclesArray.push_back_reserved(ustring(element.GetResolvedPath())); + } + return cyclesArray; + } + + if (value.CanCast<VtStringArray>()) { + return convertToCyclesArray<ustring, void>(VtValue::Cast<VtStringArray>(value)); + } + if (value.CanCast<VtTokenArray>()) { + return convertToCyclesArray<ustring, void>(VtValue::Cast<VtTokenArray>(value)); + } + if (value.CanCast<SdfPathArray>()) { + return convertToCyclesArray<ustring, void>(VtValue::Cast<SdfPathArray>(value)); + } + + TF_WARN("Could not convert VtValue to array<ustring>"); + return array<ustring>(); +} + +template<typename MatrixArray> array<Transform> convertToCyclesTransformArray(const VtValue &value) +{ + assert(value.IsHolding<MatrixArray>()); + + const auto &valueData = value.UncheckedGet<MatrixArray>(); + array<Transform> cyclesArray; + cyclesArray.reserve(valueData.size()); + for (const auto &element : valueData) { + cyclesArray.push_back_reserved(convertMatrixToCycles<MatrixArray::value_type>(element)); + } + return cyclesArray; +} + +template<> array<Transform> convertToCyclesArray<Transform, void>(const VtValue &value) +{ + if (value.IsHolding<VtMatrix4fArray>()) { + return convertToCyclesTransformArray<VtMatrix4fArray>(value); + } + if (value.IsHolding<VtMatrix3fArray>()) { + return convertToCyclesTransformArray<VtMatrix3fArray>(value); + } + if (value.IsHolding<VtMatrix4dArray>()) { + return convertToCyclesTransformArray<VtMatrix4dArray>(value); + } + if (value.IsHolding<VtMatrix3dArray>()) { + return convertToCyclesTransformArray<VtMatrix3dArray>(value); + } + + if (value.CanCast<VtMatrix4fArray>()) { + return convertToCyclesTransformArray<VtMatrix4fArray>(VtValue::Cast<VtMatrix4fArray>(value)); + } + if (value.CanCast<VtMatrix3fArray>()) { + return convertToCyclesTransformArray<VtMatrix3fArray>(VtValue::Cast<VtMatrix3fArray>(value)); + } + if (value.CanCast<VtMatrix4dArray>()) { + return convertToCyclesTransformArray<VtMatrix4dArray>(VtValue::Cast<VtMatrix4dArray>(value)); + } + if (value.CanCast<VtMatrix3dArray>()) { + return convertToCyclesTransformArray<VtMatrix3dArray>(VtValue::Cast<VtMatrix3dArray>(value)); + } + + TF_WARN("Could not convert VtValue to array<Transform>"); + return array<Transform>(); +} + +template<typename SrcType> VtValue convertFromCycles(const SrcType &value) +{ + return VtValue(value); +} + +template<> VtValue convertFromCycles<float2>(const float2 &value) +{ + const GfVec2f convertedValue(value.x, value.y); + return VtValue(convertedValue); +} + +template<> VtValue convertFromCycles<float3>(const float3 &value) +{ + const GfVec3f convertedValue(value.x, value.y, value.z); + return VtValue(convertedValue); +} + +template<> VtValue convertFromCycles<ustring>(const ustring &value) +{ + return VtValue(value.string()); +} + +GfMatrix4f convertMatrixFromCycles(const Transform &matrix) +{ + return GfMatrix4f(matrix[0][0], + matrix[1][0], + matrix[2][0], + 0.0f, + matrix[0][1], + matrix[1][1], + matrix[2][1], + 0.0f, + matrix[0][2], + matrix[1][2], + matrix[2][2], + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f); +} + +template<> VtValue convertFromCycles<Transform>(const Transform &value) +{ + return VtValue(convertMatrixFromCycles(value)); +} + +template<typename SrcType, typename DstType = SrcType> +VtValue convertFromCyclesArray(const array<SrcType> &value) +{ + static_assert(sizeof(DstType) == sizeof(SrcType), + "Size mismatch between VtArray and array base type"); + + VtArray<DstType> convertedValue; + convertedValue.resize(value.size()); + std::memcpy(convertedValue.data(), value.data(), value.size() * sizeof(SrcType)); + return VtValue(convertedValue); +} + +template<> VtValue convertFromCyclesArray<float3, GfVec3f>(const array<float3> &value) +{ + VtVec3fArray convertedValue; + convertedValue.reserve(value.size()); + for (const auto &element : value) { + convertedValue.push_back(GfVec3f(element.x, element.y, element.z)); + } + return VtValue(convertedValue); +} + +template<> VtValue convertFromCyclesArray<ustring, void>(const array<ustring> &value) +{ + VtStringArray convertedValue; + convertedValue.reserve(value.size()); + for (const auto &element : value) { + convertedValue.push_back(element.string()); + } + return VtValue(convertedValue); +} + +template<> VtValue convertFromCyclesArray<Transform, void>(const array<Transform> &value) +{ + VtMatrix4fArray convertedValue; + convertedValue.reserve(value.size()); + for (const auto &element : value) { + convertedValue.push_back(convertMatrixFromCycles(element)); + } + return VtValue(convertedValue); +} + +} // namespace + +void SetNodeValue(Node *node, const SocketType &socket, const VtValue &value) +{ + switch (socket.type) { + default: + case SocketType::UNDEFINED: + TF_RUNTIME_ERROR("Unexpected conversion: SocketType::UNDEFINED"); + break; + + case SocketType::BOOLEAN: + node->set(socket, convertToCycles<bool>(value)); + break; + case SocketType::FLOAT: + node->set(socket, convertToCycles<float>(value)); + break; + case SocketType::INT: + node->set(socket, convertToCycles<int>(value)); + break; + case SocketType::UINT: + node->set(socket, convertToCycles<unsigned int>(value)); + break; + case SocketType::COLOR: + case SocketType::VECTOR: + case SocketType::POINT: + case SocketType::NORMAL: + node->set(socket, convertToCycles<float3>(value)); + break; + case SocketType::POINT2: + node->set(socket, convertToCycles<float2>(value)); + break; + case SocketType::CLOSURE: + // Handled by node connections + break; + case SocketType::STRING: + node->set(socket, convertToCycles<ustring>(value)); + break; + case SocketType::ENUM: + // Enum's can accept a string or an int + if (value.IsHolding<TfToken>() || value.IsHolding<std::string>()) { + node->set(socket, convertToCycles<ustring>(value)); + } + else { + node->set(socket, convertToCycles<int>(value)); + } + break; + case SocketType::TRANSFORM: + node->set(socket, convertToCycles<Transform>(value)); + break; + case SocketType::NODE: + // TODO: renderIndex->GetRprim()->cycles_node ? + TF_WARN("Unimplemented conversion: SocketType::NODE"); + break; + + case SocketType::BOOLEAN_ARRAY: { + auto cyclesArray = convertToCyclesArray<bool>(value); + node->set(socket, cyclesArray); + break; + } + case SocketType::FLOAT_ARRAY: { + auto cyclesArray = convertToCyclesArray<float>(value); + node->set(socket, cyclesArray); + break; + } + case SocketType::INT_ARRAY: { + auto cyclesArray = convertToCyclesArray<int>(value); + node->set(socket, cyclesArray); + break; + } + case SocketType::COLOR_ARRAY: + case SocketType::VECTOR_ARRAY: + case SocketType::POINT_ARRAY: + case SocketType::NORMAL_ARRAY: { + auto cyclesArray = convertToCyclesArray<float3, GfVec3f>(value); + node->set(socket, cyclesArray); + break; + } + case SocketType::POINT2_ARRAY: { + auto cyclesArray = convertToCyclesArray<float2, GfVec2f>(value); + node->set(socket, cyclesArray); + break; + } + case SocketType::STRING_ARRAY: { + auto cyclesArray = convertToCyclesArray<ustring, void>(value); + node->set(socket, cyclesArray); + break; + } + case SocketType::TRANSFORM_ARRAY: { + auto cyclesArray = convertToCyclesArray<Transform, void>(value); + node->set(socket, cyclesArray); + break; + } + case SocketType::NODE_ARRAY: { + // TODO: renderIndex->GetRprim()->cycles_node ? + TF_WARN("Unimplemented conversion: SocketType::NODE_ARRAY"); + break; + } + } +} + +VtValue GetNodeValue(const Node *node, const SocketType &socket) +{ + switch (socket.type) { + default: + case SocketType::UNDEFINED: + TF_RUNTIME_ERROR("Unexpected conversion: SocketType::UNDEFINED"); + return VtValue(); + + case SocketType::BOOLEAN: + return convertFromCycles(node->get_bool(socket)); + case SocketType::FLOAT: + return convertFromCycles(node->get_float(socket)); + case SocketType::INT: + return convertFromCycles(node->get_int(socket)); + case SocketType::UINT: + return convertFromCycles(node->get_uint(socket)); + case SocketType::COLOR: + case SocketType::VECTOR: + case SocketType::POINT: + case SocketType::NORMAL: + return convertFromCycles(node->get_float3(socket)); + case SocketType::POINT2: + return convertFromCycles(node->get_float2(socket)); + case SocketType::CLOSURE: + return VtValue(); + case SocketType::STRING: + return convertFromCycles(node->get_string(socket)); + case SocketType::ENUM: + return convertFromCycles(node->get_int(socket)); + case SocketType::TRANSFORM: + return convertFromCycles(node->get_transform(socket)); + case SocketType::NODE: + TF_WARN("Unimplemented conversion: SocketType::NODE"); + return VtValue(); + + case SocketType::BOOLEAN_ARRAY: + return convertFromCyclesArray(node->get_bool_array(socket)); + case SocketType::FLOAT_ARRAY: + return convertFromCyclesArray(node->get_float_array(socket)); + case SocketType::INT_ARRAY: + return convertFromCyclesArray(node->get_int_array(socket)); + case SocketType::COLOR_ARRAY: + case SocketType::VECTOR_ARRAY: + case SocketType::POINT_ARRAY: + case SocketType::NORMAL_ARRAY: + return convertFromCyclesArray<float3, GfVec3f>(node->get_float3_array(socket)); + case SocketType::POINT2_ARRAY: + return convertFromCyclesArray<float2, GfVec2f>(node->get_float2_array(socket)); + case SocketType::STRING_ARRAY: + return convertFromCyclesArray<ustring, void>(node->get_string_array(socket)); + case SocketType::TRANSFORM_ARRAY: + return convertFromCyclesArray<Transform, void>(node->get_transform_array(socket)); + case SocketType::NODE_ARRAY: { + TF_WARN("Unimplemented conversion: SocketType::NODE_ARRAY"); + return VtValue(); + } + } +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/node_util.h b/intern/cycles/hydra/node_util.h new file mode 100644 index 00000000000..009a4a9eb38 --- /dev/null +++ b/intern/cycles/hydra/node_util.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "graph/node.h" +#include "hydra/config.h" + +#include <pxr/base/vt/value.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +void SetNodeValue(CCL_NS::Node *node, const CCL_NS::SocketType &socket, const VtValue &value); + +VtValue GetNodeValue(const CCL_NS::Node *node, const CCL_NS::SocketType &socket); + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/output_driver.cpp b/intern/cycles/hydra/output_driver.cpp new file mode 100644 index 00000000000..c5f64ac1c18 --- /dev/null +++ b/intern/cycles/hydra/output_driver.cpp @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/output_driver.h" +#include "hydra/render_buffer.h" +#include "hydra/session.h" + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +HdCyclesOutputDriver::HdCyclesOutputDriver(HdCyclesSession *renderParam) + : _renderParam(renderParam) +{ +} + +void HdCyclesOutputDriver::write_render_tile(const Tile &tile) +{ + update_render_tile(tile); + + // Update convergence state of all render buffers + for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) { + if (const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) { + renderBuffer->SetConverged(true); + } + } +} + +bool HdCyclesOutputDriver::update_render_tile(const Tile &tile) +{ + std::vector<float> pixels; + + for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) { + if (aovBinding == _renderParam->GetDisplayAovBinding()) { + continue; // Display AOV binding is already updated by Cycles display driver + } + + if (const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) { + const HdFormat format = renderBuffer->GetFormat(); + if (format == HdFormatInvalid) { + continue; // Skip invalid AOV bindings + } + + const size_t channels = HdGetComponentCount(format); + // Avoid extra copy by mapping render buffer directly when dimensions/format match the tile + if (tile.offset.x == 0 && tile.offset.y == 0 && tile.size.x == renderBuffer->GetWidth() && + tile.size.y == renderBuffer->GetHeight() && + (format >= HdFormatFloat32 && format <= HdFormatFloat32Vec4)) { + float *const data = static_cast<float *>(renderBuffer->Map()); + TF_VERIFY(tile.get_pass_pixels(aovBinding.aovName.GetString(), channels, data)); + renderBuffer->Unmap(); + } + else { + pixels.resize(channels * tile.size.x * tile.size.y); + if (tile.get_pass_pixels(aovBinding.aovName.GetString(), channels, pixels.data())) { + const bool isId = aovBinding.aovName == HdAovTokens->primId || + aovBinding.aovName == HdAovTokens->elementId || + aovBinding.aovName == HdAovTokens->instanceId; + + renderBuffer->WritePixels(pixels.data(), + GfVec2i(tile.offset.x, tile.offset.y), + GfVec2i(tile.size.x, tile.size.y), + channels, + isId); + } + else { + // Do not warn on missing elementId, which is a standard AOV but is not implememted + if (aovBinding.aovName != HdAovTokens->elementId) { + TF_RUNTIME_ERROR("Could not find pass for AOV '%s'", aovBinding.aovName.GetText()); + } + } + } + } + } + + return true; +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/output_driver.h b/intern/cycles/hydra/output_driver.h new file mode 100644 index 00000000000..f10adfccf43 --- /dev/null +++ b/intern/cycles/hydra/output_driver.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "session/output_driver.h" + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesOutputDriver final : public CCL_NS::OutputDriver { + public: + HdCyclesOutputDriver(HdCyclesSession *renderParam); + + private: + void write_render_tile(const Tile &tile) override; + bool update_render_tile(const Tile &tile) override; + + HdCyclesSession *const _renderParam; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/plugInfo.json b/intern/cycles/hydra/plugInfo.json new file mode 100644 index 00000000000..2e20f3d66a7 --- /dev/null +++ b/intern/cycles/hydra/plugInfo.json @@ -0,0 +1,3 @@ +{ + "Includes": [ "*/resources/" ] +} diff --git a/intern/cycles/hydra/plugin.cpp b/intern/cycles/hydra/plugin.cpp new file mode 100644 index 00000000000..8caca3068df --- /dev/null +++ b/intern/cycles/hydra/plugin.cpp @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/plugin.h" +#include "hydra/render_delegate.h" +#include "util/log.h" +#include "util/path.h" + +#include <pxr/base/arch/filesystem.h> +#include <pxr/base/plug/plugin.h> +#include <pxr/base/plug/thisPlugin.h> +#include <pxr/base/tf/envSetting.h> +#include <pxr/imaging/hd/rendererPluginRegistry.h> + +PXR_NAMESPACE_OPEN_SCOPE + +#ifdef WITH_CYCLES_LOGGING +TF_DEFINE_ENV_SETTING(CYCLES_LOGGING, false, "Enable Cycles logging") +TF_DEFINE_ENV_SETTING(CYCLES_LOGGING_SEVERITY, 1, "Cycles logging verbosity") +#endif + +HdCyclesPlugin::HdCyclesPlugin() +{ + const PlugPluginPtr plugin = PLUG_THIS_PLUGIN; + // Initialize Cycles paths relative to the plugin resource path + std::string rootPath = PXR_NS::ArchAbsPath(plugin->GetResourcePath()); + CCL_NS::path_init(std::move(rootPath)); + +#ifdef WITH_CYCLES_LOGGING + if (TfGetEnvSetting(CYCLES_LOGGING)) { + CCL_NS::util_logging_start(); + CCL_NS::util_logging_verbosity_set(TfGetEnvSetting(CYCLES_LOGGING_SEVERITY)); + } +#endif +} + +HdCyclesPlugin::~HdCyclesPlugin() +{ +} + +bool HdCyclesPlugin::IsSupported() const +{ + return true; +} + +HdRenderDelegate *HdCyclesPlugin::CreateRenderDelegate() +{ + return CreateRenderDelegate({}); +} + +HdRenderDelegate *HdCyclesPlugin::CreateRenderDelegate(const HdRenderSettingsMap &settingsMap) +{ + return new HD_CYCLES_NS::HdCyclesDelegate(settingsMap); +} + +void HdCyclesPlugin::DeleteRenderDelegate(HdRenderDelegate *renderDelegate) +{ + delete renderDelegate; +} + +// USD's type system accounts for namespace, so we'd have to register our name as +// HdCycles::HdCyclesPlugin in plugInfo.json, which isn't all that bad for JSON, +// but those colons may cause issues for any USD specific tooling. So just put our +// plugin class in the pxr namespace (which USD's type system will elide). +TF_REGISTRY_FUNCTION(TfType) +{ + HdRendererPluginRegistry::Define<PXR_NS::HdCyclesPlugin>(); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/plugin.h b/intern/cycles/hydra/plugin.h new file mode 100644 index 00000000000..0a76c827fda --- /dev/null +++ b/intern/cycles/hydra/plugin.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include <pxr/imaging/hd/rendererPlugin.h> + +PXR_NAMESPACE_OPEN_SCOPE + +class HdCyclesPlugin final : public PXR_NS::HdRendererPlugin { + public: + HdCyclesPlugin(); + ~HdCyclesPlugin() override; + + bool IsSupported() const override; + + PXR_NS::HdRenderDelegate *CreateRenderDelegate() override; + PXR_NS::HdRenderDelegate *CreateRenderDelegate(const PXR_NS::HdRenderSettingsMap &) override; + + void DeleteRenderDelegate(PXR_NS::HdRenderDelegate *) override; +}; + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/pointcloud.cpp b/intern/cycles/hydra/pointcloud.cpp new file mode 100644 index 00000000000..8d43fd8bfcd --- /dev/null +++ b/intern/cycles/hydra/pointcloud.cpp @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/pointcloud.h" +#include "hydra/geometry.inl" +#include "scene/pointcloud.h" + +#include <pxr/imaging/hd/extComputationUtils.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +HdCyclesPoints::HdCyclesPoints(const SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const SdfPath &instancerId +#endif + ) + : HdCyclesGeometry(rprimId +#if PXR_VERSION < 2102 + , + instancerId +#endif + ) +{ +} + +HdCyclesPoints::~HdCyclesPoints() +{ +} + +HdDirtyBits HdCyclesPoints::GetInitialDirtyBitsMask() const +{ + HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask(); + bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths | + HdChangeTracker::DirtyPrimvar; + return bits; +} + +HdDirtyBits HdCyclesPoints::_PropagateDirtyBits(HdDirtyBits bits) const +{ + // Points and widths always have to be updated together + if (bits & (HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths)) { + bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths; + } + + return bits; +} + +void HdCyclesPoints::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild) +{ + if (dirtyBits & (HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths)) { + const size_t numPoints = _geom->num_points(); + + PopulatePoints(sceneDelegate); + PopulateWidths(sceneDelegate); + + rebuild = _geom->num_points() != numPoints; + + array<int> shaders; + shaders.reserve(_geom->num_points()); + for (size_t i = 0; i < _geom->num_points(); ++i) { + shaders.push_back_reserved(0); + } + + _geom->set_shader(shaders); + } + + if (dirtyBits & HdChangeTracker::DirtyPrimvar) { + PopulatePrimvars(sceneDelegate); + } +} + +void HdCyclesPoints::PopulatePoints(HdSceneDelegate *sceneDelegate) +{ + VtValue value; + + for (const HdExtComputationPrimvarDescriptor &desc : + sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), HdInterpolationVertex)) { + if (desc.name == HdTokens->points) { + auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate); + const auto valueStoreIt = valueStore.find(desc.name); + if (valueStoreIt != valueStore.end()) { + value = std::move(valueStoreIt->second); + } + break; + } + } + + if (value.IsEmpty()) { + value = GetPrimvar(sceneDelegate, HdTokens->points); + } + + if (!value.IsHolding<VtVec3fArray>()) { + TF_WARN("Invalid points data for %s", GetId().GetText()); + return; + } + + const auto &points = value.UncheckedGet<VtVec3fArray>(); + + array<float3> pointsDataCycles; + pointsDataCycles.reserve(points.size()); + + for (const GfVec3f &point : points) { + pointsDataCycles.push_back_reserved(make_float3(point[0], point[1], point[2])); + } + + _geom->set_points(pointsDataCycles); +} + +void HdCyclesPoints::PopulateWidths(HdSceneDelegate *sceneDelegate) +{ + VtValue value = GetPrimvar(sceneDelegate, HdTokens->widths); + const HdInterpolation interpolation = GetPrimvarInterpolation(sceneDelegate, HdTokens->widths); + + if (!value.IsHolding<VtFloatArray>()) { + TF_WARN("Invalid widths data for %s", GetId().GetText()); + return; + } + + const auto &widths = value.UncheckedGet<VtFloatArray>(); + + array<float> radiusDataCycles; + radiusDataCycles.reserve(_geom->num_points()); + + if (interpolation == HdInterpolationConstant) { + TF_VERIFY(widths.size() == 1); + + const float constantRadius = widths[0] * 0.5f; + + for (size_t i = 0; i < _geom->num_points(); ++i) { + radiusDataCycles.push_back_reserved(constantRadius); + } + } + else if (interpolation == HdInterpolationVertex) { + TF_VERIFY(widths.size() == _geom->num_points()); + + for (size_t i = 0; i < _geom->num_points(); ++i) { + radiusDataCycles.push_back_reserved(widths[i] * 0.5f); + } + } + + _geom->set_radius(radiusDataCycles); +} + +void HdCyclesPoints::PopulatePrimvars(HdSceneDelegate *sceneDelegate) +{ + Scene *const scene = (Scene *)_geom->get_owner(); + + const std::pair<HdInterpolation, AttributeElement> interpolations[] = { + std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_VERTEX), + std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT), + }; + + for (const auto &interpolation : interpolations) { + for (const HdPrimvarDescriptor &desc : + GetPrimvarDescriptors(sceneDelegate, interpolation.first)) { + // Skip special primvars that are handled separately + if (desc.name == HdTokens->points || desc.name == HdTokens->widths) { + continue; + } + + VtValue value = GetPrimvar(sceneDelegate, desc.name); + if (value.IsEmpty()) { + continue; + } + + const ustring name(desc.name.GetString()); + + AttributeStandard std = ATTR_STD_NONE; + if (desc.role == HdPrimvarRoleTokens->textureCoordinate) { + std = ATTR_STD_UV; + } + else if (interpolation.first == HdInterpolationVertex) { + if (desc.name == HdTokens->displayColor || desc.role == HdPrimvarRoleTokens->color) { + std = ATTR_STD_VERTEX_COLOR; + } + else if (desc.name == HdTokens->normals) { + std = ATTR_STD_VERTEX_NORMAL; + } + } + else if (desc.name == HdTokens->displayColor && + interpolation.first == HdInterpolationConstant) { + if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) { + const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0]; + _instances[0]->set_color(make_float3(color[0], color[1], color[2])); + } + } + + // Skip attributes that are not needed + if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) || + _geom->need_attribute(scene, name)) { + ApplyPrimvars(_geom->attributes, name, value, interpolation.second, std); + } + } + } +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/pointcloud.h b/intern/cycles/hydra/pointcloud.h new file mode 100644 index 00000000000..a014a389dcc --- /dev/null +++ b/intern/cycles/hydra/pointcloud.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "hydra/geometry.h" + +#include <pxr/imaging/hd/points.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesPoints final : public HdCyclesGeometry<PXR_NS::HdPoints, CCL_NS::PointCloud> { + public: + HdCyclesPoints( + const PXR_NS::SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const PXR_NS::SdfPath &instancerId = {} +#endif + ); + ~HdCyclesPoints() override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + private: + PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override; + + void Populate(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdDirtyBits dirtyBits, + bool &rebuild) override; + + void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate); + void PopulateWidths(PXR_NS::HdSceneDelegate *sceneDelegate); + + void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate); +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/render_buffer.cpp b/intern/cycles/hydra/render_buffer.cpp new file mode 100644 index 00000000000..4867def0624 --- /dev/null +++ b/intern/cycles/hydra/render_buffer.cpp @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/render_buffer.h" +#include "hydra/session.h" +#include "util/half.h" + +#include <pxr/base/gf/vec3i.h> +#include <pxr/base/gf/vec4f.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +HdCyclesRenderBuffer::HdCyclesRenderBuffer(const SdfPath &bprimId) : HdRenderBuffer(bprimId) +{ +} + +HdCyclesRenderBuffer::~HdCyclesRenderBuffer() +{ +} + +void HdCyclesRenderBuffer::Finalize(HdRenderParam *renderParam) +{ + // Remove this render buffer from AOV bindings + // This ensures that 'OutputDriver' does not attempt to write to it anymore + static_cast<HdCyclesSession *>(renderParam)->RemoveAovBinding(this); + + HdRenderBuffer::Finalize(renderParam); +} + +bool HdCyclesRenderBuffer::Allocate(const GfVec3i &dimensions, HdFormat format, bool multiSampled) +{ + if (dimensions[2] != 1) { + TF_RUNTIME_ERROR("HdCyclesRenderBuffer::Allocate called with dimensions that are not 2D."); + return false; + } + + const size_t oldSize = _data.size(); + const size_t newSize = dimensions[0] * dimensions[1] * HdDataSizeOfFormat(format); + if (oldSize == newSize) { + return true; + } + + if (IsMapped()) { + TF_RUNTIME_ERROR("HdCyclesRenderBuffer::Allocate called while buffer is mapped."); + return false; + } + + _width = dimensions[0]; + _height = dimensions[1]; + _format = format; + + _data.resize(newSize); + + return true; +} + +void HdCyclesRenderBuffer::_Deallocate() +{ + _width = 0u; + _height = 0u; + _format = HdFormatInvalid; + + _data.clear(); + _data.shrink_to_fit(); + + _resource = VtValue(); +} + +void *HdCyclesRenderBuffer::Map() +{ + // Mapping is not implemented when a resource is set + if (!_resource.IsEmpty()) { + return nullptr; + } + + ++_mapped; + + return _data.data(); +} + +void HdCyclesRenderBuffer::Unmap() +{ + --_mapped; +} + +bool HdCyclesRenderBuffer::IsMapped() const +{ + return _mapped != 0; +} + +void HdCyclesRenderBuffer::Resolve() +{ +} + +bool HdCyclesRenderBuffer::IsConverged() const +{ + return _converged; +} + +void HdCyclesRenderBuffer::SetConverged(bool converged) +{ + _converged = converged; +} + +VtValue HdCyclesRenderBuffer::GetResource(bool multiSampled) const +{ + TF_UNUSED(multiSampled); + + return _resource; +} + +void HdCyclesRenderBuffer::SetResource(const VtValue &resource) +{ + _resource = resource; +} + +namespace { + +struct SimpleConversion { + static float convert(float value) + { + return value; + } +}; +struct IdConversion { + static int32_t convert(float value) + { + return static_cast<int32_t>(value) - 1; + } +}; +struct UInt8Conversion { + static uint8_t convert(float value) + { + return static_cast<uint8_t>(value * 255.f); + } +}; +struct SInt8Conversion { + static int8_t convert(float value) + { + return static_cast<int8_t>(value * 127.f); + } +}; +struct HalfConversion { + static half convert(float value) + { + return float_to_half_image(value); + } +}; + +template<typename SrcT, typename DstT, typename Convertor = SimpleConversion> +void writePixels(const SrcT *srcPtr, + const GfVec2i &srcSize, + int srcChannelCount, + DstT *dstPtr, + const GfVec2i &dstSize, + int dstChannelCount, + const Convertor &convertor = {}) +{ + const auto writeSize = GfVec2i(GfMin(srcSize[0], dstSize[0]), GfMin(srcSize[1], dstSize[1])); + const auto writeChannelCount = GfMin(srcChannelCount, dstChannelCount); + + for (int y = 0; y < writeSize[1]; ++y) { + for (int x = 0; x < writeSize[0]; ++x) { + for (int c = 0; c < writeChannelCount; ++c) { + dstPtr[x * dstChannelCount + c] = convertor.convert(srcPtr[x * srcChannelCount + c]); + } + } + srcPtr += srcSize[0] * srcChannelCount; + dstPtr += dstSize[0] * dstChannelCount; + } +} + +} // namespace + +void HdCyclesRenderBuffer::WritePixels(const float *srcPixels, + const PXR_NS::GfVec2i &srcOffset, + const GfVec2i &srcDims, + int srcChannels, + bool isId) +{ + uint8_t *dstPixels = _data.data(); + + const size_t formatSize = HdDataSizeOfFormat(_format); + dstPixels += srcOffset[1] * (formatSize * _width) + srcOffset[0] * formatSize; + + switch (_format) { + case HdFormatUNorm8: + case HdFormatUNorm8Vec2: + case HdFormatUNorm8Vec3: + case HdFormatUNorm8Vec4: + writePixels(srcPixels, + srcDims, + srcChannels, + dstPixels, + GfVec2i(_width, _height), + 1 + (_format - HdFormatUNorm8), + UInt8Conversion()); + break; + + case HdFormatSNorm8: + case HdFormatSNorm8Vec2: + case HdFormatSNorm8Vec3: + case HdFormatSNorm8Vec4: + writePixels(srcPixels, + srcDims, + srcChannels, + dstPixels, + GfVec2i(_width, _height), + 1 + (_format - HdFormatSNorm8), + SInt8Conversion()); + break; + + case HdFormatFloat16: + case HdFormatFloat16Vec2: + case HdFormatFloat16Vec3: + case HdFormatFloat16Vec4: + writePixels(srcPixels, + srcDims, + srcChannels, + reinterpret_cast<half *>(dstPixels), + GfVec2i(_width, _height), + 1 + (_format - HdFormatFloat16), + HalfConversion()); + break; + + case HdFormatFloat32: + case HdFormatFloat32Vec2: + case HdFormatFloat32Vec3: + case HdFormatFloat32Vec4: + writePixels(srcPixels, + srcDims, + srcChannels, + reinterpret_cast<float *>(dstPixels), + GfVec2i(_width, _height), + 1 + (_format - HdFormatFloat32)); + break; + + case HdFormatInt32: + // Special case for ID AOVs (see 'HdCyclesMesh::Sync') + if (isId) { + writePixels(srcPixels, + srcDims, + srcChannels, + reinterpret_cast<int *>(dstPixels), + GfVec2i(_width, _height), + 1, + IdConversion()); + } + else { + writePixels(srcPixels, + srcDims, + srcChannels, + reinterpret_cast<int *>(dstPixels), + GfVec2i(_width, _height), + 1); + } + break; + case HdFormatInt32Vec2: + case HdFormatInt32Vec3: + case HdFormatInt32Vec4: + writePixels(srcPixels, + srcDims, + srcChannels, + reinterpret_cast<int *>(dstPixels), + GfVec2i(_width, _height), + 1 + (_format - HdFormatInt32)); + break; + + default: + TF_RUNTIME_ERROR("HdCyclesRenderBuffer::WritePixels called with unsupported format."); + break; + } +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/render_buffer.h b/intern/cycles/hydra/render_buffer.h new file mode 100644 index 00000000000..8eb874f0068 --- /dev/null +++ b/intern/cycles/hydra/render_buffer.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/imaging/hd/renderBuffer.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesRenderBuffer final : public PXR_NS::HdRenderBuffer { + public: + HdCyclesRenderBuffer(const PXR_NS::SdfPath &bprimId); + ~HdCyclesRenderBuffer() override; + + void Finalize(PXR_NS::HdRenderParam *renderParam) override; + + bool Allocate(const PXR_NS::GfVec3i &dimensions, + PXR_NS::HdFormat format, + bool multiSampled) override; + + unsigned int GetWidth() const override + { + return _width; + } + + unsigned int GetHeight() const override + { + return _height; + } + + unsigned int GetDepth() const override + { + return 1u; + } + + PXR_NS::HdFormat GetFormat() const override + { + return _format; + } + + bool IsMultiSampled() const override + { + return false; + } + + void *Map() override; + + void Unmap() override; + + bool IsMapped() const override; + + void Resolve() override; + + bool IsConverged() const override; + + void SetConverged(bool converged); + + PXR_NS::VtValue GetResource(bool multiSampled = false) const override; + + void SetResource(const PXR_NS::VtValue &resource); + + void WritePixels(const float *pixels, + const PXR_NS::GfVec2i &offset, + const PXR_NS::GfVec2i &dims, + int channels, + bool isId = false); + + private: + void _Deallocate() override; + + unsigned int _width = 0u; + unsigned int _height = 0u; + PXR_NS::HdFormat _format = PXR_NS::HdFormatInvalid; + + std::vector<uint8_t> _data; + PXR_NS::VtValue _resource; + + std::atomic_int _mapped = 0; + std::atomic_bool _converged = false; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/render_delegate.cpp b/intern/cycles/hydra/render_delegate.cpp new file mode 100644 index 00000000000..748b6a66e1e --- /dev/null +++ b/intern/cycles/hydra/render_delegate.cpp @@ -0,0 +1,514 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/render_delegate.h" +#include "hydra/camera.h" +#include "hydra/curves.h" +#include "hydra/field.h" +#include "hydra/instancer.h" +#include "hydra/light.h" +#include "hydra/material.h" +#include "hydra/mesh.h" +#include "hydra/node_util.h" +#include "hydra/pointcloud.h" +#include "hydra/render_buffer.h" +#include "hydra/render_pass.h" +#include "hydra/session.h" +#include "hydra/volume.h" +#include "scene/integrator.h" +#include "scene/scene.h" +#include "session/session.h" + +#include <pxr/base/tf/getenv.h> +#include <pxr/imaging/hd/extComputation.h> +#include <pxr/imaging/hgi/tokens.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +// clang-format off +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (cycles) + (openvdbAsset) +); + +TF_DEFINE_PRIVATE_TOKENS(HdCyclesRenderSettingsTokens, + ((device, "cycles:device")) + ((threads, "cycles:threads")) + ((time_limit, "cycles:time_limit")) + ((samples, "cycles:samples")) + ((sample_offset, "cycles:sample_offset")) +); +// clang-format on + +namespace { + +const TfTokenVector kSupportedRPrimTypes = { + HdPrimTypeTokens->basisCurves, + HdPrimTypeTokens->mesh, + HdPrimTypeTokens->points, +#ifdef WITH_OPENVDB + HdPrimTypeTokens->volume, +#endif +}; + +const TfTokenVector kSupportedSPrimTypes = { + HdPrimTypeTokens->camera, + HdPrimTypeTokens->material, + HdPrimTypeTokens->diskLight, + HdPrimTypeTokens->distantLight, + HdPrimTypeTokens->domeLight, + HdPrimTypeTokens->rectLight, + HdPrimTypeTokens->sphereLight, + HdPrimTypeTokens->extComputation, +}; + +const TfTokenVector kSupportedBPrimTypes = { + HdPrimTypeTokens->renderBuffer, +#ifdef WITH_OPENVDB + _tokens->openvdbAsset, +#endif +}; + +SessionParams GetSessionParams(const HdRenderSettingsMap &settings) +{ + SessionParams params; + params.threads = 0; + params.background = false; + params.use_resolution_divider = false; + + HdRenderSettingsMap::const_iterator it; + + // Pull all setting that contribute to device creation first + it = settings.find(HdCyclesRenderSettingsTokens->threads); + if (it != settings.end()) { + params.threads = VtValue::Cast<int>(it->second).GetWithDefault(params.threads); + } + + // Get the Cycles device from settings or environment, falling back to CPU + std::string deviceType = Device::string_from_type(DEVICE_CPU); + it = settings.find(HdCyclesRenderSettingsTokens->device); + if (it != settings.end()) { + deviceType = VtValue::Cast<std::string>(it->second).GetWithDefault(deviceType); + } + else { + const std::string deviceTypeEnv = TfGetenv("CYCLES_DEVICE"); + if (!deviceTypeEnv.empty()) { + deviceType = deviceTypeEnv; + } + } + + // Move to all uppercase for Device::type_from_string + std::transform(deviceType.begin(), deviceType.end(), deviceType.begin(), ::toupper); + + vector<DeviceInfo> devices = Device::available_devices( + DEVICE_MASK(Device::type_from_string(deviceType.c_str()))); + if (devices.empty()) { + devices = Device::available_devices(DEVICE_MASK_CPU); + if (!devices.empty()) { + params.device = devices.front(); + } + } + else { + params.device = Device::get_multi_device(devices, params.threads, params.background); + } + + return params; +} + +} // namespace + +HdCyclesDelegate::HdCyclesDelegate(const HdRenderSettingsMap &settingsMap, Session *session_) + : HdRenderDelegate() +{ + _renderParam = session_ ? std::make_unique<HdCyclesSession>(session_) : + std::make_unique<HdCyclesSession>(GetSessionParams(settingsMap)); + + // If the delegate owns the session, pull any remaining settings + if (!session_) { + for (const auto &setting : settingsMap) { + // Skip over the settings known to be used for initialization only + if (setting.first == HdCyclesRenderSettingsTokens->device || + setting.first == HdCyclesRenderSettingsTokens->threads) { + continue; + } + + SetRenderSetting(setting.first, setting.second); + } + } +} + +HdCyclesDelegate::~HdCyclesDelegate() +{ +} + +void HdCyclesDelegate::SetDrivers(const HdDriverVector &drivers) +{ + for (HdDriver *hdDriver : drivers) { + if (hdDriver->name == HgiTokens->renderDriver && hdDriver->driver.IsHolding<Hgi *>()) { + _hgi = hdDriver->driver.UncheckedGet<Hgi *>(); + break; + } + } +} + +bool HdCyclesDelegate::IsDisplaySupported() const +{ +#ifdef _WIN32 + return _hgi && _hgi->GetAPIName() == HgiTokens->OpenGL; +#else + return false; +#endif +} + +const TfTokenVector &HdCyclesDelegate::GetSupportedRprimTypes() const +{ + return kSupportedRPrimTypes; +} + +const TfTokenVector &HdCyclesDelegate::GetSupportedSprimTypes() const +{ + return kSupportedSPrimTypes; +} + +const TfTokenVector &HdCyclesDelegate::GetSupportedBprimTypes() const +{ + return kSupportedBPrimTypes; +} + +HdRenderParam *HdCyclesDelegate::GetRenderParam() const +{ + return _renderParam.get(); +} + +HdResourceRegistrySharedPtr HdCyclesDelegate::GetResourceRegistry() const +{ + return HdResourceRegistrySharedPtr(); +} + +bool HdCyclesDelegate::IsPauseSupported() const +{ + return true; +} + +bool HdCyclesDelegate::Pause() +{ + _renderParam->session->set_pause(true); + return true; +} + +bool HdCyclesDelegate::Resume() +{ + _renderParam->session->set_pause(false); + return true; +} + +HdRenderPassSharedPtr HdCyclesDelegate::CreateRenderPass(HdRenderIndex *index, + const HdRprimCollection &collection) +{ + return HdRenderPassSharedPtr(new HdCyclesRenderPass(index, collection, _renderParam.get())); +} + +HdInstancer *HdCyclesDelegate::CreateInstancer(HdSceneDelegate *delegate, + const SdfPath &instancerId +#if PXR_VERSION < 2102 + , + const SdfPath &parentId +#endif +) +{ + return new HdCyclesInstancer(delegate, + instancerId +#if PXR_VERSION < 2102 + , + parentId +#endif + ); +} + +void HdCyclesDelegate::DestroyInstancer(HdInstancer *instancer) +{ + delete instancer; +} + +HdRprim *HdCyclesDelegate::CreateRprim(const TfToken &typeId, + const SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const SdfPath &instancerId +#endif +) +{ + if (typeId == HdPrimTypeTokens->mesh) { + return new HdCyclesMesh(rprimId +#if PXR_VERSION < 2102 + , + instancerId +#endif + ); + } + if (typeId == HdPrimTypeTokens->basisCurves) { + return new HdCyclesCurves(rprimId +#if PXR_VERSION < 2102 + , + instancerId +#endif + ); + } + if (typeId == HdPrimTypeTokens->points) { + return new HdCyclesPoints(rprimId +#if PXR_VERSION < 2102 + , + instancerId +#endif + ); + } +#ifdef WITH_OPENVDB + if (typeId == HdPrimTypeTokens->volume) { + return new HdCyclesVolume(rprimId +# if PXR_VERSION < 2102 + , + instancerId +# endif + ); + } +#endif + + TF_CODING_ERROR("Unknown Rprim type %s", typeId.GetText()); + return nullptr; +} + +void HdCyclesDelegate::DestroyRprim(HdRprim *rPrim) +{ + delete rPrim; +} + +HdSprim *HdCyclesDelegate::CreateSprim(const TfToken &typeId, const SdfPath &sprimId) +{ + if (typeId == HdPrimTypeTokens->camera) { + return new HdCyclesCamera(sprimId); + } + if (typeId == HdPrimTypeTokens->material) { + return new HdCyclesMaterial(sprimId); + } + if (typeId == HdPrimTypeTokens->diskLight || typeId == HdPrimTypeTokens->distantLight || + typeId == HdPrimTypeTokens->domeLight || typeId == HdPrimTypeTokens->rectLight || + typeId == HdPrimTypeTokens->sphereLight) { + return new HdCyclesLight(sprimId, typeId); + } + if (typeId == HdPrimTypeTokens->extComputation) { + return new HdExtComputation(sprimId); + } + + TF_CODING_ERROR("Unknown Sprim type %s", typeId.GetText()); + return nullptr; +} + +HdSprim *HdCyclesDelegate::CreateFallbackSprim(const TfToken &typeId) +{ + return CreateSprim(typeId, SdfPath::EmptyPath()); +} + +void HdCyclesDelegate::DestroySprim(HdSprim *sPrim) +{ + delete sPrim; +} + +HdBprim *HdCyclesDelegate::CreateBprim(const TfToken &typeId, const SdfPath &bprimId) +{ + if (typeId == HdPrimTypeTokens->renderBuffer) { + return new HdCyclesRenderBuffer(bprimId); + } +#ifdef WITH_OPENVDB + if (typeId == _tokens->openvdbAsset) { + return new HdCyclesField(bprimId, typeId); + } +#endif + + TF_RUNTIME_ERROR("Unknown Bprim type %s", typeId.GetText()); + return nullptr; +} + +HdBprim *HdCyclesDelegate::CreateFallbackBprim(const TfToken &typeId) +{ + return CreateBprim(typeId, SdfPath::EmptyPath()); +} + +void HdCyclesDelegate::DestroyBprim(HdBprim *bPrim) +{ + delete bPrim; +} + +void HdCyclesDelegate::CommitResources(HdChangeTracker *tracker) +{ + TF_UNUSED(tracker); + + const SceneLock lock(_renderParam.get()); + + _renderParam->UpdateScene(); +} + +TfToken HdCyclesDelegate::GetMaterialBindingPurpose() const +{ + return HdTokens->full; +} + +#if HD_API_VERSION < 41 +TfToken HdCyclesDelegate::GetMaterialNetworkSelector() const +{ + return _tokens->cycles; +} +#else +TfTokenVector HdCyclesDelegate::GetMaterialRenderContexts() const +{ + return {_tokens->cycles}; +} +#endif + +VtDictionary HdCyclesDelegate::GetRenderStats() const +{ + const Stats &stats = _renderParam->session->stats; + const Progress &progress = _renderParam->session->progress; + + double totalTime, renderTime; + progress.get_time(totalTime, renderTime); + double fractionDone = progress.get_progress(); + + std::string status, substatus; + progress.get_status(status, substatus); + if (!substatus.empty()) { + status += " | " + substatus; + } + + return {{"rendererName", VtValue("Cycles")}, + {"rendererVersion", VtValue(GfVec3i(0, 0, 0))}, + {"percentDone", VtValue(floor_to_int(fractionDone * 100))}, + {"fractionDone", VtValue(fractionDone)}, + {"loadClockTime", VtValue(totalTime - renderTime)}, + {"peakMemory", VtValue(stats.mem_peak)}, + {"totalClockTime", VtValue(totalTime)}, + {"totalMemory", VtValue(stats.mem_used)}, + {"renderProgressAnnotation", VtValue(status)}}; +} + +HdAovDescriptor HdCyclesDelegate::GetDefaultAovDescriptor(const TfToken &name) const +{ + if (name == HdAovTokens->color) { + HdFormat colorFormat = HdFormatFloat32Vec4; + if (IsDisplaySupported()) { + // Can use Cycles 'DisplayDriver' in OpenGL, but it only supports 'half4' format + colorFormat = HdFormatFloat16Vec4; + } + + return HdAovDescriptor(colorFormat, false, VtValue(GfVec4f(0.0f))); + } + if (name == HdAovTokens->depth) { + return HdAovDescriptor(HdFormatFloat32, false, VtValue(1.0f)); + } + if (name == HdAovTokens->normal) { + return HdAovDescriptor(HdFormatFloat32Vec3, false, VtValue(GfVec3f(0.0f))); + } + if (name == HdAovTokens->primId || name == HdAovTokens->instanceId || + name == HdAovTokens->elementId) { + return HdAovDescriptor(HdFormatInt32, false, VtValue(-1)); + } + + return HdAovDescriptor(); +} + +HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() const +{ + Scene *const scene = _renderParam->session->scene; + + HdRenderSettingDescriptorList descriptors; + + descriptors.push_back({ + "Time Limit", + HdCyclesRenderSettingsTokens->time_limit, + VtValue(0.0), + }); + descriptors.push_back({ + "Sample Count", + HdCyclesRenderSettingsTokens->samples, + VtValue(1024), + }); + descriptors.push_back({ + "Sample Offset", + HdCyclesRenderSettingsTokens->sample_offset, + VtValue(0), + }); + + for (const SocketType &socket : scene->integrator->type->inputs) { + descriptors.push_back({socket.ui_name.string(), + TfToken("cycles:integrator:" + socket.name.string()), + GetNodeValue(scene->integrator, socket)}); + } + + return descriptors; +} + +void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS::VtValue &value) +{ + Scene *const scene = _renderParam->session->scene; + Session *const session = _renderParam->session; + + if (key == HdCyclesRenderSettingsTokens->time_limit) { + session->set_time_limit( + VtValue::Cast<double>(value).GetWithDefault(session->params.time_limit)); + } + else if (key == HdCyclesRenderSettingsTokens->samples) { + int samples = VtValue::Cast<int>(value).GetWithDefault(session->params.samples); + samples = std::min(std::max(1, samples), Integrator::MAX_SAMPLES); + session->set_samples(samples); + } + else if (key == HdCyclesRenderSettingsTokens->sample_offset) { + session->params.sample_offset = VtValue::Cast<int>(value).GetWithDefault( + session->params.sample_offset); + ++_settingsVersion; + } + else { + const std::string &keyString = key.GetString(); + if (keyString.rfind("cycles:integrator:", 0) == 0) { + ustring socketName(keyString, sizeof("cycles:integrator:") - 1); + if (const SocketType *socket = scene->integrator->type->find_input(socketName)) { + SetNodeValue(scene->integrator, *socket, value); + ++_settingsVersion; + } + } + } +} + +VtValue HdCyclesDelegate::GetRenderSetting(const TfToken &key) const +{ + Scene *const scene = _renderParam->session->scene; + Session *const session = _renderParam->session; + + if (key == HdCyclesRenderSettingsTokens->device) { + return VtValue(TfToken(Device::string_from_type(session->params.device.type))); + } + else if (key == HdCyclesRenderSettingsTokens->threads) { + return VtValue(session->params.threads); + } + else if (key == HdCyclesRenderSettingsTokens->time_limit) { + return VtValue(session->params.time_limit); + } + else if (key == HdCyclesRenderSettingsTokens->samples) { + return VtValue(session->params.samples); + } + else if (key == HdCyclesRenderSettingsTokens->sample_offset) { + return VtValue(session->params.sample_offset); + } + else { + const std::string &keyString = key.GetString(); + if (keyString.rfind("cycles:integrator:", 0) == 0) { + ustring socketName(keyString, sizeof("cycles:integrator:") - 1); + if (const SocketType *socket = scene->integrator->type->find_input(socketName)) { + return GetNodeValue(scene->integrator, *socket); + } + } + } + + return VtValue(); +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/render_delegate.h b/intern/cycles/hydra/render_delegate.h new file mode 100644 index 00000000000..9c15c8d5281 --- /dev/null +++ b/intern/cycles/hydra/render_delegate.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/imaging/hd/renderDelegate.h> +#include <pxr/imaging/hgi/hgi.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesDelegate final : public PXR_NS::HdRenderDelegate { + public: + HdCyclesDelegate(const PXR_NS::HdRenderSettingsMap &settingsMap, + CCL_NS::Session *session_ = nullptr); + ~HdCyclesDelegate() override; + + void SetDrivers(const PXR_NS::HdDriverVector &drivers) override; + + bool IsDisplaySupported() const; + + PXR_NS::Hgi *GetHgi() const + { + return _hgi; + } + + const PXR_NS::TfTokenVector &GetSupportedRprimTypes() const override; + const PXR_NS::TfTokenVector &GetSupportedSprimTypes() const override; + const PXR_NS::TfTokenVector &GetSupportedBprimTypes() const override; + + PXR_NS::HdRenderParam *GetRenderParam() const override; + + PXR_NS::HdResourceRegistrySharedPtr GetResourceRegistry() const override; + + PXR_NS::HdRenderSettingDescriptorList GetRenderSettingDescriptors() const override; + + bool IsPauseSupported() const override; + + bool Pause() override; + bool Resume() override; + + PXR_NS::HdRenderPassSharedPtr CreateRenderPass( + PXR_NS::HdRenderIndex *index, const PXR_NS::HdRprimCollection &collection) override; + + PXR_NS::HdInstancer *CreateInstancer(PXR_NS::HdSceneDelegate *delegate, + const PXR_NS::SdfPath &id +#if PXR_VERSION < 2102 + , + const PXR_NS::SdfPath &instancerId +#endif + ) override; + void DestroyInstancer(PXR_NS::HdInstancer *instancer) override; + + PXR_NS::HdRprim *CreateRprim(const PXR_NS::TfToken &typeId, + const PXR_NS::SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const PXR_NS::SdfPath &instancerId +#endif + ) override; + void DestroyRprim(PXR_NS::HdRprim *rPrim) override; + + PXR_NS::HdSprim *CreateSprim(const PXR_NS::TfToken &typeId, + const PXR_NS::SdfPath &sprimId) override; + PXR_NS::HdSprim *CreateFallbackSprim(const PXR_NS::TfToken &typeId) override; + void DestroySprim(PXR_NS::HdSprim *sPrim) override; + + PXR_NS::HdBprim *CreateBprim(const PXR_NS::TfToken &typeId, + const PXR_NS::SdfPath &bprimId) override; + PXR_NS::HdBprim *CreateFallbackBprim(const PXR_NS::TfToken &typeId) override; + void DestroyBprim(PXR_NS::HdBprim *bPrim) override; + + void CommitResources(PXR_NS::HdChangeTracker *tracker) override; + + PXR_NS::TfToken GetMaterialBindingPurpose() const override; + +#if HD_API_VERSION < 41 + PXR_NS::TfToken GetMaterialNetworkSelector() const override; +#else + PXR_NS::TfTokenVector GetMaterialRenderContexts() const override; +#endif + + PXR_NS::VtDictionary GetRenderStats() const override; + + PXR_NS::HdAovDescriptor GetDefaultAovDescriptor(const PXR_NS::TfToken &name) const override; + + void SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS::VtValue &value) override; + + PXR_NS::VtValue GetRenderSetting(const PXR_NS::TfToken &key) const override; + + private: + PXR_NS::Hgi *_hgi = nullptr; + std::unique_ptr<HdCyclesSession> _renderParam; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/render_pass.cpp b/intern/cycles/hydra/render_pass.cpp new file mode 100644 index 00000000000..9d47dfc5c8d --- /dev/null +++ b/intern/cycles/hydra/render_pass.cpp @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/render_pass.h" +#include "hydra/camera.h" +#include "hydra/display_driver.h" +#include "hydra/output_driver.h" +#include "hydra/render_buffer.h" +#include "hydra/render_delegate.h" +#include "hydra/session.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/scene.h" +#include "session/session.h" + +#include <pxr/imaging/hd/renderPassState.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +HdCyclesRenderPass::HdCyclesRenderPass(HdRenderIndex *index, + HdRprimCollection const &collection, + HdCyclesSession *renderParam) + : HdRenderPass(index, collection), _renderParam(renderParam) +{ + Session *const session = _renderParam->session; + // Reset cancel state so session thread can continue rendering + session->progress.reset(); + + session->set_output_driver(make_unique<HdCyclesOutputDriver>(renderParam)); + + const auto renderDelegate = static_cast<const HdCyclesDelegate *>( + GetRenderIndex()->GetRenderDelegate()); + if (renderDelegate->IsDisplaySupported()) { + session->set_display_driver( + make_unique<HdCyclesDisplayDriver>(renderParam, renderDelegate->GetHgi())); + } +} + +HdCyclesRenderPass::~HdCyclesRenderPass() +{ + Session *const session = _renderParam->session; + session->cancel(true); +} + +bool HdCyclesRenderPass::IsConverged() const +{ + for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) { + if (aovBinding.renderBuffer && !aovBinding.renderBuffer->IsConverged()) { + return false; + } + } + + return true; +} + +void HdCyclesRenderPass::ResetConverged() +{ + for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) { + if (const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) { + renderBuffer->SetConverged(false); + } + } +} + +void HdCyclesRenderPass::_Execute(const HdRenderPassStateSharedPtr &renderPassState, + const TfTokenVector &renderTags) +{ + Scene *const scene = _renderParam->session->scene; + Session *const session = _renderParam->session; + + if (session->progress.get_cancel()) { + return; // Something went wrong and cannot continue without recreating the session + } + + if (scene->mutex.try_lock()) { + const auto renderDelegate = static_cast<HdCyclesDelegate *>( + GetRenderIndex()->GetRenderDelegate()); + + const unsigned int settingsVersion = renderDelegate->GetRenderSettingsVersion(); + + // Update requested AOV bindings + const HdRenderPassAovBindingVector &aovBindings = renderPassState->GetAovBindings(); + if (_renderParam->GetAovBindings() != aovBindings || + // Need to resync passes when denoising is enabled or disabled to update the pass mode + (settingsVersion != _lastSettingsVersion && + scene->integrator->use_denoise_is_modified())) { + _renderParam->SyncAovBindings(aovBindings); + + if (renderDelegate->IsDisplaySupported()) { + // Update display pass to the first requested color AOV + HdRenderPassAovBinding displayAovBinding = !aovBindings.empty() ? aovBindings.front() : + HdRenderPassAovBinding(); + if (displayAovBinding.aovName == HdAovTokens->color && displayAovBinding.renderBuffer) { + _renderParam->SetDisplayAovBinding(displayAovBinding); + } + else { + _renderParam->SetDisplayAovBinding(HdRenderPassAovBinding()); + } + } + } + + // Update camera dimensions to the viewport size +#if PXR_VERSION >= 2102 + CameraUtilFraming framing = renderPassState->GetFraming(); + if (!framing.IsValid()) { + const GfVec4f vp = renderPassState->GetViewport(); + framing = CameraUtilFraming(GfRect2i(GfVec2i(0), int(vp[2]), int(vp[3]))); + } + + scene->camera->set_full_width(framing.dataWindow.GetWidth()); + scene->camera->set_full_height(framing.dataWindow.GetHeight()); +#else + const GfVec4f vp = renderPassState->GetViewport(); + scene->camera->set_full_width(int(vp[2])); + scene->camera->set_full_height(int(vp[3])); +#endif + + if (const auto camera = static_cast<const HdCyclesCamera *>(renderPassState->GetCamera())) { + camera->ApplyCameraSettings(scene->camera); + } + else { + HdCyclesCamera::ApplyCameraSettings(renderPassState->GetWorldToViewMatrix(), + renderPassState->GetProjectionMatrix(), + renderPassState->GetClipPlanes(), + scene->camera); + } + + // Reset session if the session, scene, camera or AOV bindings changed + if (scene->need_reset() || settingsVersion != _lastSettingsVersion) { + _lastSettingsVersion = settingsVersion; + + // Reset convergence state of all render buffers + ResetConverged(); + + BufferParams buffer_params; +#if PXR_VERSION >= 2102 + buffer_params.full_x = static_cast<int>(framing.displayWindow.GetMin()[0]); + buffer_params.full_y = static_cast<int>(framing.displayWindow.GetMin()[1]); + buffer_params.full_width = static_cast<int>(framing.displayWindow.GetSize()[0]); + buffer_params.full_height = static_cast<int>(framing.displayWindow.GetSize()[1]); + + buffer_params.window_x = framing.dataWindow.GetMinX() - buffer_params.full_x; + buffer_params.window_y = framing.dataWindow.GetMinY() - buffer_params.full_y; + buffer_params.window_width = framing.dataWindow.GetWidth(); + buffer_params.window_height = framing.dataWindow.GetHeight(); + + buffer_params.width = buffer_params.window_width; + buffer_params.height = buffer_params.window_height; +#else + buffer_params.width = static_cast<int>(vp[2]); + buffer_params.height = static_cast<int>(vp[3]); + buffer_params.full_width = buffer_params.width; + buffer_params.full_height = buffer_params.height; + buffer_params.window_width = buffer_params.width; + buffer_params.window_height = buffer_params.height; +#endif + + session->reset(session->params, buffer_params); + } + + scene->mutex.unlock(); + + // Start Cycles render thread if not already running + session->start(); + } + + session->draw(); +} + +void HdCyclesRenderPass::_MarkCollectionDirty() +{ +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/render_pass.h b/intern/cycles/hydra/render_pass.h new file mode 100644 index 00000000000..f04c97097a6 --- /dev/null +++ b/intern/cycles/hydra/render_pass.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" + +#include <pxr/imaging/hd/renderPass.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesRenderPass final : public PXR_NS::HdRenderPass { + public: + HdCyclesRenderPass(PXR_NS::HdRenderIndex *index, + const PXR_NS::HdRprimCollection &collection, + HdCyclesSession *renderParam); + ~HdCyclesRenderPass() override; + + bool IsConverged() const override; + + private: + void ResetConverged(); + + void _Execute(const PXR_NS::HdRenderPassStateSharedPtr &renderPassState, + const PXR_NS::TfTokenVector &renderTags) override; + + void _MarkCollectionDirty() override; + + HdCyclesSession *_renderParam; + unsigned int _lastSettingsVersion = 0; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/resources/plugInfo.json b/intern/cycles/hydra/resources/plugInfo.json new file mode 100644 index 00000000000..94fe778ea44 --- /dev/null +++ b/intern/cycles/hydra/resources/plugInfo.json @@ -0,0 +1,22 @@ +{ + "Plugins": [ + { + "Info": { + "Types": { + "HdCyclesPlugin": { + "bases": [ + "HdRendererPlugin" + ], + "displayName": "Cycles", + "priority": 0 + } + } + }, + "LibraryPath": "@PLUG_INFO_LIBRARY_PATH@", + "Name": "hdCycles", + "ResourcePath": "@PLUG_INFO_RESOURCE_PATH@", + "Root": "@PLUG_INFO_ROOT@", + "Type": "library" + } + ] +} diff --git a/intern/cycles/hydra/session.cpp b/intern/cycles/hydra/session.cpp new file mode 100644 index 00000000000..f6865bdedd1 --- /dev/null +++ b/intern/cycles/hydra/session.cpp @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/session.h" +#include "scene/shader.h" +// Have to include shader.h before background.h so that 'set_shader' uses the correct 'set' +// overload taking a 'Node *', rather than the one taking a 'bool' +#include "scene/background.h" +#include "scene/light.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "session/session.h" + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +namespace { + +const std::unordered_map<TfToken, PassType, TfToken::HashFunctor> kAovToPass = { + {HdAovTokens->color, PASS_COMBINED}, + {HdAovTokens->depth, PASS_DEPTH}, + {HdAovTokens->normal, PASS_NORMAL}, + {HdAovTokens->primId, PASS_OBJECT_ID}, + {HdAovTokens->instanceId, PASS_AOV_VALUE}, +}; + +} // namespace + +SceneLock::SceneLock(const HdRenderParam *renderParam) + : scene(static_cast<const HdCyclesSession *>(renderParam)->session->scene), + sceneLock(scene->mutex) +{ +} + +SceneLock::~SceneLock() +{ +} + +HdCyclesSession::HdCyclesSession(Session *session_) : session(session_), _ownCyclesSession(false) +{ +} + +HdCyclesSession::HdCyclesSession(const SessionParams ¶ms) + : session(new Session(params, SceneParams())), _ownCyclesSession(true) +{ + Scene *const scene = session->scene; + + // Create background with ambient light + { + ShaderGraph *graph = new ShaderGraph(); + + BackgroundNode *bgNode = graph->create_node<BackgroundNode>(); + bgNode->set_color(one_float3()); + graph->add(bgNode); + + graph->connect(bgNode->output("Background"), graph->output()->input("Surface")); + + scene->default_background->set_graph(graph); + scene->default_background->tag_update(scene); + } + + // Wire up object color in default surface material + { + ShaderGraph *graph = new ShaderGraph(); + + ObjectInfoNode *objectNode = graph->create_node<ObjectInfoNode>(); + graph->add(objectNode); + + DiffuseBsdfNode *diffuseNode = graph->create_node<DiffuseBsdfNode>(); + graph->add(diffuseNode); + + graph->connect(objectNode->output("Color"), diffuseNode->input("Color")); + graph->connect(diffuseNode->output("BSDF"), graph->output()->input("Surface")); + +#if 1 + // Create the instanceId AOV output + const ustring instanceId(HdAovTokens->instanceId.GetString()); + + OutputAOVNode *aovNode = graph->create_node<OutputAOVNode>(); + aovNode->set_name(instanceId); + graph->add(aovNode); + + AttributeNode *instanceIdNode = graph->create_node<AttributeNode>(); + instanceIdNode->set_attribute(instanceId); + graph->add(instanceIdNode); + + graph->connect(instanceIdNode->output("Fac"), aovNode->input("Value")); +#endif + + scene->default_surface->set_graph(graph); + scene->default_surface->tag_update(scene); + } +} + +HdCyclesSession::~HdCyclesSession() +{ + if (_ownCyclesSession) { + delete session; + } +} + +void HdCyclesSession::UpdateScene() +{ + Scene *const scene = session->scene; + + // Update background depending on presence of a background light + if (scene->light_manager->need_update()) { + Light *background_light = nullptr; + for (Light *light : scene->lights) { + if (light->get_light_type() == LIGHT_BACKGROUND) { + background_light = light; + break; + } + } + + if (!background_light) { + scene->background->set_shader(scene->default_background); + scene->background->set_transparent(true); + } + else { + scene->background->set_shader(background_light->get_shader()); + scene->background->set_transparent(false); + } + + scene->background->tag_update(scene); + } +} + +void HdCyclesSession::SyncAovBindings(const HdRenderPassAovBindingVector &aovBindings) +{ + Scene *const scene = session->scene; + + // Delete all existing passes + scene->delete_nodes(set<Pass *>(scene->passes.begin(), scene->passes.end())); + + // Update passes with requested AOV bindings + _aovBindings = aovBindings; + for (const HdRenderPassAovBinding &aovBinding : aovBindings) { + const auto cyclesAov = kAovToPass.find(aovBinding.aovName); + if (cyclesAov == kAovToPass.end()) { + // TODO: Use PASS_AOV_COLOR and PASS_AOV_VALUE for these? + TF_WARN("Unknown pass %s", aovBinding.aovName.GetText()); + continue; + } + + const PassType type = cyclesAov->second; + const PassMode mode = PassMode::DENOISED; + + Pass *pass = scene->create_node<Pass>(); + pass->set_type(type); + pass->set_mode(mode); + pass->set_name(ustring(aovBinding.aovName.GetString())); + } +} + +void HdCyclesSession::RemoveAovBinding(HdRenderBuffer *renderBuffer) +{ + for (HdRenderPassAovBinding &aovBinding : _aovBindings) { + if (renderBuffer == aovBinding.renderBuffer) { + aovBinding.renderBuffer = nullptr; + break; + } + } + + if (renderBuffer == _displayAovBinding.renderBuffer) { + _displayAovBinding.renderBuffer = nullptr; + } +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/session.h b/intern/cycles/hydra/session.h new file mode 100644 index 00000000000..7e649c1847a --- /dev/null +++ b/intern/cycles/hydra/session.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "util/thread.h" + +#include <pxr/imaging/hd/renderDelegate.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +struct SceneLock { + SceneLock(const PXR_NS::HdRenderParam *renderParam); + ~SceneLock(); + + CCL_NS::Scene *scene; + + private: + CCL_NS::thread_scoped_lock sceneLock; +}; + +class HdCyclesSession final : public PXR_NS::HdRenderParam { + public: + HdCyclesSession(CCL_NS::Session *session_); + HdCyclesSession(const CCL_NS::SessionParams ¶ms); + ~HdCyclesSession() override; + + void UpdateScene(); + + PXR_NS::HdRenderPassAovBinding GetDisplayAovBinding() const + { + return _displayAovBinding; + } + + void SetDisplayAovBinding(const PXR_NS::HdRenderPassAovBinding &aovBinding) + { + _displayAovBinding = aovBinding; + } + + const PXR_NS::HdRenderPassAovBindingVector &GetAovBindings() const + { + return _aovBindings; + } + + void SyncAovBindings(const PXR_NS::HdRenderPassAovBindingVector &aovBindings); + + void RemoveAovBinding(PXR_NS::HdRenderBuffer *renderBuffer); + + CCL_NS::Session *session; + + private: + const bool _ownCyclesSession; + PXR_NS::HdRenderPassAovBindingVector _aovBindings; + PXR_NS::HdRenderPassAovBinding _displayAovBinding; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/volume.cpp b/intern/cycles/hydra/volume.cpp new file mode 100644 index 00000000000..7b965c613ed --- /dev/null +++ b/intern/cycles/hydra/volume.cpp @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#include "hydra/volume.h" +#include "hydra/field.h" +#include "hydra/geometry.inl" +#include "scene/volume.h" + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +// clang-format off +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (openvdbAsset) +); +// clang-format on + +HdCyclesVolume::HdCyclesVolume(const SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const SdfPath &instancerId +#endif + ) + : HdCyclesGeometry(rprimId +#if PXR_VERSION < 2102 + , + instancerId +#endif + ) +{ +} + +HdCyclesVolume::~HdCyclesVolume() +{ +} + +HdDirtyBits HdCyclesVolume::GetInitialDirtyBitsMask() const +{ + HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask(); + bits |= HdChangeTracker::DirtyVolumeField; + return bits; +} + +void HdCyclesVolume::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild) +{ + Scene *const scene = (Scene *)_geom->get_owner(); + + if (dirtyBits & HdChangeTracker::DirtyVolumeField) { + for (const HdVolumeFieldDescriptor &field : + sceneDelegate->GetVolumeFieldDescriptors(GetId())) { + if (const auto openvdbAsset = static_cast<HdCyclesField *>( + sceneDelegate->GetRenderIndex().GetBprim(_tokens->openvdbAsset, field.fieldId))) { + const ustring name(field.fieldName.GetString()); + + AttributeStandard std = ATTR_STD_NONE; + if (name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) { + std = ATTR_STD_VOLUME_DENSITY; + } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) { + std = ATTR_STD_VOLUME_COLOR; + } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) { + std = ATTR_STD_VOLUME_FLAME; + } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) { + std = ATTR_STD_VOLUME_HEAT; + } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) { + std = ATTR_STD_VOLUME_TEMPERATURE; + } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) { + std = ATTR_STD_VOLUME_VELOCITY; + } + + // Skip attributes that are not needed + if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) || + _geom->need_attribute(scene, name)) { + Attribute *const attr = (std != ATTR_STD_NONE) ? + _geom->attributes.add(std) : + _geom->attributes.add( + name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL); + attr->data_voxel() = openvdbAsset->GetImageHandle(); + } + } + } + + rebuild = true; + } +} + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/hydra/volume.h b/intern/cycles/hydra/volume.h new file mode 100644 index 00000000000..7fb5fa779b5 --- /dev/null +++ b/intern/cycles/hydra/volume.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 NVIDIA Corporation + * Copyright 2022 Blender Foundation */ + +#pragma once + +#include "hydra/config.h" +#include "hydra/geometry.h" + +#include <pxr/imaging/hd/volume.h> + +HDCYCLES_NAMESPACE_OPEN_SCOPE + +class HdCyclesVolume final : public HdCyclesGeometry<PXR_NS::HdVolume, CCL_NS::Volume> { + public: + HdCyclesVolume( + const PXR_NS::SdfPath &rprimId +#if PXR_VERSION < 2102 + , + const PXR_NS::SdfPath &instancerId = {} +#endif + ); + ~HdCyclesVolume() override; + + PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override; + + private: + void Populate(PXR_NS::HdSceneDelegate *sceneDelegate, + PXR_NS::HdDirtyBits dirtyBits, + bool &rebuild) override; +}; + +HDCYCLES_NAMESPACE_CLOSE_SCOPE diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp index 90a5a01320b..a75662c90d8 100644 --- a/intern/cycles/integrator/render_scheduler.cpp +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -20,7 +20,7 @@ RenderScheduler::RenderScheduler(TileManager &tile_manager, const SessionParams background_(params.background), pixel_size_(params.pixel_size), tile_manager_(tile_manager), - default_start_resolution_divider_(pixel_size_ * 8) + default_start_resolution_divider_(params.use_resolution_divider ? pixel_size_ * 8 : 0) { use_progressive_noise_floor_ = !background_; } @@ -119,7 +119,7 @@ void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples, /* In background mode never do lower resolution render preview, as it is not really supported * by the software. */ - if (background_) { + if (background_ || start_resolution_divider_ == 0) { state_.resolution_divider = 1; } else { @@ -1050,6 +1050,10 @@ bool RenderScheduler::work_need_rebalance() void RenderScheduler::update_start_resolution_divider() { + if (default_start_resolution_divider_ == 0) { + return; + } + if (start_resolution_divider_ == 0) { /* Resolution divider has never been calculated before: use default resolution, so that we have * somewhat good initial behavior, giving a chance to collect real numbers. */ diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 8e7b46ab574..6e3ac1bd32f 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -50,7 +50,6 @@ set(SRC_KERNEL_DEVICE_GPU_HEADERS device/gpu/kernel.h device/gpu/parallel_active_index.h device/gpu/parallel_prefix_sum.h - device/gpu/parallel_reduce.h device/gpu/parallel_sorted_index.h device/gpu/work_stealing.h ) diff --git a/intern/cycles/kernel/device/cpu/image.h b/intern/cycles/kernel/device/cpu/image.h index b6bcd19b9ea..3b714a3e580 100644 --- a/intern/cycles/kernel/device/cpu/image.h +++ b/intern/cycles/kernel/device/cpu/image.h @@ -31,7 +31,17 @@ ccl_device_inline float frac(float x, int *ix) return x - (float)i; } -template<typename T> struct TextureInterpolator { +template<typename TexT, typename OutT = float4> struct TextureInterpolator { + + static ccl_always_inline OutT zero() + { + if constexpr (std::is_same<OutT, float4>::value) { + return zero_float4(); + } + else { + return 0.0f; + } + } static ccl_always_inline float4 read(float4 r) { @@ -40,21 +50,18 @@ template<typename T> struct TextureInterpolator { static ccl_always_inline float4 read(uchar4 r) { - float f = 1.0f / 255.0f; + const float f = 1.0f / 255.0f; return make_float4(r.x * f, r.y * f, r.z * f, r.w * f); } - static ccl_always_inline float4 read(uchar r) + static ccl_always_inline float read(uchar r) { - float f = r * (1.0f / 255.0f); - return make_float4(f, f, f, 1.0f); + return r * (1.0f / 255.0f); } - static ccl_always_inline float4 read(float r) + static ccl_always_inline float read(float r) { - /* TODO(dingto): Optimize this, so interpolation - * happens on float instead of float4 */ - return make_float4(r, r, r, 1.0f); + return r; } static ccl_always_inline float4 read(half4 r) @@ -62,37 +69,131 @@ template<typename T> struct TextureInterpolator { return half4_to_float4_image(r); } - static ccl_always_inline float4 read(half r) + static ccl_always_inline float read(half r) { - float f = half_to_float_image(r); - return make_float4(f, f, f, 1.0f); + return half_to_float_image(r); } - static ccl_always_inline float4 read(uint16_t r) + static ccl_always_inline float read(uint16_t r) { - float f = r * (1.0f / 65535.0f); - return make_float4(f, f, f, 1.0f); + return r * (1.0f / 65535.0f); } static ccl_always_inline float4 read(ushort4 r) { - float f = 1.0f / 65535.0f; + const float f = 1.0f / 65535.0f; return make_float4(r.x * f, r.y * f, r.z * f, r.w * f); } - static ccl_always_inline float4 read(const T *data, int x, int y, int width, int height) + /* Read 2D Texture Data + * Does not check if data request is in bounds. */ + static ccl_always_inline OutT read(const TexT *data, int x, int y, int width, int height) + { + return read(data[y * width + x]); + } + + /* Read 2D Texture Data Clip + * Returns transparent black if data request is out of bounds. */ + static ccl_always_inline OutT read_clip(const TexT *data, int x, int y, int width, int height) { - if (x < 0 || y < 0 || x >= width || y >= height) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + if (x < 0 || x >= width || y < 0 || y >= height) { + return zero(); } return read(data[y * width + x]); } + /* Read 3D Texture Data + * Does not check if data request is in bounds. */ + static ccl_always_inline OutT + read(const TexT *data, int x, int y, int z, int width, int height, int depth) + { + return read(data[x + y * width + z * width * height]); + } + + /* Read 3D Texture Data Clip + * Returns transparent black if data request is out of bounds. */ + static ccl_always_inline OutT + read_clip(const TexT *data, int x, int y, int z, int width, int height, int depth) + { + if (x < 0 || x >= width || y < 0 || y >= height || z < 0 || z >= depth) { + return zero(); + } + return read(data[x + y * width + z * width * height]); + } + + /* Trilinear Interpolation */ + static ccl_always_inline OutT + trilinear_lookup(const TexT *data, + float tx, + float ty, + float tz, + int ix, + int iy, + int iz, + int nix, + int niy, + int niz, + int width, + int height, + int depth, + OutT read(const TexT *, int, int, int, int, int, int)) + { + OutT r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) * + read(data, ix, iy, iz, width, height, depth); + r += (1.0f - tz) * (1.0f - ty) * tx * read(data, nix, iy, iz, width, height, depth); + r += (1.0f - tz) * ty * (1.0f - tx) * read(data, ix, niy, iz, width, height, depth); + r += (1.0f - tz) * ty * tx * read(data, nix, niy, iz, width, height, depth); + + r += tz * (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, niz, width, height, depth); + r += tz * (1.0f - ty) * tx * read(data, nix, iy, niz, width, height, depth); + r += tz * ty * (1.0f - tx) * read(data, ix, niy, niz, width, height, depth); + r += tz * ty * tx * read(data, nix, niy, niz, width, height, depth); + return r; + } + + /** Tricubic Interpolation */ + static ccl_always_inline OutT + tricubic_lookup(const TexT *data, + float tx, + float ty, + float tz, + const int xc[4], + const int yc[4], + const int zc[4], + int width, + int height, + int depth, + OutT read(const TexT *, int, int, int, int, int, int)) + { + float u[4], v[4], w[4]; + + /* Some helper macros to keep code size reasonable. + * Lets the compiler inline all the matrix multiplications. + */ +#define DATA(x, y, z) (read(data, xc[x], yc[y], zc[z], width, height, depth)) +#define COL_TERM(col, row) \ + (v[col] * (u[0] * DATA(0, col, row) + u[1] * DATA(1, col, row) + u[2] * DATA(2, col, row) + \ + u[3] * DATA(3, col, row))) +#define ROW_TERM(row) \ + (w[row] * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row))) + + SET_CUBIC_SPLINE_WEIGHTS(u, tx); + SET_CUBIC_SPLINE_WEIGHTS(v, ty); + SET_CUBIC_SPLINE_WEIGHTS(w, tz); + /* Actual interpolation. */ + return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3); + +#undef COL_TERM +#undef ROW_TERM +#undef DATA + } + static ccl_always_inline int wrap_periodic(int x, int width) { x %= width; - if (x < 0) + if (x < 0) { x += width; + } return x; } @@ -103,9 +204,8 @@ template<typename T> struct TextureInterpolator { /* ******** 2D interpolation ******** */ - static ccl_always_inline float4 interp_closest(const TextureInfo &info, float x, float y) + static ccl_always_inline OutT interp_closest(const TextureInfo &info, float x, float y) { - const T *data = (const T *)info.data; const int width = info.width; const int height = info.height; int ix, iy; @@ -117,105 +217,134 @@ template<typename T> struct TextureInterpolator { iy = wrap_periodic(iy, height); break; case EXTENSION_CLIP: - if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + /* No samples are inside the clip region. */ + if (ix < 0 || ix >= width || iy < 0 || iy >= height) { + return zero(); } - ATTR_FALLTHROUGH; + break; case EXTENSION_EXTEND: ix = wrap_clamp(ix, width); iy = wrap_clamp(iy, height); break; default: kernel_assert(0); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero(); } - return read(data[ix + iy * width]); + + const TexT *data = (const TexT *)info.data; + return read((const TexT *)data, ix, iy, width, height); } - static ccl_always_inline float4 interp_linear(const TextureInfo &info, float x, float y) + static ccl_always_inline OutT interp_linear(const TextureInfo &info, float x, float y) { - const T *data = (const T *)info.data; const int width = info.width; const int height = info.height; - int ix, iy, nix, niy; + + /* A -0.5 offset is used to center the linear samples around the sample point. */ + int ix, iy; + int nix, niy; const float tx = frac(x * (float)width - 0.5f, &ix); const float ty = frac(y * (float)height - 0.5f, &iy); + switch (info.extension) { case EXTENSION_REPEAT: ix = wrap_periodic(ix, width); - iy = wrap_periodic(iy, height); nix = wrap_periodic(ix + 1, width); + + iy = wrap_periodic(iy, height); niy = wrap_periodic(iy + 1, height); break; case EXTENSION_CLIP: + /* No linear samples are inside the clip region. */ + if (ix < -1 || ix >= width || iy < -1 || iy >= height) { + return zero(); + } nix = ix + 1; niy = iy + 1; break; case EXTENSION_EXTEND: nix = wrap_clamp(ix + 1, width); - niy = wrap_clamp(iy + 1, height); ix = wrap_clamp(ix, width); + niy = wrap_clamp(iy + 1, height); iy = wrap_clamp(iy, height); break; default: kernel_assert(0); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero(); } - return (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, width, height) + - (1.0f - ty) * tx * read(data, nix, iy, width, height) + - ty * (1.0f - tx) * read(data, ix, niy, width, height) + - ty * tx * read(data, nix, niy, width, height); + + const TexT *data = (const TexT *)info.data; + return (1.0f - ty) * (1.0f - tx) * read_clip(data, ix, iy, width, height) + + (1.0f - ty) * tx * read_clip(data, nix, iy, width, height) + + ty * (1.0f - tx) * read_clip(data, ix, niy, width, height) + + ty * tx * read_clip(data, nix, niy, width, height); } - static ccl_always_inline float4 interp_cubic(const TextureInfo &info, float x, float y) + static ccl_always_inline OutT interp_cubic(const TextureInfo &info, float x, float y) { - const T *data = (const T *)info.data; const int width = info.width; const int height = info.height; - int ix, iy, nix, niy; + + /* A -0.5 offset is used to center the cubic samples around the sample point. */ + int ix, iy; const float tx = frac(x * (float)width - 0.5f, &ix); const float ty = frac(y * (float)height - 0.5f, &iy); - int pix, piy, nnix, nniy; + + int pix, piy; + int nix, niy; + int nnix, nniy; + switch (info.extension) { case EXTENSION_REPEAT: ix = wrap_periodic(ix, width); - iy = wrap_periodic(iy, height); pix = wrap_periodic(ix - 1, width); - piy = wrap_periodic(iy - 1, height); nix = wrap_periodic(ix + 1, width); - niy = wrap_periodic(iy + 1, height); nnix = wrap_periodic(ix + 2, width); + + iy = wrap_periodic(iy, height); + piy = wrap_periodic(iy - 1, height); + niy = wrap_periodic(iy + 1, height); nniy = wrap_periodic(iy + 2, height); break; case EXTENSION_CLIP: + /* No cubic samples are inside the clip region. */ + if (ix < -2 || ix > width || iy < -2 || iy > height) { + return zero(); + } + pix = ix - 1; - piy = iy - 1; nix = ix + 1; - niy = iy + 1; nnix = ix + 2; + + piy = iy - 1; + niy = iy + 1; nniy = iy + 2; break; case EXTENSION_EXTEND: pix = wrap_clamp(ix - 1, width); - piy = wrap_clamp(iy - 1, height); nix = wrap_clamp(ix + 1, width); - niy = wrap_clamp(iy + 1, height); nnix = wrap_clamp(ix + 2, width); - nniy = wrap_clamp(iy + 2, height); ix = wrap_clamp(ix, width); + + piy = wrap_clamp(iy - 1, height); + niy = wrap_clamp(iy + 1, height); + nniy = wrap_clamp(iy + 2, height); iy = wrap_clamp(iy, height); break; default: kernel_assert(0); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero(); } + + const TexT *data = (const TexT *)info.data; const int xc[4] = {pix, ix, nix, nnix}; const int yc[4] = {piy, iy, niy, nniy}; float u[4], v[4]; - /* Some helper macro to keep code reasonable size, - * let compiler to inline all the matrix multiplications. + + /* Some helper macros to keep code size reasonable. + * Lets the compiler inline all the matrix multiplications. */ -#define DATA(x, y) (read(data, xc[x], yc[y], width, height)) +#define DATA(x, y) (read_clip(data, xc[x], yc[y], width, height)) #define TERM(col) \ (v[col] * \ (u[0] * DATA(0, col) + u[1] * DATA(1, col) + u[2] * DATA(2, col) + u[3] * DATA(3, col))) @@ -229,11 +358,8 @@ template<typename T> struct TextureInterpolator { #undef DATA } - static ccl_always_inline float4 interp(const TextureInfo &info, float x, float y) + static ccl_always_inline OutT interp(const TextureInfo &info, float x, float y) { - if (UNLIKELY(!info.data)) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } switch (info.interpolation) { case INTERPOLATION_CLOSEST: return interp_closest(info, x, y); @@ -246,14 +372,14 @@ template<typename T> struct TextureInterpolator { /* ******** 3D interpolation ******** */ - static ccl_always_inline float4 interp_3d_closest(const TextureInfo &info, - float x, - float y, - float z) + static ccl_always_inline OutT interp_3d_closest(const TextureInfo &info, + float x, + float y, + float z) { - int width = info.width; - int height = info.height; - int depth = info.depth; + const int width = info.width; + const int height = info.height; + const int depth = info.depth; int ix, iy, iz; frac(x * (float)width, &ix); @@ -267,10 +393,11 @@ template<typename T> struct TextureInterpolator { iz = wrap_periodic(iz, depth); break; case EXTENSION_CLIP: - if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + /* No samples are inside the clip region. */ + if (ix < 0 || ix >= width || iy < 0 || iy >= height || iz < 0 || iz >= depth) { + return zero(); } - ATTR_FALLTHROUGH; + break; case EXTENSION_EXTEND: ix = wrap_clamp(ix, width); iy = wrap_clamp(iy, height); @@ -278,24 +405,25 @@ template<typename T> struct TextureInterpolator { break; default: kernel_assert(0); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero(); } - const T *data = (const T *)info.data; - return read(data[ix + iy * width + iz * width * height]); + const TexT *data = (const TexT *)info.data; + return read(data, ix, iy, iz, width, height, depth); } - static ccl_always_inline float4 interp_3d_linear(const TextureInfo &info, - float x, - float y, - float z) + static ccl_always_inline OutT interp_3d_linear(const TextureInfo &info, + float x, + float y, + float z) { - int width = info.width; - int height = info.height; - int depth = info.depth; + const int width = info.width; + const int height = info.height; + const int depth = info.depth; int ix, iy, iz; int nix, niy, niz; + /* A -0.5 offset is used to center the linear samples around the sample point. */ float tx = frac(x * (float)width - 0.5f, &ix); float ty = frac(y * (float)height - 0.5f, &iy); float tz = frac(z * (float)depth - 0.5f, &iz); @@ -303,50 +431,79 @@ template<typename T> struct TextureInterpolator { switch (info.extension) { case EXTENSION_REPEAT: ix = wrap_periodic(ix, width); - iy = wrap_periodic(iy, height); - iz = wrap_periodic(iz, depth); - nix = wrap_periodic(ix + 1, width); + + iy = wrap_periodic(iy, height); niy = wrap_periodic(iy + 1, height); + + iz = wrap_periodic(iz, depth); niz = wrap_periodic(iz + 1, depth); break; case EXTENSION_CLIP: - if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + /* No linear samples are inside the clip region. */ + if (ix < -1 || ix >= width || iy < -1 || iy >= height || iz < -1 || iz >= depth) { + return zero(); + } + + nix = ix + 1; + niy = iy + 1; + niz = iz + 1; + + /* All linear samples are inside the clip region. */ + if (ix >= 0 && nix < width && iy >= 0 && niy < height && iz >= 0 && niz < depth) { + break; } - ATTR_FALLTHROUGH; + + /* The linear samples span the clip border. + * #read_clip is used to ensure proper interpolation across the clip border. */ + return trilinear_lookup((const TexT *)info.data, + tx, + ty, + tz, + ix, + iy, + iz, + nix, + niy, + niz, + width, + height, + depth, + read_clip); case EXTENSION_EXTEND: nix = wrap_clamp(ix + 1, width); - niy = wrap_clamp(iy + 1, height); - niz = wrap_clamp(iz + 1, depth); - ix = wrap_clamp(ix, width); + + niy = wrap_clamp(iy + 1, height); iy = wrap_clamp(iy, height); + + niz = wrap_clamp(iz + 1, depth); iz = wrap_clamp(iz, depth); break; default: kernel_assert(0); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero(); } - const T *data = (const T *)info.data; - float4 r; - - r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) * - read(data[ix + iy * width + iz * width * height]); - r += (1.0f - tz) * (1.0f - ty) * tx * read(data[nix + iy * width + iz * width * height]); - r += (1.0f - tz) * ty * (1.0f - tx) * read(data[ix + niy * width + iz * width * height]); - r += (1.0f - tz) * ty * tx * read(data[nix + niy * width + iz * width * height]); - - r += tz * (1.0f - ty) * (1.0f - tx) * read(data[ix + iy * width + niz * width * height]); - r += tz * (1.0f - ty) * tx * read(data[nix + iy * width + niz * width * height]); - r += tz * ty * (1.0f - tx) * read(data[ix + niy * width + niz * width * height]); - r += tz * ty * tx * read(data[nix + niy * width + niz * width * height]); - - return r; + return trilinear_lookup((const TexT *)info.data, + tx, + ty, + tz, + ix, + iy, + iz, + nix, + niy, + niz, + width, + height, + depth, + read); } - /* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are + /* Tricubic b-spline interpolation. + * + * TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are * causing stack overflow issue in this function unless it is inlined. * * Only happens for AVX2 kernel and global __KERNEL_SSE__ vectorization @@ -357,100 +514,101 @@ template<typename T> struct TextureInterpolator { #else static ccl_never_inline #endif - float4 + OutT interp_3d_cubic(const TextureInfo &info, float x, float y, float z) { int width = info.width; int height = info.height; int depth = info.depth; int ix, iy, iz; - int nix, niy, niz; - /* Tricubic b-spline interpolation. */ + + /* A -0.5 offset is used to center the cubic samples around the sample point. */ const float tx = frac(x * (float)width - 0.5f, &ix); const float ty = frac(y * (float)height - 0.5f, &iy); const float tz = frac(z * (float)depth - 0.5f, &iz); - int pix, piy, piz, nnix, nniy, nniz; + + int pix, piy, piz; + int nix, niy, niz; + int nnix, nniy, nniz; switch (info.extension) { case EXTENSION_REPEAT: ix = wrap_periodic(ix, width); - iy = wrap_periodic(iy, height); - iz = wrap_periodic(iz, depth); - pix = wrap_periodic(ix - 1, width); - piy = wrap_periodic(iy - 1, height); - piz = wrap_periodic(iz - 1, depth); - nix = wrap_periodic(ix + 1, width); - niy = wrap_periodic(iy + 1, height); - niz = wrap_periodic(iz + 1, depth); - nnix = wrap_periodic(ix + 2, width); + + iy = wrap_periodic(iy, height); + niy = wrap_periodic(iy + 1, height); + piy = wrap_periodic(iy - 1, height); nniy = wrap_periodic(iy + 2, height); + + iz = wrap_periodic(iz, depth); + piz = wrap_periodic(iz - 1, depth); + niz = wrap_periodic(iz + 1, depth); nniz = wrap_periodic(iz + 2, depth); break; - case EXTENSION_CLIP: - if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + case EXTENSION_CLIP: { + /* No cubic samples are inside the clip region. */ + if (ix < -2 || ix > width || iy < -2 || iy > height || iz < -2 || iz > depth) { + return zero(); + } + + pix = ix - 1; + nnix = ix + 2; + nix = ix + 1; + + piy = iy - 1; + niy = iy + 1; + nniy = iy + 2; + + piz = iz - 1; + niz = iz + 1; + nniz = iz + 2; + + /* All cubic samples are inside the clip region. */ + if (pix >= 0 && nnix < width && piy >= 0 && nniy < height && piz >= 0 && nniz < depth) { + break; } - ATTR_FALLTHROUGH; + + /* The Cubic samples span the clip border. + * read_clip is used to ensure proper interpolation across the clip border. */ + const int xc[4] = {pix, ix, nix, nnix}; + const int yc[4] = {piy, iy, niy, nniy}; + const int zc[4] = {piz, iz, niz, nniz}; + return tricubic_lookup( + (const TexT *)info.data, tx, ty, tz, xc, yc, zc, width, height, depth, read_clip); + } case EXTENSION_EXTEND: pix = wrap_clamp(ix - 1, width); - piy = wrap_clamp(iy - 1, height); - piz = wrap_clamp(iz - 1, depth); - nix = wrap_clamp(ix + 1, width); - niy = wrap_clamp(iy + 1, height); - niz = wrap_clamp(iz + 1, depth); - nnix = wrap_clamp(ix + 2, width); - nniy = wrap_clamp(iy + 2, height); - nniz = wrap_clamp(iz + 2, depth); - ix = wrap_clamp(ix, width); + + piy = wrap_clamp(iy - 1, height); + niy = wrap_clamp(iy + 1, height); + nniy = wrap_clamp(iy + 2, height); iy = wrap_clamp(iy, height); + + piz = wrap_clamp(iz - 1, depth); + niz = wrap_clamp(iz + 1, depth); + nniz = wrap_clamp(iz + 2, depth); iz = wrap_clamp(iz, depth); break; default: kernel_assert(0); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero(); } - const int xc[4] = {pix, ix, nix, nnix}; - const int yc[4] = {width * piy, width * iy, width * niy, width * nniy}; - const int zc[4] = { - width * height * piz, width * height * iz, width * height * niz, width * height * nniz}; - float u[4], v[4], w[4]; - - /* Some helper macro to keep code reasonable size, - * let compiler to inline all the matrix multiplications. - */ -#define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]])) -#define COL_TERM(col, row) \ - (v[col] * (u[0] * DATA(0, col, row) + u[1] * DATA(1, col, row) + u[2] * DATA(2, col, row) + \ - u[3] * DATA(3, col, row))) -#define ROW_TERM(row) \ - (w[row] * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row))) - - SET_CUBIC_SPLINE_WEIGHTS(u, tx); - SET_CUBIC_SPLINE_WEIGHTS(v, ty); - SET_CUBIC_SPLINE_WEIGHTS(w, tz); - - /* Actual interpolation. */ - const T *data = (const T *)info.data; - return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3); - -#undef COL_TERM -#undef ROW_TERM -#undef DATA + const int yc[4] = {piy, iy, niy, nniy}; + const int zc[4] = {piz, iz, niz, nniz}; + const TexT *data = (const TexT *)info.data; + return tricubic_lookup(data, tx, ty, tz, xc, yc, zc, width, height, depth, read); } - static ccl_always_inline float4 + static ccl_always_inline OutT interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp) { - if (UNLIKELY(!info.data)) - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) { case INTERPOLATION_CLOSEST: return interp_3d_closest(info, x, y, z); @@ -463,13 +621,13 @@ template<typename T> struct TextureInterpolator { }; #ifdef WITH_NANOVDB -template<typename T> struct NanoVDBInterpolator { +template<typename TexT, typename OutT = float4> struct NanoVDBInterpolator { - typedef typename nanovdb::NanoGrid<T>::AccessorType AccessorType; + typedef typename nanovdb::NanoGrid<TexT>::AccessorType AccessorType; - static ccl_always_inline float4 read(float r) + static ccl_always_inline float read(float r) { - return make_float4(r, r, r, 1.0f); + return r; } static ccl_always_inline float4 read(nanovdb::Vec3f r) @@ -477,40 +635,43 @@ template<typename T> struct NanoVDBInterpolator { return make_float4(r[0], r[1], r[2], 1.0f); } - static ccl_always_inline float4 interp_3d_closest(const AccessorType &acc, - float x, - float y, - float z) + static ccl_always_inline OutT interp_3d_closest(const AccessorType &acc, + float x, + float y, + float z) { const nanovdb::Vec3f xyz(x, y, z); return read(nanovdb::SampleFromVoxels<AccessorType, 0, false>(acc)(xyz)); } - static ccl_always_inline float4 interp_3d_linear(const AccessorType &acc, - float x, - float y, - float z) + static ccl_always_inline OutT interp_3d_linear(const AccessorType &acc, + float x, + float y, + float z) { const nanovdb::Vec3f xyz(x - 0.5f, y - 0.5f, z - 0.5f); return read(nanovdb::SampleFromVoxels<AccessorType, 1, false>(acc)(xyz)); } + /* Tricubic b-spline interpolation. */ # if defined(__GNUC__) || defined(__clang__) static ccl_always_inline # else static ccl_never_inline # endif - float4 + OutT interp_3d_cubic(const AccessorType &acc, float x, float y, float z) { int ix, iy, iz; int nix, niy, niz; int pix, piy, piz; int nnix, nniy, nniz; - /* Tricubic b-spline interpolation. */ + + /* A -0.5 offset is used to center the cubic samples around the sample point. */ const float tx = frac(x - 0.5f, &ix); const float ty = frac(y - 0.5f, &iy); const float tz = frac(z - 0.5f, &iz); + pix = ix - 1; piy = iy - 1; piz = iz - 1; @@ -526,8 +687,8 @@ template<typename T> struct NanoVDBInterpolator { const int zc[4] = {piz, iz, niz, nniz}; float u[4], v[4], w[4]; - /* Some helper macro to keep code reasonable size, - * let compiler to inline all the matrix multiplications. + /* Some helper macros to keep code size reasonable. + * Lets the compiler inline all the matrix multiplications. */ # define DATA(x, y, z) (read(acc.getValue(nanovdb::Coord(xc[x], yc[y], zc[z])))) # define COL_TERM(col, row) \ @@ -548,12 +709,12 @@ template<typename T> struct NanoVDBInterpolator { # undef DATA } - static ccl_always_inline float4 + static ccl_always_inline OutT interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp) { using namespace nanovdb; - NanoGrid<T> *const grid = (NanoGrid<T> *)info.data; + NanoGrid<TexT> *const grid = (NanoGrid<TexT> *)info.data; AccessorType acc = grid->getAccessor(); switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) { @@ -574,15 +735,27 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, flo { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); + if (UNLIKELY(!info.data)) { + return zero_float4(); + } + switch (info.data_type) { - case IMAGE_DATA_TYPE_HALF: - return TextureInterpolator<half>::interp(info, x, y); - case IMAGE_DATA_TYPE_BYTE: - return TextureInterpolator<uchar>::interp(info, x, y); - case IMAGE_DATA_TYPE_USHORT: - return TextureInterpolator<uint16_t>::interp(info, x, y); - case IMAGE_DATA_TYPE_FLOAT: - return TextureInterpolator<float>::interp(info, x, y); + case IMAGE_DATA_TYPE_HALF: { + const float f = TextureInterpolator<half, float>::interp(info, x, y); + return make_float4(f, f, f, 1.0f); + } + case IMAGE_DATA_TYPE_BYTE: { + const float f = TextureInterpolator<uchar, float>::interp(info, x, y); + return make_float4(f, f, f, 1.0f); + } + case IMAGE_DATA_TYPE_USHORT: { + const float f = TextureInterpolator<uint16_t, float>::interp(info, x, y); + return make_float4(f, f, f, 1.0f); + } + case IMAGE_DATA_TYPE_FLOAT: { + const float f = TextureInterpolator<float, float>::interp(info, x, y); + return make_float4(f, f, f, 1.0f); + } case IMAGE_DATA_TYPE_HALF4: return TextureInterpolator<half4>::interp(info, x, y); case IMAGE_DATA_TYPE_BYTE4: @@ -605,19 +778,30 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); + if (UNLIKELY(!info.data)) { + return zero_float4(); + } + if (info.use_transform_3d) { P = transform_point(&info.transform_3d, P); } - switch (info.data_type) { - case IMAGE_DATA_TYPE_HALF: - return TextureInterpolator<half>::interp_3d(info, P.x, P.y, P.z, interp); - case IMAGE_DATA_TYPE_BYTE: - return TextureInterpolator<uchar>::interp_3d(info, P.x, P.y, P.z, interp); - case IMAGE_DATA_TYPE_USHORT: - return TextureInterpolator<uint16_t>::interp_3d(info, P.x, P.y, P.z, interp); - case IMAGE_DATA_TYPE_FLOAT: - return TextureInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp); + case IMAGE_DATA_TYPE_HALF: { + const float f = TextureInterpolator<half, float>::interp_3d(info, P.x, P.y, P.z, interp); + return make_float4(f, f, f, 1.0f); + } + case IMAGE_DATA_TYPE_BYTE: { + const float f = TextureInterpolator<uchar, float>::interp_3d(info, P.x, P.y, P.z, interp); + return make_float4(f, f, f, 1.0f); + } + case IMAGE_DATA_TYPE_USHORT: { + const float f = TextureInterpolator<uint16_t, float>::interp_3d(info, P.x, P.y, P.z, interp); + return make_float4(f, f, f, 1.0f); + } + case IMAGE_DATA_TYPE_FLOAT: { + const float f = TextureInterpolator<float, float>::interp_3d(info, P.x, P.y, P.z, interp); + return make_float4(f, f, f, 1.0f); + } case IMAGE_DATA_TYPE_HALF4: return TextureInterpolator<half4>::interp_3d(info, P.x, P.y, P.z, interp); case IMAGE_DATA_TYPE_BYTE4: @@ -627,8 +811,10 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, case IMAGE_DATA_TYPE_FLOAT4: return TextureInterpolator<float4>::interp_3d(info, P.x, P.y, P.z, interp); #ifdef WITH_NANOVDB - case IMAGE_DATA_TYPE_NANOVDB_FLOAT: - return NanoVDBInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp); + case IMAGE_DATA_TYPE_NANOVDB_FLOAT: { + const float f = NanoVDBInterpolator<float, float>::interp_3d(info, P.x, P.y, P.z, interp); + return make_float4(f, f, f, 1.0f); + } case IMAGE_DATA_TYPE_NANOVDB_FLOAT3: return NanoVDBInterpolator<nanovdb::Vec3f>::interp_3d(info, P.x, P.y, P.z, interp); #endif diff --git a/intern/cycles/kernel/device/cuda/compat.h b/intern/cycles/kernel/device/cuda/compat.h index d7365e631aa..b392455c740 100644 --- a/intern/cycles/kernel/device/cuda/compat.h +++ b/intern/cycles/kernel/device/cuda/compat.h @@ -72,7 +72,6 @@ typedef unsigned long long uint64_t; #define ccl_gpu_syncthreads() __syncthreads() #define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) -#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down_sync(mask, var, detla) /* GPU texture objects */ diff --git a/intern/cycles/kernel/device/gpu/parallel_reduce.h b/intern/cycles/kernel/device/gpu/parallel_reduce.h deleted file mode 100644 index 2b30dc9c666..00000000000 --- a/intern/cycles/kernel/device/gpu/parallel_reduce.h +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2021-2022 Blender Foundation */ - -#pragma once - -CCL_NAMESPACE_BEGIN - -/* Parallel sum of array input_data with size n into output_sum. - * - * Adapted from "Optimizing Parallel Reduction in GPU", Mark Harris. - * - * This version adds multiple elements per thread sequentially. This reduces - * the overall cost of the algorithm while keeping the work complexity O(n) and - * the step complexity O(log n). (Brent's Theorem optimization) */ - -#ifdef __HIP__ -# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 1024 -#else -# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512 -#endif - -template<uint blocksize, typename InputT, typename OutputT, typename ConvertOp> -__device__ void gpu_parallel_sum( - const InputT *input_data, const uint n, OutputT *output_sum, OutputT zero, ConvertOp convert) -{ - extern ccl_gpu_shared OutputT shared_data[]; - - const uint tid = ccl_gpu_thread_idx_x; - const uint gridsize = blocksize * ccl_gpu_grid_dim_x(); - - OutputT sum = zero; - for (uint i = ccl_gpu_block_idx_x * blocksize + tid; i < n; i += gridsize) { - sum += convert(input_data[i]); - } - shared_data[tid] = sum; - - ccl_gpu_syncthreads(); - - if (blocksize >= 512 && tid < 256) { - shared_data[tid] = sum = sum + shared_data[tid + 256]; - } - - ccl_gpu_syncthreads(); - - if (blocksize >= 256 && tid < 128) { - shared_data[tid] = sum = sum + shared_data[tid + 128]; - } - - ccl_gpu_syncthreads(); - - if (blocksize >= 128 && tid < 64) { - shared_data[tid] = sum = sum + shared_data[tid + 64]; - } - - ccl_gpu_syncthreads(); - - if (blocksize >= 64 && tid < 32) { - shared_data[tid] = sum = sum + shared_data[tid + 32]; - } - - ccl_gpu_syncthreads(); - - if (tid < 32) { - for (int offset = ccl_gpu_warp_size / 2; offset > 0; offset /= 2) { - sum += ccl_shfl_down_sync(0xFFFFFFFF, sum, offset); - } - } - - if (tid == 0) { - output_sum[ccl_gpu_block_idx_x] = sum; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h index 6e117b84337..29fbc119cd1 100644 --- a/intern/cycles/kernel/device/hip/compat.h +++ b/intern/cycles/kernel/device/hip/compat.h @@ -62,7 +62,7 @@ typedef unsigned long long uint64_t; #define ccl_gpu_block_idx_x (blockIdx.x) #define ccl_gpu_grid_dim_x (gridDim.x) #define ccl_gpu_warp_size (warpSize) -#define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp)) +#define ccl_gpu_thread_mask(thread_warp) uint64_t(0xFFFFFFFFFFFFFFFF >> (64 - thread_warp)) #define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) #define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) @@ -71,7 +71,6 @@ typedef unsigned long long uint64_t; #define ccl_gpu_syncthreads() __syncthreads() #define ccl_gpu_ballot(predicate) __ballot(predicate) -#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down(var, detla) /* GPU texture objects */ typedef hipTextureObject_t ccl_gpu_tex_object; diff --git a/intern/cycles/kernel/device/optix/compat.h b/intern/cycles/kernel/device/optix/compat.h index e7fe7139cc1..ae7a0309e51 100644 --- a/intern/cycles/kernel/device/optix/compat.h +++ b/intern/cycles/kernel/device/optix/compat.h @@ -74,7 +74,6 @@ typedef unsigned long long uint64_t; #define ccl_gpu_syncthreads() __syncthreads() #define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) -#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down_sync(mask, var, detla) /* GPU texture objects */ diff --git a/intern/cycles/kernel/film/accumulate.h b/intern/cycles/kernel/film/accumulate.h index 6345430e4f4..d6a385a4bff 100644 --- a/intern/cycles/kernel/film/accumulate.h +++ b/intern/cycles/kernel/film/accumulate.h @@ -352,6 +352,12 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg pass_offset = pass; } else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + /* Don't write any light passes for shadow catcher, for easier + * compositing back together of the combined pass. */ + if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) { + return; + } + if (path_flag & PATH_RAY_SURFACE_PASS) { /* Indirectly visible through reflection. */ const float3 diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight); @@ -437,6 +443,12 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, if (kernel_data.film.light_pass_flag & PASS_ANY) { const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); + /* Don't write any light passes for shadow catcher, for easier + * compositing back together of the combined pass. */ + if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) { + return; + } + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { int pass_offset = PASS_UNUSED; diff --git a/intern/cycles/kernel/film/read.h b/intern/cycles/kernel/film/read.h index 0931e14ee84..a0236909f4b 100644 --- a/intern/cycles/kernel/film/read.h +++ b/intern/cycles/kernel/film/read.h @@ -451,7 +451,7 @@ ccl_device_inline float4 film_calculate_shadow_catcher_matte_with_shadow( float scale, scale_exposure; if (!film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure)) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero_float4(); } ccl_global const float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte; diff --git a/intern/cycles/kernel/geom/curve.h b/intern/cycles/kernel/geom/curve.h index 79366f11082..4dbc6d4f6db 100644 --- a/intern/cycles/kernel/geom/curve.h +++ b/intern/cycles/kernel/geom/curve.h @@ -164,7 +164,7 @@ ccl_device float4 curve_attribute_float4(KernelGlobals kg, if (dx) *dx = sd->du.dx * (f1 - f0); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); # endif return (1.0f - sd->u) * f0 + sd->u * f1; @@ -172,9 +172,9 @@ ccl_device float4 curve_attribute_float4(KernelGlobals kg, else { # ifdef __RAY_DIFFERENTIALS__ if (dx) - *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dx = zero_float4(); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); # endif if (desc.element & (ATTR_ELEMENT_CURVE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) { @@ -183,7 +183,7 @@ ccl_device float4 curve_attribute_float4(KernelGlobals kg, return kernel_tex_fetch(__attributes_float4, offset); } else { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero_float4(); } } } diff --git a/intern/cycles/kernel/geom/patch.h b/intern/cycles/kernel/geom/patch.h index 9a006baf7bf..1c63a00e30d 100644 --- a/intern/cycles/kernel/geom/patch.h +++ b/intern/cycles/kernel/geom/patch.h @@ -391,11 +391,11 @@ ccl_device float4 patch_eval_float4(KernelGlobals kg, int num_control = patch_eval_control_verts( kg, sd->object, patch, u, v, channel, indices, weights, weights_du, weights_dv); - float4 val = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 val = zero_float4(); if (du) - *du = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *du = zero_float4(); if (dv) - *dv = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dv = zero_float4(); for (int i = 0; i < num_control; i++) { float4 v = kernel_tex_fetch(__attributes_float4, offset + indices[i]); @@ -428,11 +428,11 @@ ccl_device float4 patch_eval_uchar4(KernelGlobals kg, int num_control = patch_eval_control_verts( kg, sd->object, patch, u, v, channel, indices, weights, weights_du, weights_dv); - float4 val = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 val = zero_float4(); if (du) - *du = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *du = zero_float4(); if (dv) - *dv = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dv = zero_float4(); for (int i = 0; i < num_control; i++) { float4 v = color_srgb_to_linear_v4( diff --git a/intern/cycles/kernel/geom/point.h b/intern/cycles/kernel/geom/point.h index 041ecb3c2cf..ee7eca9e0c6 100644 --- a/intern/cycles/kernel/geom/point.h +++ b/intern/cycles/kernel/geom/point.h @@ -83,16 +83,16 @@ ccl_device float4 point_attribute_float4(KernelGlobals kg, { # ifdef __RAY_DIFFERENTIALS__ if (dx) - *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dx = zero_float4(); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); # endif if (desc.element == ATTR_ELEMENT_VERTEX) { return kernel_tex_fetch(__attributes_float4, desc.offset + sd->prim); } else { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero_float4(); } } diff --git a/intern/cycles/kernel/geom/primitive.h b/intern/cycles/kernel/geom/primitive.h index 63d1168364c..9b4b61fbd84 100644 --- a/intern/cycles/kernel/geom/primitive.h +++ b/intern/cycles/kernel/geom/primitive.h @@ -135,10 +135,10 @@ ccl_device_forceinline float4 primitive_surface_attribute_float4(KernelGlobals k #endif else { if (dx) - *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dx = zero_float4(); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); + return zero_float4(); } } @@ -187,7 +187,7 @@ ccl_device_inline float4 primitive_volume_attribute_float4(KernelGlobals kg, return volume_attribute_float4(kg, sd, desc); } else { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero_float4(); } } #endif diff --git a/intern/cycles/kernel/geom/subd_triangle.h b/intern/cycles/kernel/geom/subd_triangle.h index 0ff5292b5b5..24e1e454b8c 100644 --- a/intern/cycles/kernel/geom/subd_triangle.h +++ b/intern/cycles/kernel/geom/subd_triangle.h @@ -566,9 +566,9 @@ ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals kg, #endif /* __PATCH_EVAL__ */ if (desc.element == ATTR_ELEMENT_FACE) { if (dx) - *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dx = zero_float4(); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); return kernel_tex_fetch(__attributes_float4, desc.offset + subd_triangle_patch_face(kg, patch)); @@ -648,19 +648,19 @@ ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals kg, } else if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) { if (dx) - *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dx = zero_float4(); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); return kernel_tex_fetch(__attributes_float4, desc.offset); } else { if (dx) - *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dx = zero_float4(); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero_float4(); } } diff --git a/intern/cycles/kernel/geom/triangle.h b/intern/cycles/kernel/geom/triangle.h index 865bf549ae3..8ac7e67ff05 100644 --- a/intern/cycles/kernel/geom/triangle.h +++ b/intern/cycles/kernel/geom/triangle.h @@ -338,9 +338,9 @@ ccl_device float4 triangle_attribute_float4(KernelGlobals kg, else { #ifdef __RAY_DIFFERENTIALS__ if (dx) - *dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dx = zero_float4(); if (dy) - *dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + *dy = zero_float4(); #endif if (desc.element & (ATTR_ELEMENT_FACE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) { @@ -349,7 +349,7 @@ ccl_device float4 triangle_attribute_float4(KernelGlobals kg, return kernel_tex_fetch(__attributes_float4, offset); } else { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero_float4(); } } } diff --git a/intern/cycles/kernel/geom/volume.h b/intern/cycles/kernel/geom/volume.h index aa6f3b42bf2..22715dee5bf 100644 --- a/intern/cycles/kernel/geom/volume.h +++ b/intern/cycles/kernel/geom/volume.h @@ -75,7 +75,7 @@ ccl_device float4 volume_attribute_float4(KernelGlobals kg, return kernel_tex_image_interp_3d(kg, desc.offset, P, interp); } else { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + return zero_float4(); } } diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index 4cc933aff50..49ef01dc870 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -59,6 +59,8 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, /* Move ray forward. */ volume_ray.P = stack_sd->P; + volume_ray.self.object = isect.object; + volume_ray.self.prim = isect.prim; if (volume_ray.t != FLT_MAX) { volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t); } @@ -198,6 +200,8 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s /* Move ray forward. */ volume_ray.P = stack_sd->P; + volume_ray.self.object = isect.object; + volume_ray.self.prim = isect.prim; ++step; } #endif diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index d2442755646..df9af6ca107 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -352,13 +352,15 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, float ao_pdf; sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); + bool skip_self = true; + Ray ray ccl_optional_struct_init; - ray.P = shadow_ray_offset(kg, sd, ao_D); + ray.P = shadow_ray_offset(kg, sd, ao_D, &skip_self); ray.D = ao_D; ray.t = kernel_data.integrator.ao_bounces_distance; ray.time = sd->time; - ray.self.object = sd->object; - ray.self.prim = sd->prim; + ray.self.object = (skip_self) ? sd->object : OBJECT_NONE; + ray.self.prim = (skip_self) ? sd->prim : PRIM_NONE; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; ray.dP = differential_zero_compact(); diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 41882e4b3ee..5acfc92cca1 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -180,11 +180,9 @@ ccl_device_inline float3 shadow_ray_smooth_surface_offset( ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg, ccl_private const ShaderData *ccl_restrict sd, - float3 L) + float3 L, + ccl_private bool *r_skip_self) { - float NL = dot(sd->N, L); - bool transmit = (NL < 0.0f); - float3 Ng = (transmit ? -sd->Ng : sd->Ng); float3 P = sd->P; if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) { @@ -194,19 +192,25 @@ ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg, * offset_cutoff = 0.1f means that 10-20% of rays will be affected. Also * make a smooth transition near the threshold. */ if (offset_cutoff > 0.0f) { - float NgL = dot(Ng, L); - float offset_amount = 0.0f; + float NL = dot(sd->N, L); + const bool transmit = (NL < 0.0f); if (NL < 0) { NL = -NL; } - if (NL < offset_cutoff) { - offset_amount = clamp(2.0f - (NgL + NL) / offset_cutoff, 0.0f, 1.0f); - } - else { - offset_amount = clamp(1.0f - NgL / offset_cutoff, 0.0f, 1.0f); - } + + const float3 Ng = (transmit ? -sd->Ng : sd->Ng); + const float NgL = dot(Ng, L); + + const float offset_amount = (NL < offset_cutoff) ? + clamp(2.0f - (NgL + NL) / offset_cutoff, 0.0f, 1.0f) : + clamp(1.0f - NgL / offset_cutoff, 0.0f, 1.0f); + if (offset_amount > 0.0f) { P += shadow_ray_smooth_surface_offset(kg, sd, Ng) * offset_amount; + + /* Only skip self intersections if light direction and geometric normal point in the same + * direction, otherwise we're meant to hit this surface. */ + *r_skip_self = (NgL > 0.0f); } } } @@ -217,7 +221,8 @@ ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg, ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restrict sd, ccl_private const LightSample *ccl_restrict ls, const float3 P, - ccl_private Ray *ray) + ccl_private Ray *ray, + const bool skip_self) { if (ls->shader & SHADER_CAST_SHADOW) { /* setup ray */ @@ -246,10 +251,10 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri ray->time = sd->time; /* Fill in intersection surface and light details. */ - ray->self.prim = sd->prim; - ray->self.object = sd->object; - ray->self.light_prim = ls->prim; + ray->self.object = (skip_self) ? sd->object : OBJECT_NONE; + ray->self.prim = (skip_self) ? sd->prim : PRIM_NONE; ray->self.light_object = ls->object; + ray->self.light_prim = ls->prim; } /* Create shadow ray towards light sample. */ @@ -259,8 +264,9 @@ ccl_device_inline void light_sample_to_surface_shadow_ray( ccl_private const LightSample *ccl_restrict ls, ccl_private Ray *ray) { - const float3 P = shadow_ray_offset(kg, sd, ls->D); - shadow_ray_setup(sd, ls, P, ray); + bool skip_self = true; + const float3 P = shadow_ray_offset(kg, sd, ls->D, &skip_self); + shadow_ray_setup(sd, ls, P, ray, skip_self); } /* Create shadow ray towards light sample. */ @@ -271,7 +277,7 @@ ccl_device_inline void light_sample_to_volume_shadow_ray( const float3 P, ccl_private Ray *ray) { - shadow_ray_setup(sd, ls, P, ray); + shadow_ray_setup(sd, ls, P, ray, false); } ccl_device_inline float light_sample_mis_weight_forward(KernelGlobals kg, diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 79547872c68..832498f1f73 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -595,8 +595,8 @@ static bool set_attribute_float4(float4 f, TypeDesc type, bool derivatives, void float4 fv[3]; fv[0] = f; - fv[1] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - fv[2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + fv[1] = zero_float4(); + fv[2] = zero_float4(); return set_attribute_float4(fv, type, derivatives, val); } diff --git a/intern/cycles/kernel/osl/shaders/node_float_curve.osl b/intern/cycles/kernel/osl/shaders/node_float_curve.osl index 265a21bd4aa..70de1217877 100644 --- a/intern/cycles/kernel/osl/shaders/node_float_curve.osl +++ b/intern/cycles/kernel/osl/shaders/node_float_curve.osl @@ -7,13 +7,15 @@ shader node_float_curve(float ramp[] = {0.0}, float min_x = 0.0, float max_x = 1.0, + int extrapolate = 1, + float ValueIn = 0.0, float Factor = 0.0, output float ValueOut = 0.0) { float c = (ValueIn - min_x) / (max_x - min_x); - ValueOut = rgb_ramp_lookup(ramp, c, 1, 1); + ValueOut = rgb_ramp_lookup(ramp, c, 1, extrapolate); ValueOut = mix(ValueIn, ValueOut, Factor); } diff --git a/intern/cycles/kernel/osl/shaders/node_rgb_curves.osl b/intern/cycles/kernel/osl/shaders/node_rgb_curves.osl index b73c74801ee..388fdd05b70 100644 --- a/intern/cycles/kernel/osl/shaders/node_rgb_curves.osl +++ b/intern/cycles/kernel/osl/shaders/node_rgb_curves.osl @@ -7,6 +7,7 @@ shader node_rgb_curves(color ramp[] = {0.0}, float min_x = 0.0, float max_x = 1.0, + int extrapolate = 1, color ColorIn = 0.0, float Fac = 0.0, @@ -14,9 +15,9 @@ shader node_rgb_curves(color ramp[] = {0.0}, { color c = (ColorIn - color(min_x, min_x, min_x)) / (max_x - min_x); - color r = rgb_ramp_lookup(ramp, c[0], 1, 1); - color g = rgb_ramp_lookup(ramp, c[1], 1, 1); - color b = rgb_ramp_lookup(ramp, c[2], 1, 1); + color r = rgb_ramp_lookup(ramp, c[0], 1, extrapolate); + color g = rgb_ramp_lookup(ramp, c[1], 1, extrapolate); + color b = rgb_ramp_lookup(ramp, c[2], 1, extrapolate); ColorOut[0] = r[0]; ColorOut[1] = g[1]; diff --git a/intern/cycles/kernel/osl/shaders/node_vector_curves.osl b/intern/cycles/kernel/osl/shaders/node_vector_curves.osl index a3bf1209e6e..3ef720ad6d6 100644 --- a/intern/cycles/kernel/osl/shaders/node_vector_curves.osl +++ b/intern/cycles/kernel/osl/shaders/node_vector_curves.osl @@ -7,6 +7,7 @@ shader node_vector_curves(color ramp[] = {0.0}, float min_x = 0.0, float max_x = 1.0, + int extrapolate = 1, vector VectorIn = vector(0.0, 0.0, 0.0), float Fac = 0.0, @@ -14,9 +15,9 @@ shader node_vector_curves(color ramp[] = {0.0}, { vector c = (VectorIn - vector(min_x, min_x, min_x)) / (max_x - min_x); - color r = rgb_ramp_lookup(ramp, c[0], 1, 1); - color g = rgb_ramp_lookup(ramp, c[0], 1, 1); - color b = rgb_ramp_lookup(ramp, c[0], 1, 1); + color r = rgb_ramp_lookup(ramp, c[0], 1, extrapolate); + color g = rgb_ramp_lookup(ramp, c[0], 1, extrapolate); + color b = rgb_ramp_lookup(ramp, c[0], 1, extrapolate); VectorOut[0] = r[0]; VectorOut[1] = g[1]; diff --git a/intern/cycles/kernel/svm/image.h b/intern/cycles/kernel/svm/image.h index e9669800f4c..31f29531740 100644 --- a/intern/cycles/kernel/svm/image.h +++ b/intern/cycles/kernel/svm/image.h @@ -186,7 +186,7 @@ ccl_device_noinline void svm_node_tex_image_box(KernelGlobals kg, float3 co = stack_load_float3(stack, co_offset); uint id = node.y; - float4 f = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 f = zero_float4(); /* Map so that no textures are flipped, rotation is somewhat arbitrary. */ if (weight.x > 0.0f) { diff --git a/intern/cycles/kernel/svm/ramp.h b/intern/cycles/kernel/svm/ramp.h index aba90284a2e..342b15da9ed 100644 --- a/intern/cycles/kernel/svm/ramp.h +++ b/intern/cycles/kernel/svm/ramp.h @@ -102,8 +102,8 @@ ccl_device_noinline int svm_node_rgb_ramp( ccl_device_noinline int svm_node_curves( KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { - uint fac_offset, color_offset, out_offset; - svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &out_offset); + uint fac_offset, color_offset, out_offset, extrapolate; + svm_unpack_node_uchar4(node.y, &fac_offset, &color_offset, &out_offset, &extrapolate); uint table_size = read_node(kg, &offset).x; @@ -114,9 +114,9 @@ ccl_device_noinline int svm_node_curves( const float range_x = max_x - min_x; const float3 relpos = (color - make_float3(min_x, min_x, min_x)) / range_x; - float r = rgb_ramp_lookup(kg, offset, relpos.x, true, true, table_size).x; - float g = rgb_ramp_lookup(kg, offset, relpos.y, true, true, table_size).y; - float b = rgb_ramp_lookup(kg, offset, relpos.z, true, true, table_size).z; + float r = rgb_ramp_lookup(kg, offset, relpos.x, true, extrapolate, table_size).x; + float g = rgb_ramp_lookup(kg, offset, relpos.y, true, extrapolate, table_size).y; + float b = rgb_ramp_lookup(kg, offset, relpos.z, true, extrapolate, table_size).z; color = (1.0f - fac) * color + fac * make_float3(r, g, b); stack_store_float3(stack, out_offset, color); @@ -128,8 +128,8 @@ ccl_device_noinline int svm_node_curves( ccl_device_noinline int svm_node_curve( KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { - uint fac_offset, value_in_offset, out_offset; - svm_unpack_node_uchar3(node.y, &fac_offset, &value_in_offset, &out_offset); + uint fac_offset, value_in_offset, out_offset, extrapolate; + svm_unpack_node_uchar4(node.y, &fac_offset, &value_in_offset, &out_offset, &extrapolate); uint table_size = read_node(kg, &offset).x; @@ -140,7 +140,7 @@ ccl_device_noinline int svm_node_curve( const float range = max - min; const float relpos = (in - min) / range; - float v = float_ramp_lookup(kg, offset, relpos, true, true, table_size); + float v = float_ramp_lookup(kg, offset, relpos, true, extrapolate, table_size); in = (1.0f - fac) * in + fac * v; stack_store_float(stack, out_offset, in); diff --git a/intern/cycles/kernel/svm/vertex_color.h b/intern/cycles/kernel/svm/vertex_color.h index 231c388618c..bf8d5677114 100644 --- a/intern/cycles/kernel/svm/vertex_color.h +++ b/intern/cycles/kernel/svm/vertex_color.h @@ -14,9 +14,16 @@ ccl_device_noinline void svm_node_vertex_color(KernelGlobals kg, { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { - float4 vertex_color = primitive_surface_attribute_float4(kg, sd, descriptor, NULL, NULL); - stack_store_float3(stack, color_offset, float4_to_float3(vertex_color)); - stack_store_float(stack, alpha_offset, vertex_color.w); + if (descriptor.type == NODE_ATTR_FLOAT4 || descriptor.type == NODE_ATTR_RGBA) { + float4 vertex_color = primitive_surface_attribute_float4(kg, sd, descriptor, NULL, NULL); + stack_store_float3(stack, color_offset, float4_to_float3(vertex_color)); + stack_store_float(stack, alpha_offset, vertex_color.w); + } + else { + float3 vertex_color = primitive_surface_attribute_float3(kg, sd, descriptor, NULL, NULL); + stack_store_float3(stack, color_offset, vertex_color); + stack_store_float(stack, alpha_offset, 1.0f); + } } else { stack_store_float3(stack, color_offset, make_float3(0.0f, 0.0f, 0.0f)); @@ -33,11 +40,20 @@ ccl_device_noinline void svm_node_vertex_color_bump_dx(KernelGlobals kg, { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { - float4 dx; - float4 vertex_color = primitive_surface_attribute_float4(kg, sd, descriptor, &dx, NULL); - vertex_color += dx; - stack_store_float3(stack, color_offset, float4_to_float3(vertex_color)); - stack_store_float(stack, alpha_offset, vertex_color.w); + if (descriptor.type == NODE_ATTR_FLOAT4 || descriptor.type == NODE_ATTR_RGBA) { + float4 dx; + float4 vertex_color = primitive_surface_attribute_float4(kg, sd, descriptor, &dx, NULL); + vertex_color += dx; + stack_store_float3(stack, color_offset, float4_to_float3(vertex_color)); + stack_store_float(stack, alpha_offset, vertex_color.w); + } + else { + float3 dx; + float3 vertex_color = primitive_surface_attribute_float3(kg, sd, descriptor, &dx, NULL); + vertex_color += dx; + stack_store_float3(stack, color_offset, vertex_color); + stack_store_float(stack, alpha_offset, 1.0f); + } } else { stack_store_float3(stack, color_offset, make_float3(0.0f, 0.0f, 0.0f)); @@ -54,11 +70,20 @@ ccl_device_noinline void svm_node_vertex_color_bump_dy(KernelGlobals kg, { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { - float4 dy; - float4 vertex_color = primitive_surface_attribute_float4(kg, sd, descriptor, NULL, &dy); - vertex_color += dy; - stack_store_float3(stack, color_offset, float4_to_float3(vertex_color)); - stack_store_float(stack, alpha_offset, vertex_color.w); + if (descriptor.type == NODE_ATTR_FLOAT4 || descriptor.type == NODE_ATTR_RGBA) { + float4 dy; + float4 vertex_color = primitive_surface_attribute_float4(kg, sd, descriptor, NULL, &dy); + vertex_color += dy; + stack_store_float3(stack, color_offset, float4_to_float3(vertex_color)); + stack_store_float(stack, alpha_offset, vertex_color.w); + } + else { + float3 dy; + float3 vertex_color = primitive_surface_attribute_float3(kg, sd, descriptor, NULL, &dy); + vertex_color += dy; + stack_store_float3(stack, color_offset, vertex_color); + stack_store_float(stack, alpha_offset, 1.0f); + } } else { stack_store_float3(stack, color_offset, make_float3(0.0f, 0.0f, 0.0f)); diff --git a/intern/cycles/kernel/svm/voronoi.h b/intern/cycles/kernel/svm/voronoi.h index 8afd7cc9b5f..4ff1047aab7 100644 --- a/intern/cycles/kernel/svm/voronoi.h +++ b/intern/cycles/kernel/svm/voronoi.h @@ -684,8 +684,8 @@ ccl_device void voronoi_f1_4d(float4 coord, float4 localPosition = coord - cellPosition; float minDistance = 8.0f; - float4 targetOffset = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - float4 targetPosition = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 targetOffset = zero_float4(); + float4 targetPosition = zero_float4(); for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { ccl_loop_no_unroll for (int j = -1; j <= 1; j++) @@ -724,7 +724,7 @@ ccl_device void voronoi_smooth_f1_4d(float4 coord, float smoothDistance = 8.0f; float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f); - float4 smoothPosition = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 smoothPosition = zero_float4(); for (int u = -2; u <= 2; u++) { for (int k = -2; k <= 2; k++) { ccl_loop_no_unroll for (int j = -2; j <= 2; j++) @@ -765,10 +765,10 @@ ccl_device void voronoi_f2_4d(float4 coord, float distanceF1 = 8.0f; float distanceF2 = 8.0f; - float4 offsetF1 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - float4 positionF1 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - float4 offsetF2 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - float4 positionF2 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 offsetF1 = zero_float4(); + float4 positionF1 = zero_float4(); + float4 offsetF2 = zero_float4(); + float4 positionF2 = zero_float4(); for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { ccl_loop_no_unroll for (int j = -1; j <= 1; j++) @@ -808,7 +808,7 @@ ccl_device void voronoi_distance_to_edge_4d(float4 coord, float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; - float4 vectorToClosest = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 vectorToClosest = zero_float4(); float minDistance = 8.0f; for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { @@ -859,8 +859,8 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; - float4 closestPoint = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - float4 closestPointOffset = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 closestPoint = zero_float4(); + float4 closestPointOffset = zero_float4(); float minDistance = 8.0f; for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { @@ -882,7 +882,7 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, } minDistance = 8.0f; - float4 closestPointToClosestPoint = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 closestPointToClosestPoint = zero_float4(); for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { ccl_loop_no_unroll for (int j = -1; j <= 1; j++) diff --git a/intern/cycles/kernel/svm/voxel.h b/intern/cycles/kernel/svm/voxel.h index c5f6a6f004a..553a00cd09a 100644 --- a/intern/cycles/kernel/svm/voxel.h +++ b/intern/cycles/kernel/svm/voxel.h @@ -30,7 +30,7 @@ ccl_device_noinline int svm_node_tex_voxel( float4 r = kernel_tex_image_interp_3d(kg, id, co, INTERPOLATION_NONE); #else - float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 r = zero_float4(); #endif if (stack_valid(density_out_offset)) stack_store_float(stack, density_out_offset, r.w); diff --git a/intern/cycles/scene/integrator.cpp b/intern/cycles/scene/integrator.cpp index 64c95538f82..fd559178073 100644 --- a/intern/cycles/scene/integrator.cpp +++ b/intern/cycles/scene/integrator.cpp @@ -109,8 +109,10 @@ NODE_DEFINE(Integrator) SOCKET_INT(denoise_start_sample, "Start Sample to Denoise", 0); SOCKET_BOOLEAN(use_denoise_pass_albedo, "Use Albedo Pass for Denoiser", true); SOCKET_BOOLEAN(use_denoise_pass_normal, "Use Normal Pass for Denoiser", true); - SOCKET_ENUM( - denoiser_prefilter, "Denoiser Type", denoiser_prefilter_enum, DENOISER_PREFILTER_ACCURATE); + SOCKET_ENUM(denoiser_prefilter, + "Denoiser Prefilter", + denoiser_prefilter_enum, + DENOISER_PREFILTER_ACCURATE); return type; } diff --git a/intern/cycles/scene/mesh.cpp b/intern/cycles/scene/mesh.cpp index a459195efee..05024a7790e 100644 --- a/intern/cycles/scene/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -142,8 +142,8 @@ NODE_DEFINE(Mesh) SOCKET_INT(num_ngons, "NGons Number", 0); /* Subdivisions parameters */ - SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 0.0f) - SOCKET_INT(subd_max_level, "Subdivision Dicing Rate", 0); + SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f) + SOCKET_INT(subd_max_level, "Max Subdivision Level", 1); SOCKET_TRANSFORM(subd_objecttoworld, "Subdivision Object Transform", transform_identity()); return type; @@ -357,7 +357,7 @@ void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_) } } -void Mesh::add_subd_face(int *corners, int num_corners, int shader_, bool smooth_) +void Mesh::add_subd_face(const int *corners, int num_corners, int shader_, bool smooth_) { int start_corner = subd_face_corners.size(); @@ -411,8 +411,6 @@ void Mesh::add_edge_crease(int v0, int v1, float weight) void Mesh::add_vertex_crease(int v, float weight) { - assert(v < verts.size()); - subd_vert_creases.push_back_slow(v); subd_vert_creases_weight.push_back_slow(weight); diff --git a/intern/cycles/scene/mesh.h b/intern/cycles/scene/mesh.h index 4e6991a3960..a09f192d969 100644 --- a/intern/cycles/scene/mesh.h +++ b/intern/cycles/scene/mesh.h @@ -199,7 +199,7 @@ class Mesh : public Geometry { void add_vertex(float3 P); void add_vertex_slow(float3 P); void add_triangle(int v0, int v1, int v2, int shader, bool smooth); - void add_subd_face(int *corners, int num_corners, int shader_, bool smooth_); + void add_subd_face(const int *corners, int num_corners, int shader_, bool smooth_); void add_edge_crease(int v0, int v1, float weight); void add_vertex_crease(int v, float weight); diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 272a0dde7d8..a951a558731 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -6529,9 +6529,9 @@ void CurvesNode::constant_fold(const ConstantFolder &folder, ShaderInput *value_ float3 pos = (value - make_float3(min_x, min_x, min_x)) / (max_x - min_x); float3 result; - result[0] = rgb_ramp_lookup(curves.data(), pos[0], true, true, curves.size()).x; - result[1] = rgb_ramp_lookup(curves.data(), pos[1], true, true, curves.size()).y; - result[2] = rgb_ramp_lookup(curves.data(), pos[2], true, true, curves.size()).z; + result[0] = rgb_ramp_lookup(curves.data(), pos[0], true, extrapolate, curves.size()).x; + result[1] = rgb_ramp_lookup(curves.data(), pos[1], true, extrapolate, curves.size()).y; + result[2] = rgb_ramp_lookup(curves.data(), pos[2], true, extrapolate, curves.size()).z; folder.make_constant(interp(value, result, fac)); } @@ -6555,7 +6555,8 @@ void CurvesNode::compile(SVMCompiler &compiler, compiler.add_node(type, compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(value_in), - compiler.stack_assign(value_out)), + compiler.stack_assign(value_out), + extrapolate), __float_as_int(min_x), __float_as_int(max_x)); @@ -6572,6 +6573,7 @@ void CurvesNode::compile(OSLCompiler &compiler, const char *name) compiler.parameter_color_array("ramp", curves); compiler.parameter(this, "min_x"); compiler.parameter(this, "max_x"); + compiler.parameter(this, "extrapolate"); compiler.add(this, name); } @@ -6594,6 +6596,7 @@ NODE_DEFINE(RGBCurvesNode) SOCKET_COLOR_ARRAY(curves, "Curves", array<float3>()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); + SOCKET_BOOLEAN(extrapolate, "Extrapolate", true); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_IN_COLOR(value, "Color", zero_float3()); @@ -6631,6 +6634,7 @@ NODE_DEFINE(VectorCurvesNode) SOCKET_VECTOR_ARRAY(curves, "Curves", array<float3>()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); + SOCKET_BOOLEAN(extrapolate, "Extrapolate", true); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_IN_VECTOR(value, "Vector", zero_float3()); @@ -6668,6 +6672,7 @@ NODE_DEFINE(FloatCurveNode) SOCKET_FLOAT_ARRAY(curve, "Curve", array<float>()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); + SOCKET_BOOLEAN(extrapolate, "Extrapolate", true); SOCKET_IN_FLOAT(fac, "Factor", 0.0f); SOCKET_IN_FLOAT(value, "Value", 0.0f); @@ -6693,7 +6698,7 @@ void FloatCurveNode::constant_fold(const ConstantFolder &folder) } float pos = (value - min_x) / (max_x - min_x); - float result = float_ramp_lookup(curve.data(), pos, true, true, curve.size()); + float result = float_ramp_lookup(curve.data(), pos, true, extrapolate, curve.size()); folder.make_constant(value + fac * (result - value)); } @@ -6716,7 +6721,8 @@ void FloatCurveNode::compile(SVMCompiler &compiler) compiler.add_node(NODE_FLOAT_CURVE, compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(value_in), - compiler.stack_assign(value_out)), + compiler.stack_assign(value_out), + extrapolate), __float_as_int(min_x), __float_as_int(max_x)); @@ -6733,6 +6739,7 @@ void FloatCurveNode::compile(OSLCompiler &compiler) compiler.parameter_array("ramp", curve.data(), curve.size()); compiler.parameter(this, "min_x"); compiler.parameter(this, "max_x"); + compiler.parameter(this, "extrapolate"); compiler.add(this, "node_float_curve"); } diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index ef9e8772961..9aef5d3151f 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -1391,6 +1391,7 @@ class CurvesNode : public ShaderNode { NODE_SOCKET_API(float, max_x) NODE_SOCKET_API(float, fac) NODE_SOCKET_API(float3, value) + NODE_SOCKET_API(bool, extrapolate) protected: using ShaderNode::constant_fold; @@ -1421,6 +1422,7 @@ class FloatCurveNode : public ShaderNode { NODE_SOCKET_API(float, max_x) NODE_SOCKET_API(float, fac) NODE_SOCKET_API(float, value) + NODE_SOCKET_API(bool, extrapolate) }; class RGBRampNode : public ShaderNode { diff --git a/intern/cycles/session/session.h b/intern/cycles/session/session.h index 3e90b41e3e9..d431c61a5fc 100644 --- a/intern/cycles/session/session.h +++ b/intern/cycles/session/session.h @@ -54,6 +54,8 @@ class SessionParams { bool use_auto_tile; int tile_size; + bool use_resolution_divider; + ShadingSystem shadingsystem; /* Session-specific temporary directory to store in-progress EXR files in. */ @@ -76,6 +78,8 @@ class SessionParams { use_auto_tile = true; tile_size = 2048; + use_resolution_divider = true; + shadingsystem = SHADINGSYSTEM_SVM; } diff --git a/intern/cycles/session/tile.cpp b/intern/cycles/session/tile.cpp index 755e85450a3..82272a7dbf5 100644 --- a/intern/cycles/session/tile.cpp +++ b/intern/cycles/session/tile.cpp @@ -43,7 +43,6 @@ static std::vector<std::string> exr_channel_names_for_passes(const BufferParams static const char *component_suffixes[] = {"R", "G", "B", "A"}; int pass_index = 0; - int num_channels = 0; std::vector<std::string> channel_names; for (const BufferPass &pass : buffer_params.passes) { if (pass.offset == PASS_UNUSED) { @@ -51,7 +50,6 @@ static std::vector<std::string> exr_channel_names_for_passes(const BufferParams } const PassInfo pass_info = pass.get_info(); - num_channels += pass_info.num_components; /* EXR canonically expects first part of channel names to be sorted alphabetically, which is * not guaranteed to be the case with passes names. Assign a prefix based on the pass index diff --git a/intern/cycles/util/math_float4.h b/intern/cycles/util/math_float4.h index 9f4a1a904b5..ae9dfe75a9c 100644 --- a/intern/cycles/util/math_float4.h +++ b/intern/cycles/util/math_float4.h @@ -297,7 +297,7 @@ ccl_device_inline float4 cross(const float4 &a, const float4 &b) ccl_device_inline bool is_zero(const float4 &a) { # ifdef __KERNEL_SSE__ - return a == make_float4(0.0f); + return a == zero_float4(); # else return (a.x == 0.0f && a.y == 0.0f && a.z == 0.0f && a.w == 0.0f); # endif @@ -458,7 +458,7 @@ ccl_device_inline float4 select(const int4 &mask, const float4 &a, const float4 ccl_device_inline float4 mask(const int4 &mask, const float4 &a) { /* Replace elements of x with zero where mask isn't set. */ - return select(mask, a, make_float4(0.0f)); + return select(mask, a, zero_float4()); } ccl_device_inline float4 reduce_min(const float4 &a) diff --git a/intern/cycles/util/math_matrix.h b/intern/cycles/util/math_matrix.h index a4318fda6e8..b10d9b3c938 100644 --- a/intern/cycles/util/math_matrix.h +++ b/intern/cycles/util/math_matrix.h @@ -376,7 +376,7 @@ ccl_device void math_matrix_jacobi_eigendecomposition(ccl_private float *A, ccl_device_inline void math_vector_zero_sse(float4 *A, int n) { for (int i = 0; i < n; i++) { - A[i] = make_float4(0.0f); + A[i] = zero_float4(); } } @@ -384,7 +384,7 @@ ccl_device_inline void math_matrix_zero_sse(float4 *A, int n) { for (int row = 0; row < n; row++) { for (int col = 0; col <= row; col++) { - MAT(A, n, row, col) = make_float4(0.0f); + MAT(A, n, row, col) = zero_float4(); } } } diff --git a/intern/cycles/util/tbb.h b/intern/cycles/util/tbb.h index 2c26c3a5170..7105ddda0f8 100644 --- a/intern/cycles/util/tbb.h +++ b/intern/cycles/util/tbb.h @@ -16,6 +16,7 @@ #if TBB_INTERFACE_VERSION_MAJOR >= 10 # define WITH_TBB_GLOBAL_CONTROL +# define TBB_PREVIEW_GLOBAL_CONTROL 1 # include <tbb/global_control.h> #endif diff --git a/intern/dualcon/intern/octree.cpp b/intern/dualcon/intern/octree.cpp index 9e360848e6b..18242d48292 100644 --- a/intern/dualcon/intern/octree.cpp +++ b/intern/dualcon/intern/octree.cpp @@ -242,6 +242,10 @@ void Octree::printMemUsage() dc_printf("Total allocated bytes on disk: %d \n", totalbytes); dc_printf("Total leaf nodes: %d\n", totalLeafs); + + /* Unused when not debuggining. */ + (void)totalbytes; + (void)totalLeafs; } void Octree::resetMinimalEdges() diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 4e48a908c00..a82f634183d 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -146,7 +146,7 @@ extern void GHOST_GetAllDisplayDimensions(GHOST_SystemHandle systemhandle, * The new window is added to the list of windows managed. * Never explicitly delete the window, use disposeWindow() instead. * \param systemhandle: The handle to the system. - * \param parentWindow: Handle of parent (or owner) window, or NULL + * \param parent_windowhandle: Handle of parent (or owner) window, or NULL * \param title: The name of the window. * (displayed in the title bar of the window if the OS supports it). * \param left: The coordinate of the left edge of the window. @@ -175,7 +175,7 @@ extern GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle, * Create a new off-screen context. * Never explicitly delete the context, use #disposeContext() instead. * \param systemhandle: The handle to the system. - * \param platform_support_callback: An optional callback to check platform support. + * \param glSettings: Misc OpenGL options. * \return A handle to the new context ( == NULL if creation failed). */ extern GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle, diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index 9322b6cee7a..dd800ef52a3 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.mm +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -7,6 +7,12 @@ * Definition of GHOST_ContextCGL class. */ +/* Don't generate OpenGL deprecation warning. This is a known thing, and is not something easily + * solvable in a short term. */ +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + #include "GHOST_ContextCGL.h" #include <Cocoa/Cocoa.h> diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp index 702c10d377b..c3fcd7214ca 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.cpp +++ b/intern/ghost/intern/GHOST_ImeWin32.cpp @@ -88,9 +88,14 @@ bool GHOST_ImeWin32::IsImeKeyEvent(char ascii, GHOST_TKey key) if (IsLanguage(IMELANG_JAPANESE) && (ascii >= ' ' && ascii <= '~')) { return true; } - else if (IsLanguage(IMELANG_CHINESE) && ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii) && - !(key == GHOST_kKeyNumpadPeriod)) { - return true; + if (IsLanguage(IMELANG_CHINESE)) { + if (ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii) && !(key == GHOST_kKeyNumpadPeriod)) { + return true; + } + if (conversion_modes_ & IME_CMODE_FULLSHAPE && (ascii >= '0' && ascii <= '9')) { + /* When in Full Width mode the number keys are also converted. */ + return true; + } } } return false; diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index f0db6b6fdfc..b6836614962 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1050,8 +1050,6 @@ void GHOST_SystemCocoa::notifyExternalEventProcessed() GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa *window) { - NSArray *windowsList; - windowsList = [NSApp orderedWindows]; if (!validWindow(window)) { return GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 0f4b5147082..11a3c097958 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -151,6 +151,12 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, ::SetWindowPos(m_hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } + if (parentwindow) { + /* Release any parent capture to allow immediate interaction (T90110). */ + ::ReleaseCapture(); + parentwindow->lostMouseCapture(); + } + /* Show the window. */ int nCmdShow; switch (state) { diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index 79b4e9d0cf6..d5f47871aff 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -206,9 +206,7 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TSuccess endProgressBar(); /** - * Register a mouse capture state (should be called - * for any real button press, controls mouse - * capturing). + * Set or Release mouse capture (should be called for any real button press). * * \param event: Whether mouse was pressed and released, * or an operator grabbed or ungrabbed the mouse. @@ -216,8 +214,9 @@ class GHOST_WindowWin32 : public GHOST_Window { void updateMouseCapture(GHOST_MouseCaptureEventWin32 event); /** - * Inform the window that it has lost mouse capture, - * called in response to native window system messages. + * Inform the window that it has lost mouse capture, called in response to native window system + * messages (WA_INACTIVE, WM_CAPTURECHANGED) or if ReleaseCapture() is explicitly called (for new + * window creation). */ void lostMouseCapture(); diff --git a/intern/ghost/intern/GHOST_XrControllerModel.cpp b/intern/ghost/intern/GHOST_XrControllerModel.cpp index 78d81651e92..5be3a8e70ad 100644 --- a/intern/ghost/intern/GHOST_XrControllerModel.cpp +++ b/intern/ghost/intern/GHOST_XrControllerModel.cpp @@ -234,7 +234,7 @@ static void calc_node_transforms(const tinygltf::Node &gltf_node, {(float)dm[4], (float)dm[5], (float)dm[6], (float)dm[7]}, {(float)dm[8], (float)dm[9], (float)dm[10], (float)dm[11]}, {(float)dm[12], (float)dm[13], (float)dm[14], (float)dm[15]}}; - memcpy(r_local_transform, m, sizeof(float) * 16); + memcpy(r_local_transform, m, sizeof(float[4][4])); } else { /* No matrix is present, so construct a matrix from the TRS values (each one is optional). */ diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index d8a8c1181e4..8cd2c9f94dd 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -279,6 +279,24 @@ template<typename T> inline T *MEM_cnew(const char *allocation_name) } /** + * Allocate memory for an object of type #T and copy construct an object from `other`. + * Only applicable for a trivial types. + * + * This function works around problem of copy-constructing DNA structs which contains deprecated + * fields: some compilers will generate access deprecated field in implicitly defined copy + * constructors. + * + * This is a better alternative to #MEM_dupallocN. + */ +template<typename T> inline T *MEM_cnew(const char *allocation_name, const T &other) +{ + static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new should be used."); + T *new_object = static_cast<T *>(MEM_mallocN(sizeof(T), allocation_name)); + memcpy(new_object, &other, sizeof(T)); + return new_object; +} + +/** * Destructs and deallocates an object previously allocated with any `MEM_*` function. * Passing in null does nothing. */ diff --git a/intern/itasc/Armature.cpp b/intern/itasc/Armature.cpp index f365d40e9f1..e6968faf19a 100644 --- a/intern/itasc/Armature.cpp +++ b/intern/itasc/Armature.cpp @@ -1,12 +1,9 @@ -/** \file itasc/Armature.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file * \ingroup intern_itasc */ -/* - * Armature.cpp - * - * Created on: Feb 3, 2009 - * Author: benoitbolsee - */ #include "Armature.hpp" #include <algorithm> diff --git a/intern/itasc/Armature.hpp b/intern/itasc/Armature.hpp index 3167247ab03..ce47869c55b 100644 --- a/intern/itasc/Armature.hpp +++ b/intern/itasc/Armature.hpp @@ -1,8 +1,8 @@ -/* - * Armature.hpp - * - * Created on: Feb 3, 2009 - * Author: benoitbolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file + * \ingroup intern_itasc */ #ifndef ARMATURE_HPP_ diff --git a/intern/itasc/Cache.cpp b/intern/itasc/Cache.cpp index 28e6fbd22b1..1d5e63ba298 100644 --- a/intern/itasc/Cache.cpp +++ b/intern/itasc/Cache.cpp @@ -1,12 +1,10 @@ -/** \file itasc/Cache.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file * \ingroup intern_itasc */ -/* - * Cache.cpp - * - * Created on: Feb 24, 2009 - * Author: benoit bolsee - */ + #include <string.h> #include <assert.h> #include <math.h> diff --git a/intern/itasc/Cache.hpp b/intern/itasc/Cache.hpp index d461bb32fa8..27f95f16ec4 100644 --- a/intern/itasc/Cache.hpp +++ b/intern/itasc/Cache.hpp @@ -1,8 +1,8 @@ -/* - * Cache.hpp - * - * Created on: Feb 24, 2009 - * Author: benoit tbolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file + * \ingroup intern_itasc */ #ifndef CACHE_HPP_ diff --git a/intern/itasc/ConstraintSet.cpp b/intern/itasc/ConstraintSet.cpp index fda1ee9d70d..f731f4c5a5f 100644 --- a/intern/itasc/ConstraintSet.cpp +++ b/intern/itasc/ConstraintSet.cpp @@ -1,12 +1,9 @@ -/** \file itasc/ConstraintSet.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file * \ingroup intern_itasc */ -/* - * ConstraintSet.cpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits - */ #include "ConstraintSet.hpp" #include "kdl/utilities/svd_eigen_HH.hpp" diff --git a/intern/itasc/ConstraintSet.hpp b/intern/itasc/ConstraintSet.hpp index bf29190a050..ad92a8d9779 100644 --- a/intern/itasc/ConstraintSet.hpp +++ b/intern/itasc/ConstraintSet.hpp @@ -1,8 +1,8 @@ -/* - * ConstraintSet.hpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef CONSTRAINTSET_HPP_ diff --git a/intern/itasc/ControlledObject.cpp b/intern/itasc/ControlledObject.cpp index bb2e4117361..231a96d7813 100644 --- a/intern/itasc/ControlledObject.cpp +++ b/intern/itasc/ControlledObject.cpp @@ -1,12 +1,9 @@ -/** \file itasc/ControlledObject.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file * \ingroup intern_itasc */ -/* - * ControlledObject.cpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits - */ #include "ControlledObject.hpp" diff --git a/intern/itasc/ControlledObject.hpp b/intern/itasc/ControlledObject.hpp index 4530bf9823b..8ffb68cfb79 100644 --- a/intern/itasc/ControlledObject.hpp +++ b/intern/itasc/ControlledObject.hpp @@ -1,8 +1,8 @@ -/* - * ControlledObject.hpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef CONTROLLEDOBJECT_HPP_ diff --git a/intern/itasc/CopyPose.cpp b/intern/itasc/CopyPose.cpp index a9b6d3cd680..9b4ff5cb6a0 100644 --- a/intern/itasc/CopyPose.cpp +++ b/intern/itasc/CopyPose.cpp @@ -1,12 +1,9 @@ -/** \file itasc/CopyPose.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file * \ingroup intern_itasc */ -/* - * CopyPose.cpp - * - * Created on: Mar 17, 2009 - * Author: benoit bolsee - */ #include "CopyPose.hpp" #include "kdl/kinfam_io.hpp" diff --git a/intern/itasc/CopyPose.hpp b/intern/itasc/CopyPose.hpp index fd8a5451310..92966b85b6f 100644 --- a/intern/itasc/CopyPose.hpp +++ b/intern/itasc/CopyPose.hpp @@ -1,8 +1,8 @@ -/* - * CopyPose.h - * - * Created on: Mar 17, 2009 - * Author: benoit bolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file + * \ingroup intern_itasc */ #ifndef COPYPOSE_H_ diff --git a/intern/itasc/Distance.cpp b/intern/itasc/Distance.cpp index 6a368944ed3..7359a45dec0 100644 --- a/intern/itasc/Distance.cpp +++ b/intern/itasc/Distance.cpp @@ -1,12 +1,9 @@ -/** \file itasc/Distance.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file * \ingroup intern_itasc */ -/* - * Distance.cpp - * - * Created on: Jan 30, 2009 - * Author: rsmits - */ #include "Distance.hpp" #include "kdl/kinfam_io.hpp" diff --git a/intern/itasc/Distance.hpp b/intern/itasc/Distance.hpp index 5c56dd871ef..02a8f9d9f29 100644 --- a/intern/itasc/Distance.hpp +++ b/intern/itasc/Distance.hpp @@ -1,8 +1,8 @@ -/* - * Distance.hpp - * - * Created on: Jan 30, 2009 - * Author: rsmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef DISTANCE_HPP_ diff --git a/intern/itasc/FixedObject.cpp b/intern/itasc/FixedObject.cpp index 8a547ac2b05..1fa4b77dfe5 100644 --- a/intern/itasc/FixedObject.cpp +++ b/intern/itasc/FixedObject.cpp @@ -1,12 +1,9 @@ -/** \file itasc/FixedObject.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file * \ingroup intern_itasc */ -/* - * FixedObject.cpp - * - * Created on: Feb 10, 2009 - * Author: benoitbolsee - */ #include "FixedObject.hpp" diff --git a/intern/itasc/FixedObject.hpp b/intern/itasc/FixedObject.hpp index ad26e7cb2d6..1b8544a90e5 100644 --- a/intern/itasc/FixedObject.hpp +++ b/intern/itasc/FixedObject.hpp @@ -1,8 +1,8 @@ -/* - * FixedObject.h - * - * Created on: Feb 10, 2009 - * Author: benoitbolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file + * \ingroup intern_itasc */ #ifndef FIXEDOBJECT_HPP_ diff --git a/intern/itasc/MovingFrame.cpp b/intern/itasc/MovingFrame.cpp index e3f4d612155..cda9ae8b3fa 100644 --- a/intern/itasc/MovingFrame.cpp +++ b/intern/itasc/MovingFrame.cpp @@ -1,12 +1,9 @@ -/** \file itasc/MovingFrame.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file * \ingroup intern_itasc */ -/* - * MovingFrame.cpp - * - * Created on: Feb 10, 2009 - * Author: benoitbolsee - */ #include "MovingFrame.hpp" #include <string.h> diff --git a/intern/itasc/MovingFrame.hpp b/intern/itasc/MovingFrame.hpp index 2e9c2f64bd6..9e499610812 100644 --- a/intern/itasc/MovingFrame.hpp +++ b/intern/itasc/MovingFrame.hpp @@ -1,8 +1,8 @@ -/* - * MovingFrame.h - * - * Created on: Feb 10, 2009 - * Author: benoitbolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file + * \ingroup intern_itasc */ #ifndef MOVINGFRAME_HPP_ diff --git a/intern/itasc/Object.hpp b/intern/itasc/Object.hpp index bf80d83e5aa..00c1b9e9fca 100644 --- a/intern/itasc/Object.hpp +++ b/intern/itasc/Object.hpp @@ -1,8 +1,8 @@ -/* - * Object.hpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef OBJECT_HPP_ diff --git a/intern/itasc/Scene.cpp b/intern/itasc/Scene.cpp index b5f8e4df386..0b9c8cb1056 100644 --- a/intern/itasc/Scene.cpp +++ b/intern/itasc/Scene.cpp @@ -1,12 +1,9 @@ -/** \file itasc/Scene.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file * \ingroup intern_itasc */ -/* - * Scene.cpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits - */ #include "Scene.hpp" #include "ControlledObject.hpp" @@ -270,7 +267,7 @@ bool Scene::initialize() m_ytask.resize(m_ncTotal); bool toggle = true; - int cnt = 0; + int count = 0; // Initialize all ConstraintSets: for (ConstraintMap::iterator it = constraints.begin(); it != constraints.end(); ++it) { // Calculate the external pose: @@ -279,8 +276,8 @@ bool Scene::initialize() getConstraintPose(cs->task, cs, external_pose); result &= cs->task->initialise(external_pose); cs->task->initCache(m_cache); - for (int i = 0; i < cs->constraintrange.count; i++, cnt++) { - m_ytask[cnt] = toggle; + for (int i = 0; i < cs->constraintrange.count; i++, count++) { + m_ytask[count] = toggle; } toggle = !toggle; project(m_Cf, cs->constraintrange, cs->featurerange) = cs->task->getCf(); diff --git a/intern/itasc/Scene.hpp b/intern/itasc/Scene.hpp index 5ed031b543e..92ded8c8d9d 100644 --- a/intern/itasc/Scene.hpp +++ b/intern/itasc/Scene.hpp @@ -1,8 +1,8 @@ -/* - * Scene.hpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef SCENE_HPP_ diff --git a/intern/itasc/Solver.hpp b/intern/itasc/Solver.hpp index 809c03d9bce..2213834f114 100644 --- a/intern/itasc/Solver.hpp +++ b/intern/itasc/Solver.hpp @@ -1,8 +1,8 @@ -/* - * Solver.hpp - * - * Created on: Jan 8, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef SOLVER_HPP_ diff --git a/intern/itasc/UncontrolledObject.cpp b/intern/itasc/UncontrolledObject.cpp index 3083b711217..d8c5db9a7c5 100644 --- a/intern/itasc/UncontrolledObject.cpp +++ b/intern/itasc/UncontrolledObject.cpp @@ -1,12 +1,9 @@ -/** \file itasc/UncontrolledObject.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file * \ingroup intern_itasc */ -/* - * UncontrolledObject.cpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits - */ #include "UncontrolledObject.hpp" diff --git a/intern/itasc/UncontrolledObject.hpp b/intern/itasc/UncontrolledObject.hpp index 81445538fa6..fad3be61653 100644 --- a/intern/itasc/UncontrolledObject.hpp +++ b/intern/itasc/UncontrolledObject.hpp @@ -1,8 +1,8 @@ -/* - * UncontrolledObject.h - * - * Created on: Jan 5, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef UNCONTROLLEDOBJECT_HPP_ diff --git a/intern/itasc/WDLSSolver.cpp b/intern/itasc/WDLSSolver.cpp index c5221fc2767..37190a88974 100644 --- a/intern/itasc/WDLSSolver.cpp +++ b/intern/itasc/WDLSSolver.cpp @@ -1,12 +1,9 @@ -/** \file itasc/WDLSSolver.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file * \ingroup intern_itasc */ -/* - * WDLSSolver.hpp.cpp - * - * Created on: Jan 8, 2009 - * Author: rubensmits - */ #include "WDLSSolver.hpp" #include "kdl/utilities/svd_eigen_HH.hpp" diff --git a/intern/itasc/WDLSSolver.hpp b/intern/itasc/WDLSSolver.hpp index 4e72490cf1b..61c96d29c77 100644 --- a/intern/itasc/WDLSSolver.hpp +++ b/intern/itasc/WDLSSolver.hpp @@ -1,8 +1,8 @@ -/* - * WDLSSolver.hpp - * - * Created on: Jan 8, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef WDLSSOLVER_HPP_ diff --git a/intern/itasc/WSDLSSolver.cpp b/intern/itasc/WSDLSSolver.cpp index 32273828acc..bd1f1407185 100644 --- a/intern/itasc/WSDLSSolver.cpp +++ b/intern/itasc/WSDLSSolver.cpp @@ -1,12 +1,9 @@ -/** \file itasc/WSDLSSolver.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file * \ingroup intern_itasc */ -/* - * WDLSSolver.hpp.cpp - * - * Created on: Jan 8, 2009 - * Author: rubensmits - */ #include "WSDLSSolver.hpp" #include "kdl/utilities/svd_eigen_HH.hpp" diff --git a/intern/itasc/WSDLSSolver.hpp b/intern/itasc/WSDLSSolver.hpp index e6516031428..9456879d40e 100644 --- a/intern/itasc/WSDLSSolver.hpp +++ b/intern/itasc/WSDLSSolver.hpp @@ -1,8 +1,8 @@ -/* - * WSDLSSolver.hpp - * - * Created on: Mar 26, 2009 - * Author: benoit bolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file + * \ingroup intern_itasc */ #ifndef WSDLSSOLVER_HPP_ diff --git a/intern/itasc/WorldObject.cpp b/intern/itasc/WorldObject.cpp index 34641478ea0..69a80385d1a 100644 --- a/intern/itasc/WorldObject.cpp +++ b/intern/itasc/WorldObject.cpp @@ -1,12 +1,9 @@ -/** \file itasc/WorldObject.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file * \ingroup intern_itasc */ -/* - * WorldObject.cpp - * - * Created on: Feb 10, 2009 - * Author: benoitbolsee - */ #include "WorldObject.hpp" diff --git a/intern/itasc/WorldObject.hpp b/intern/itasc/WorldObject.hpp index 99756dcd684..49b275fef92 100644 --- a/intern/itasc/WorldObject.hpp +++ b/intern/itasc/WorldObject.hpp @@ -1,8 +1,8 @@ -/* - * WorldObject.h - * - * Created on: Feb 10, 2009 - * Author: benoitbolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef WORLDOBJECT_HPP_ diff --git a/intern/itasc/eigen_types.cpp b/intern/itasc/eigen_types.cpp index a1c0525766e..f0ae03b1e0c 100644 --- a/intern/itasc/eigen_types.cpp +++ b/intern/itasc/eigen_types.cpp @@ -1,12 +1,9 @@ -/** \file itasc/eigen_types.cpp +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file * \ingroup intern_itasc */ -/* - * eigen_types.cpp - * - * Created on: March 19, 2009 - * Author: benoit bolsee - */ #include "eigen_types.hpp" diff --git a/intern/itasc/eigen_types.hpp b/intern/itasc/eigen_types.hpp index 601b69274d9..2ab4da1cb72 100644 --- a/intern/itasc/eigen_types.hpp +++ b/intern/itasc/eigen_types.hpp @@ -1,8 +1,8 @@ -/* - * eigen_types.hpp - * - * Created on: March 6, 2009 - * Author: benoit bolsee +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Benoit Bolsee. */ + +/** \file + * \ingroup intern_itasc */ #ifndef EIGEN_TYPES_HPP_ diff --git a/intern/itasc/ublas_types.hpp b/intern/itasc/ublas_types.hpp index bf9bdcc26f2..3ba5b4a1e21 100644 --- a/intern/itasc/ublas_types.hpp +++ b/intern/itasc/ublas_types.hpp @@ -1,8 +1,8 @@ -/* - * ublas_types.hpp - * - * Created on: Jan 5, 2009 - * Author: rubensmits +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright 2009 Ruben Smits. */ + +/** \file + * \ingroup intern_itasc */ #ifndef UBLAS_TYPES_HPP_ diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp index 6083c4908a1..282fbdb3f77 100644 --- a/intern/mantaflow/intern/MANTA_main.cpp +++ b/intern/mantaflow/intern/MANTA_main.cpp @@ -263,6 +263,8 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) } /* All requested initializations must not fail in constructor. */ BLI_assert(initSuccess); + (void)initSuccess; /* Ignored in release. */ + updatePointers(fmd); } diff --git a/intern/mantaflow/intern/strings/fluid_script.h b/intern/mantaflow/intern/strings/fluid_script.h index 48e0c243e16..548606f1b32 100644 --- a/intern/mantaflow/intern/strings/fluid_script.h +++ b/intern/mantaflow/intern/strings/fluid_script.h @@ -739,13 +739,13 @@ file_format_data = '$CACHE_DATA_FORMAT$'\n\ file_format_mesh = '$CACHE_MESH_FORMAT$'\n\ \n\ # How many frame to load from cache\n\ -from_cache_cnt = 100\n\ +from_cache_count = 100\n\ \n\ -loop_cnt = 0\n\ +loop_count = 0\n\ while current_frame_s$ID$ <= end_frame_s$ID$:\n\ \n\ # Load already simulated data from cache:\n\ - if loop_cnt < from_cache_cnt:\n\ + if loop_count < from_cache_count:\n\ load_data(current_frame_s$ID$, cache_resumable)\n\ \n\ # Otherwise simulate new data\n\ @@ -756,7 +756,7 @@ while current_frame_s$ID$ <= end_frame_s$ID$:\n\ step(current_frame_s$ID$)\n\ \n\ current_frame_s$ID$ += 1\n\ - loop_cnt += 1\n\ + loop_count += 1\n\ \n\ if gui:\n\ gui.pause()\n"; diff --git a/intern/mikktspace/mikktspace.c b/intern/mikktspace/mikktspace.c index fe302eb5e7c..794590d30a4 100644 --- a/intern/mikktspace/mikktspace.c +++ b/intern/mikktspace/mikktspace.c @@ -1114,7 +1114,7 @@ static tbool GenerateTSpaces(STSpace psTspace[], STSpace *pSubGroupTspace = NULL; SSubGroup *pUniSubGroups = NULL; int *pTmpMembers = NULL; - int iMaxNrFaces = 0, iUniqueTspaces = 0, g = 0, i = 0; + int iMaxNrFaces = 0, g = 0, i = 0; for (g = 0; g < iNrActiveGroups; g++) if (iMaxNrFaces < pGroups[g].iNrFaces) iMaxNrFaces = pGroups[g].iNrFaces; @@ -1136,7 +1136,6 @@ static tbool GenerateTSpaces(STSpace psTspace[], return TFALSE; } - iUniqueTspaces = 0; for (g = 0; g < iNrActiveGroups; g++) { const SGroup *pGroup = &pGroups[g]; int iUniqueSubGroups = 0, s = 0; @@ -1211,9 +1210,7 @@ static tbool GenerateTSpaces(STSpace psTspace[], ++l; } - // assign tangent space index assert(bFound || l == iUniqueSubGroups); - // piTempTangIndices[f*3+index] = iUniqueTspaces+l; // if no match was found we allocate a new subgroup if (!bFound) { @@ -1262,10 +1259,9 @@ static tbool GenerateTSpaces(STSpace psTspace[], } } - // clean up and offset iUniqueTspaces + // clean up for (s = 0; s < iUniqueSubGroups; s++) free(pUniSubGroups[s].pTriMembers); - iUniqueTspaces += iUniqueSubGroups; } // clean up diff --git a/intern/opencolorio/fallback_impl.cc b/intern/opencolorio/fallback_impl.cc index 82bd4d9fc36..aaab8b4e6b9 100644 --- a/intern/opencolorio/fallback_impl.cc +++ b/intern/opencolorio/fallback_impl.cc @@ -445,6 +445,16 @@ const char *FallbackImpl::colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr * /*cs*/ return ""; } +int FallbackImpl::colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr * /*cs*/) +{ + return 0; +} +const char *FallbackImpl::colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr * /*cs*/, + const int /*index*/) +{ + return ""; +} + OCIO_ConstProcessorRcPtr *FallbackImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr * /*config*/, const char * /*input*/, const char * /*view*/, diff --git a/intern/opencolorio/ocio_capi.cc b/intern/opencolorio/ocio_capi.cc index 7e7710843e2..91784a288c8 100644 --- a/intern/opencolorio/ocio_capi.cc +++ b/intern/opencolorio/ocio_capi.cc @@ -234,6 +234,16 @@ const char *OCIO_colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) return impl->colorSpaceGetFamily(cs); } +int OCIO_colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) +{ + return impl->colorSpaceGetNumAliases(cs); +} + +const char *OCIO_colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) +{ + return impl->colorSpaceGetAlias(cs, index); +} + OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *config, const char *input, const char *view, diff --git a/intern/opencolorio/ocio_capi.h b/intern/opencolorio/ocio_capi.h index 87a8043aec6..5c036ec263a 100644 --- a/intern/opencolorio/ocio_capi.h +++ b/intern/opencolorio/ocio_capi.h @@ -157,6 +157,8 @@ void OCIO_cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *processor); const char *OCIO_colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs); const char *OCIO_colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs); const char *OCIO_colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs); +int OCIO_colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs); +const char *OCIO_colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index); OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *config, const char *input, diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc index a02a37522b9..ca1b7cc42e1 100644 --- a/intern/opencolorio/ocio_impl.cc +++ b/intern/opencolorio/ocio_impl.cc @@ -640,6 +640,15 @@ const char *OCIOImpl::colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) return (*(ConstColorSpaceRcPtr *)cs)->getFamily(); } +int OCIOImpl::colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) +{ + return (*(ConstColorSpaceRcPtr *)cs)->getNumAliases(); +} +const char *OCIOImpl::colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) +{ + return (*(ConstColorSpaceRcPtr *)cs)->getAlias(index); +} + OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr *config_, const char *input, const char *view, diff --git a/intern/opencolorio/ocio_impl.h b/intern/opencolorio/ocio_impl.h index 5be1d3aacf8..d42fa58121f 100644 --- a/intern/opencolorio/ocio_impl.h +++ b/intern/opencolorio/ocio_impl.h @@ -76,6 +76,8 @@ class IOCIOImpl { virtual const char *colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs) = 0; virtual const char *colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs) = 0; virtual const char *colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) = 0; + virtual int colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) = 0; + virtual const char *colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) = 0; virtual OCIO_ConstProcessorRcPtr *createDisplayProcessor(OCIO_ConstConfigRcPtr *config, const char *input, @@ -190,6 +192,8 @@ class FallbackImpl : public IOCIOImpl { const char *colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs); const char *colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs); const char *colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs); + int colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs); + const char *colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index); OCIO_ConstProcessorRcPtr *createDisplayProcessor(OCIO_ConstConfigRcPtr *config, const char *input, @@ -277,6 +281,8 @@ class OCIOImpl : public IOCIOImpl { const char *colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs); const char *colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs); const char *colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs); + int colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs); + const char *colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index); OCIO_ConstProcessorRcPtr *createDisplayProcessor(OCIO_ConstConfigRcPtr *config, const char *input, diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index e3d44ae9d55..fcaab5dd752 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -177,6 +177,7 @@ static bool createGPUShader(OCIO_GPUShader &shader, ShaderCreateInfo info("OCIO_Display"); /* Work around OpenColorIO not supporting latest GLSL yet. */ + info.define("texture1D", "texture"); info.define("texture2D", "texture"); info.define("texture3D", "texture"); info.typedef_source("ocio_shader_shared.hh"); @@ -192,6 +193,11 @@ static bool createGPUShader(OCIO_GPUShader &shader, info.fragment_source("gpu_shader_display_transform_frag.glsl"); info.fragment_source_generated = source; + /* T96502: Work around for incorrect OCIO GLSL code generation when using + * GradingPrimaryTransform. Should be reevaluated when changing to a next version of OCIO. + * (currently v2.1.1). */ + info.define("inf 1e32"); + if (use_curve_mapping) { info.define("USE_CURVE_MAPPING"); info.uniform_buf(UNIFORMBUF_SLOT_CURVEMAP, "OCIO_GPUCurveMappingParameters", "curve_mapping"); @@ -201,8 +207,11 @@ static bool createGPUShader(OCIO_GPUShader &shader, /* Set LUT textures. */ int slot = TEXTURE_SLOT_LUTS_OFFSET; for (OCIO_GPULutTexture &texture : textures.luts) { - ImageType type = GPU_texture_dimensions(texture.texture) == 2 ? ImageType::FLOAT_2D : - ImageType::FLOAT_3D; + const int dimensions = GPU_texture_dimensions(texture.texture); + ImageType type = (dimensions == 1) ? ImageType::FLOAT_1D : + (dimensions == 2) ? ImageType::FLOAT_2D : + ImageType::FLOAT_3D; + info.sampler(slot++, type, texture.sampler_name.c_str()); } @@ -300,9 +309,9 @@ static bool addGPUUniform(OCIO_GPUTextures &textures, return true; } -static bool addGPULut2D(OCIO_GPUTextures &textures, - const GpuShaderDescRcPtr &shader_desc, - int index) +static bool addGPULut1D2D(OCIO_GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc, + int index) { const char *texture_name = nullptr; const char *sampler_name = nullptr; @@ -324,7 +333,15 @@ static bool addGPULut2D(OCIO_GPUTextures &textures, GPU_R16F; OCIO_GPULutTexture lut; - lut.texture = GPU_texture_create_2d(texture_name, width, height, 1, format, values); + /* There does not appear to be an explicit way to check if a texture is 1D or 2D. + * It depends on more than height. So check instead by looking at the source. */ + std::string sampler1D_name = std::string("sampler1D ") + sampler_name; + if (strstr(shader_desc->getShaderText(), sampler1D_name.c_str()) != nullptr) { + lut.texture = GPU_texture_create_1d(texture_name, width, 1, format, values); + } + else { + lut.texture = GPU_texture_create_2d(texture_name, width, height, 1, format, values); + } if (lut.texture == nullptr) { return false; } @@ -385,7 +402,7 @@ static bool createGPUTextures(OCIO_GPUTextures &textures, } } for (int index = 0; index < shaderdesc_to_scene_linear->getNumTextures(); index++) { - if (!addGPULut2D(textures, shaderdesc_to_scene_linear, index)) { + if (!addGPULut1D2D(textures, shaderdesc_to_scene_linear, index)) { return false; } } @@ -400,7 +417,7 @@ static bool createGPUTextures(OCIO_GPUTextures &textures, } } for (int index = 0; index < shaderdesc_to_display->getNumTextures(); index++) { - if (!addGPULut2D(textures, shaderdesc_to_display, index)) { + if (!addGPULut1D2D(textures, shaderdesc_to_display, index)) { return false; } } diff --git a/release/datafiles/blender_icons16/icon16_hair.dat b/release/datafiles/blender_icons16/icon16_curves.dat Binary files differindex 6d550c12209..6d550c12209 100644 --- a/release/datafiles/blender_icons16/icon16_hair.dat +++ b/release/datafiles/blender_icons16/icon16_curves.dat diff --git a/release/datafiles/blender_icons16/icon16_hair_data.dat b/release/datafiles/blender_icons16/icon16_curves_data.dat Binary files differindex ef891ae3fc4..ef891ae3fc4 100644 --- a/release/datafiles/blender_icons16/icon16_hair_data.dat +++ b/release/datafiles/blender_icons16/icon16_curves_data.dat diff --git a/release/datafiles/blender_icons16/icon16_force_boid.dat b/release/datafiles/blender_icons16/icon16_force_boid.dat Binary files differindex 71f89bd7c04..f719054d84a 100644 --- a/release/datafiles/blender_icons16/icon16_force_boid.dat +++ b/release/datafiles/blender_icons16/icon16_force_boid.dat diff --git a/release/datafiles/blender_icons16/icon16_outliner_data_hair.dat b/release/datafiles/blender_icons16/icon16_outliner_data_curves.dat Binary files differindex ab0568bf81f..ab0568bf81f 100644 --- a/release/datafiles/blender_icons16/icon16_outliner_data_hair.dat +++ b/release/datafiles/blender_icons16/icon16_outliner_data_curves.dat diff --git a/release/datafiles/blender_icons16/icon16_outliner_ob_hair.dat b/release/datafiles/blender_icons16/icon16_outliner_ob_curves.dat Binary files differindex b110c789c04..b110c789c04 100644 --- a/release/datafiles/blender_icons16/icon16_outliner_ob_hair.dat +++ b/release/datafiles/blender_icons16/icon16_outliner_ob_curves.dat diff --git a/release/datafiles/blender_icons32/icon32_hair.dat b/release/datafiles/blender_icons32/icon32_curves.dat Binary files differindex de7507384cc..de7507384cc 100644 --- a/release/datafiles/blender_icons32/icon32_hair.dat +++ b/release/datafiles/blender_icons32/icon32_curves.dat diff --git a/release/datafiles/blender_icons32/icon32_hair_data.dat b/release/datafiles/blender_icons32/icon32_curves_data.dat Binary files differindex 5b03446438d..5b03446438d 100644 --- a/release/datafiles/blender_icons32/icon32_hair_data.dat +++ b/release/datafiles/blender_icons32/icon32_curves_data.dat diff --git a/release/datafiles/blender_icons32/icon32_force_boid.dat b/release/datafiles/blender_icons32/icon32_force_boid.dat Binary files differindex 7fc7cb5ee8c..9043989024b 100644 --- a/release/datafiles/blender_icons32/icon32_force_boid.dat +++ b/release/datafiles/blender_icons32/icon32_force_boid.dat diff --git a/release/datafiles/blender_icons32/icon32_outliner_data_hair.dat b/release/datafiles/blender_icons32/icon32_outliner_data_curves.dat Binary files differindex 7c8d909d24e..7c8d909d24e 100644 --- a/release/datafiles/blender_icons32/icon32_outliner_data_hair.dat +++ b/release/datafiles/blender_icons32/icon32_outliner_data_curves.dat diff --git a/release/datafiles/blender_icons32/icon32_outliner_ob_hair.dat b/release/datafiles/blender_icons32/icon32_outliner_ob_curves.dat Binary files differindex 0cadeea0af6..0cadeea0af6 100644 --- a/release/datafiles/blender_icons32/icon32_outliner_ob_hair.dat +++ b/release/datafiles/blender_icons32/icon32_outliner_ob_curves.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_add.dat b/release/datafiles/icons/ops.curves.sculpt_add.dat Binary files differnew file mode 100644 index 00000000000..b66f4da5b71 --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_add.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_comb.dat b/release/datafiles/icons/ops.curves.sculpt_comb.dat Binary files differnew file mode 100644 index 00000000000..d6dd75a35d7 --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_comb.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_cut.dat b/release/datafiles/icons/ops.curves.sculpt_cut.dat Binary files differnew file mode 100644 index 00000000000..e7ef86e2fbc --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_cut.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_delete.dat b/release/datafiles/icons/ops.curves.sculpt_delete.dat Binary files differnew file mode 100644 index 00000000000..896d472e017 --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_delete.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_grow.dat b/release/datafiles/icons/ops.curves.sculpt_grow.dat Binary files differnew file mode 100644 index 00000000000..9b3453085e4 --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_grow.dat diff --git a/release/scripts/addons b/release/scripts/addons -Subproject bb62f10715a871d7069d2b2c74b2efc97c3c350 +Subproject 67f1fbca1482d9d9362a4001332e785c3fd5d23 diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 4b10c29346e..1cd9cf9c649 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -344,7 +344,6 @@ url_manual_mapping = ( ("bpy.types.sequencerpreviewoverlay.show_metadata*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay-show-metadata"), ("bpy.types.sequencertimelineoverlay.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-fcurves"), ("bpy.types.spaceclipeditor.use_grayscale_preview*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-grayscale-preview"), - ("bpy.types.spaceoutliner.use_filter_lib_override*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override"), ("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"), ("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"), ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"), diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 74e0ddfdf62..7ce9a5650bd 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -85,6 +85,28 @@ class Prefs(bpy.types.KeyConfigPreferences): ), update=update_fn, ) + + # Experimental: only show with developer extras, see: T96544. + use_tweak_select_passthrough: BoolProperty( + name="Tweak Select: Mouse Select & Move", + description=( + "The tweak tool is activated immediately instead of placing the cursor. " + "This is an experimental preference and may be removed" + ), + default=False, + update=update_fn, + ) + # Experimental: only show with developer extras, see: T96544. + use_tweak_tool_lmb_interaction: BoolProperty( + name="Tweak Tool: Left Mouse Select & Move", + description=( + "The tweak tool is activated immediately instead of placing the cursor. " + "This is an experimental preference and may be removed" + ), + default=False, + update=update_fn, + ) + use_alt_click_leader: BoolProperty( name="Alt Click Tool Prompt", description=( @@ -236,6 +258,7 @@ class Prefs(bpy.types.KeyConfigPreferences): prefs = context.preferences + show_developer_ui = prefs.view.show_developer_ui is_select_left = (self.select_mouse == 'LEFT') use_mouse_emulate_3_button = ( prefs.inputs.use_mouse_emulate_3_button and @@ -270,6 +293,13 @@ class Prefs(bpy.types.KeyConfigPreferences): row = sub.row() row.prop(self, "use_select_all_toggle") + if show_developer_ui: + row = sub.row() + row.prop(self, "use_tweak_select_passthrough") + if show_developer_ui and (not is_select_left): + row = sub.row() + row.prop(self, "use_tweak_tool_lmb_interaction") + # 3DView settings. col = layout.column() col.label(text="3D View") @@ -301,6 +331,7 @@ def load(): kc = context.window_manager.keyconfigs.new(IDNAME) kc_prefs = kc.preferences + show_developer_ui = prefs.view.show_developer_ui is_select_left = (kc_prefs.select_mouse == 'LEFT') use_mouse_emulate_3_button = ( prefs.inputs.use_mouse_emulate_3_button and @@ -322,6 +353,11 @@ def load(): use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'), use_fallback_tool=True, use_fallback_tool_rmb=(False if is_select_left else kc_prefs.rmb_action == 'FALLBACK_TOOL'), + use_tweak_select_passthrough=(show_developer_ui and kc_prefs.use_tweak_select_passthrough), + use_tweak_tool_lmb_interaction=( + False if is_select_left else + (show_developer_ui and kc_prefs.use_tweak_tool_lmb_interaction) + ), use_alt_tool_or_cursor=( (not use_mouse_emulate_3_button) and (kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 80794d9f23d..d307c3d0f0a 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -31,6 +31,8 @@ class Params: "context_menu_event", "cursor_set_event", "cursor_tweak_event", + "use_tweak_select_passthrough", + "use_tweak_tool_lmb_interaction", "use_mouse_emulate_3_button", # User preferences: @@ -102,6 +104,8 @@ class Params: use_gizmo_drag=True, use_fallback_tool=False, use_fallback_tool_rmb=False, + use_tweak_select_passthrough=False, + use_tweak_tool_lmb_interaction=False, use_v3d_tab_menu=False, use_v3d_shade_ex_pie=False, use_v3d_mmb_pan=False, @@ -129,6 +133,7 @@ class Params: self.tool_maybe_tweak_value = 'PRESS' else: self.tool_maybe_tweak_value = 'CLICK_DRAG' + self.use_tweak_tool_lmb_interaction = use_tweak_tool_lmb_interaction self.context_menu_event = {"type": 'W', "value": 'PRESS'} @@ -150,6 +155,7 @@ class Params: self.action_mouse = 'RIGHTMOUSE' self.tool_mouse = 'LEFTMOUSE' self.tool_maybe_tweak_value = 'CLICK_DRAG' + self.use_tweak_tool_lmb_interaction = False if self.legacy: self.context_menu_event = {"type": 'W', "value": 'PRESS'} @@ -185,6 +191,8 @@ class Params: self.use_file_single_click = use_file_single_click + self.use_tweak_select_passthrough = use_tweak_select_passthrough + self.use_fallback_tool = use_fallback_tool self.use_fallback_tool_rmb = use_fallback_tool_rmb @@ -442,7 +450,7 @@ def _template_items_change_frame(params): # Tool System Templates -def _template_items_tool_select(params, operator, cursor_operator, fallback, *, extend): +def _template_items_tool_select(params, operator, cursor_operator, fallback): if params.select_mouse == 'LEFTMOUSE': # By default use 'PRESS' for immediate select without quick delay. # Fallback key-maps 'CLICK' since 'PRESS' events passes through (allowing either click or drag). @@ -454,9 +462,22 @@ def _template_items_tool_select(params, operator, cursor_operator, fallback, *, (operator, {"type": 'LEFTMOUSE', "value": 'CLICK' if fallback else 'PRESS'}, {"properties": [("deselect_all", True)]}), (operator, {"type": 'LEFTMOUSE', "value": 'CLICK' if fallback else 'PRESS', "shift": True}, - {"properties": [(extend, True)]}), + {"properties": [("toggle", True)]}), ] else: + # Experimental support for LMB interaction for the tweak tool. + if params.use_tweak_tool_lmb_interaction and not fallback: + return [ + (operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": [("deselect_all", True), ("select_passthrough", True)]}), + (operator, {"type": 'LEFTMOUSE', "value": 'CLICK'}, + {"properties": [("deselect_all", True)]}), + (operator, {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, + {"properties": [("deselect_all", False), ("toggle", True)]}), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, + {"properties": [("release_confirm", True)]}), + ] + # For right mouse, set the cursor. return [ (cursor_operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), @@ -1168,7 +1189,12 @@ def km_uv_editor(params): items.extend([ # Selection modes. *_template_items_uv_select_mode(params), - *_template_uv_select(type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy), + *_template_uv_select( + type=params.select_mouse, + value=params.select_mouse_value_fallback, + select_passthrough=params.use_tweak_select_passthrough, + legacy=params.legacy, + ), ("uv.mark_seam", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None), @@ -1496,6 +1522,7 @@ def km_view3d(params): type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy, + select_passthrough=params.use_tweak_select_passthrough, ), op_tool_optional( ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), @@ -4660,7 +4687,7 @@ def _template_paint_radial_control(paint, rotation=False, secondary_rotation=Fal return items -def _template_view3d_select(*, type, value, legacy, exclude_mod=None): +def _template_view3d_select(*, type, value, legacy, select_passthrough, exclude_mod=None): # NOTE: `exclude_mod` is needed since we don't want this tool to exclude Control-RMB actions when this is used # as a tool key-map with RMB-select and `use_fallback_tool_rmb` is enabled. See T92467. return [( @@ -4668,7 +4695,8 @@ def _template_view3d_select(*, type, value, legacy, exclude_mod=None): {"type": type, "value": value, **{m: True for m in mods}}, {"properties": [(c, True) for c in props]}, ) for props, mods in ( - (("deselect_all",) if not legacy else (), ()), + ((("deselect_all", "select_passthrough") if select_passthrough else + ("deselect_all",)) if not legacy else (), ()), (("toggle",), ("shift",)), (("center", "object"), ("ctrl",)), (("enumerate",), ("alt",)), @@ -4694,12 +4722,15 @@ def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=Tru ] -def _template_uv_select(*, type, value, legacy): +def _template_uv_select(*, type, value, select_passthrough, legacy): return [ ("uv.select", {"type": type, "value": value}, - {"properties": [("deselect_all", not legacy)]}), + {"properties": [ + *((("deselect_all", True),) if not legacy else ()), + *((("select_passthrough", True),) if select_passthrough else ()), + ]}), ("uv.select", {"type": type, "value": value, "shift": True}, - {"properties": [("extend", True)]}), + {"properties": [("toggle", True)]}), ] @@ -6289,9 +6320,13 @@ def km_image_editor_tool_uv_select(params, *, fallback): {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "uv.select", "uv.cursor_set", fallback, extend="extend")), + params, "uv.select", "uv.cursor_set", fallback)), *([] if (not params.use_fallback_tool_rmb) else _template_uv_select( - type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + type=params.select_mouse, + value=params.select_mouse_value, + select_passthrough=params.use_tweak_select_passthrough, + legacy=params.legacy, + )), ]}, ) @@ -6496,9 +6531,14 @@ def km_3d_view_tool_select(params, *, fallback): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "view3d.select", "view3d.cursor3d", fallback, extend="toggle")), + params, "view3d.select", "view3d.cursor3d", fallback)), *([] if (not params.use_fallback_tool_rmb) else _template_view3d_select( - type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy, exclude_mod="ctrl")), + type=params.select_mouse, + value=params.select_mouse_value, + legacy=params.legacy, + select_passthrough=params.use_tweak_select_passthrough, + exclude_mod="ctrl", + )), ]}, ) @@ -7408,7 +7448,7 @@ def km_3d_view_tool_edit_gpencil_select(params, *, fallback): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "gpencil.select", "view3d.cursor3d", fallback, extend="toggle")), + params, "gpencil.select", "view3d.cursor3d", fallback)), *([] if (not params.use_fallback_tool_rmb) else _template_view3d_gpencil_select( type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), ]}, @@ -7546,7 +7586,7 @@ def km_3d_view_tool_sculpt_gpencil_select(params): return ( "3D View Tool: Sculpt Gpencil, Tweak", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d", False, extend="toggle")}, + {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d", False)}, ) @@ -7586,7 +7626,7 @@ def km_sequencer_editor_tool_generic_select(params, *, fallback): {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "sequencer.select", "sequencer.cursor_set", fallback, extend="toggle")), + params, "sequencer.select", "sequencer.cursor_set", fallback)), *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_preview_select( type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 55ee91af7cd..6661031552e 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -564,9 +564,9 @@ def km_uv_editor(params): {"properties": [("type", 'ISLAND')]}), ("uv.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, - {"properties": [("extend", False), ("deselect_all", True)]}), + {"properties": [("deselect_all", True)]}), ("uv.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, - {"properties": [("extend", True), ("deselect_all", False)]}), + {"properties": [("toggle", True), ("deselect_all", False)]}), ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True}, diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index a7f401afad1..d6e5604fde0 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -2996,7 +2996,7 @@ class WM_MT_splash_quick_setup(Menu): old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version() if bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version: - sub.operator("preferences.copy_prev", text="Load %d.%d Settings" % old_version) + sub.operator("preferences.copy_prev", text=iface_("Load %d.%d Settings", "Operator") % old_version) sub.operator("wm.save_userpref", text="Save New Settings") else: sub.label() 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 c1d6dcfd3f4..947a7e5c7a7 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -677,7 +677,7 @@ class GreasePencilSimplifyPanel: rd = context.scene.render - layout.active = rd.simplify_gpencil + layout.active = rd.use_simplify and rd.simplify_gpencil col = layout.column() col.prop(rd, "simplify_gpencil_onplay") diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index 5721e20f328..89f840306e1 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -193,7 +193,7 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel): obj = context.object obj_type = obj.type - is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'HAIR', 'POINTCLOUD'}) + is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'CURVES', 'POINTCLOUD'}) has_bounds = (is_geometry or obj_type in {'LATTICE', 'ARMATURE'}) is_wire = (obj_type in {'CAMERA', 'EMPTY'}) is_empty_image = (obj_type == 'EMPTY' and obj.empty_display_type == 'IMAGE') diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py index 34e86184097..312a580bf7d 100644 --- a/release/scripts/startup/bl_ui/properties_output.py +++ b/release/scripts/startup/bl_ui/properties_output.py @@ -296,6 +296,41 @@ class RENDER_PT_output_views(RenderOutputButtonsPanel, Panel): layout.template_image_views(rd.image_settings) +class RENDER_PT_output_color_management(RenderOutputButtonsPanel, Panel): + bl_label = "Color Management" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "RENDER_PT_output" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + scene = context.scene + image_settings = scene.render.image_settings + + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + layout.row().prop(image_settings, "color_management", text=" ", expand=True) + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=True) + + if image_settings.color_management == 'OVERRIDE': + owner = image_settings + else: + owner = scene + flow.enabled = False + + col = flow.column() + + if image_settings.has_linear_colorspace: + if hasattr(owner, 'linear_colorspace_settings'): + col.prop(owner.linear_colorspace_settings, "name", text="Color Space") + else: + col.prop(owner.display_settings, "display_device") + col.separator() + col.template_colormanaged_view_settings(owner, "view_settings") + + class RENDER_PT_encoding(RenderOutputButtonsPanel, Panel): bl_label = "Encoding" bl_parent_id = "RENDER_PT_output" @@ -484,6 +519,7 @@ classes = ( RENDER_PT_stereoscopy, RENDER_PT_output, RENDER_PT_output_views, + RENDER_PT_output_color_management, RENDER_PT_encoding, RENDER_PT_encoding_video, RENDER_PT_encoding_audio, diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index 6f9ef12c3b7..706e228c5d9 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -331,7 +331,8 @@ class GRAPH_MT_slider(Menu): layout = self.layout layout.operator("graph.breakdown", text="Breakdown") - layout.operator("graph.blend_to_neighbor", text="Blend To Neighbor") + layout.operator("graph.blend_to_neighbor", text="Blend to Neighbor") + layout.operator("graph.blend_to_default", text="Blend to Default Value") class GRAPH_MT_view_pie(Menu): diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 7c88006a4d6..d7d905cb820 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -61,7 +61,7 @@ class NODE_HT_header(Header): layout.separator_spacer() types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', - 'GPENCIL', 'VOLUME', 'HAIR', 'POINTCLOUD'} + 'GPENCIL', 'VOLUME', 'CURVES', 'POINTCLOUD'} # disable material slot buttons when pinned, cannot find correct slot within id_from (T36589) # disable also when the selected object does not support materials has_material_slots = not snode.pin and ob_type in types_that_support_material diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 9d3f20ce4a4..bbe165b9286 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -442,15 +442,6 @@ class OUTLINER_PT_filter(Panel): row.label(icon='BLANK1') row.prop(space, "use_filter_object_others", text="Others") - if bpy.data.libraries: - col.separator() - row = col.row() - row.label(icon='LIBRARY_DATA_OVERRIDE') - row.prop(space, "use_filter_lib_override", text="Library Overrides") - row = col.row() - row.label(icon='LIBRARY_DATA_OVERRIDE') - row.prop(space, "use_filter_lib_override_system", text="System Overrides") - classes = ( OUTLINER_HT_header, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 09716c77917..f9642a2dbb6 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -386,8 +386,8 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa col.prop(edit, "use_duplicate_curve", text="Curve") # col.prop(edit, "use_duplicate_fcurve", text="F-Curve") # Not implemented. col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil") - if hasattr(edit, "use_duplicate_hair"): - col.prop(edit, "use_duplicate_hair", text="Hair") + if hasattr(edit, "use_duplicate_curves"): + col.prop(edit, "use_duplicate_curves", text="Curves") col = flow.column() col.prop(edit, "use_duplicate_lattice", text="Lattice") @@ -461,6 +461,7 @@ class USERPREF_PT_edit_weight_paint(EditingPanel, CenterAlignMixIn, Panel): col.active = view.use_weight_color_range col.template_color_ramp(view, "weight_color_range", expand=True) + class USERPREF_PT_edit_text_editor(EditingPanel, CenterAlignMixIn, Panel): bl_label = "Text Editor" bl_options = {'DEFAULT_CLOSED'} @@ -685,10 +686,10 @@ class USERPREF_PT_viewport_display(ViewportPanel, CenterAlignMixIn, Panel): prefs = context.preferences view = prefs.view - col = layout.column(heading="Show") + col = layout.column(heading="Text Info Overlay") col.prop(view, "show_object_info", text="Object Info") col.prop(view, "show_view_name", text="View Name") - col.prop(view, "show_playback_fps", text="Playback FPS") + col.prop(view, "show_playback_fps", text="Playback Frame Rate (FPS)") layout.separator() @@ -2260,6 +2261,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): context, ( ({"property": "use_sculpt_vertex_colors"}, "T71947"), ({"property": "use_sculpt_tools_tilt"}, "T82877"), + ({"property": "use_select_nearest_on_first_click"}, "T96752"), ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), ({"property": "use_named_attribute_nodes"}, ("T91742")), @@ -2276,6 +2278,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): ({"property": "use_new_curves_type"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), ({"property": "use_full_frame_compositor"}, "T88150"), + ({"property": "enable_eevee_next"}, "T93220"), ), ) @@ -2295,7 +2298,6 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): ({"property": "use_undo_legacy"}, "T60695"), ({"property": "override_auto_resync"}, "T83811"), ({"property": "use_cycles_debug"}, None), - ({"property": "use_geometry_nodes_legacy"}, "T91274"), ({"property": "show_asset_debug_info"}, None), ({"property": "use_asset_indexing"}, None), ), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 7c4ba575f00..f40d96e30a0 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -492,18 +492,30 @@ class _draw_tool_settings_context_mode: header=True ) - UnifiedPaintPanel.prop_unified( - layout, - context, - brush, - "strength", - unified_name="use_unified_strength", - header=True - ) + if brush.curves_sculpt_tool not in {'ADD', 'DELETE'}: + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + header=True + ) - if brush.curves_sculpt_tool == "TEST3": - layout.prop(tool_settings.curves_sculpt, "distance") + if brush.curves_sculpt_tool == 'COMB': + layout.prop(brush, "falloff_shape", expand=True) + layout.prop(brush, "curve_preset") + if brush.curves_sculpt_tool == 'ADD': + layout.prop(brush, "use_frontface") + layout.prop(brush, "falloff_shape", expand=True) + layout.prop(brush.curves_sculpt_settings, "add_amount") + layout.prop(tool_settings.curves_sculpt, "curve_length") + layout.prop(tool_settings.curves_sculpt, "interpolate_length") + layout.prop(tool_settings.curves_sculpt, "interpolate_shape") + + if brush.curves_sculpt_tool == 'TEST1': + layout.prop(tool_settings.curves_sculpt, "distance") class VIEW3D_HT_header(Header): @@ -5285,6 +5297,8 @@ class VIEW3D_MT_pivot_pie(Menu): pie.prop_enum(context.scene.tool_settings, "transform_pivot_point", value='ACTIVE_ELEMENT') if (obj is None) or (mode in {'OBJECT', 'POSE', 'WEIGHT_PAINT'}): pie.prop(context.scene.tool_settings, "use_transform_pivot_point_align") + if mode in {'EDIT_GPENCIL'}: + pie.prop(context.scene.tool_settings.gpencil_sculpt, "use_scale_thickness") class VIEW3D_MT_orientations_pie(Menu): diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index f9d3c988a22..df07fbb3198 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -675,7 +675,7 @@ class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel, Smooth class VIEW3D_PT_tools_weight_gradient(Panel, View3DPaintPanel): # dont give context on purpose to not show this in the generic header toolsettings # this is added only in the gradient tool's ToolDef - #bl_context = ".weightpaint" # dot on purpose (access from topbar) + # bl_context = ".weightpaint" # dot on purpose (access from topbar) bl_label = "Falloff" bl_options = {'DEFAULT_CLOSED'} # also dont draw as an extra panel in the sidebar (already included in the Brush settings) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 705b98c4d11..e7c00f4915e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -71,18 +71,6 @@ def curve_node_items(context): space = context.space_data if not space: return - - if geometry_nodes_legacy_poll(context): - yield NodeItem("GeometryNodeLegacyCurveEndpoints") - yield NodeItem("GeometryNodeLegacyCurveReverse") - yield NodeItem("GeometryNodeLegacyCurveSubdivide") - yield NodeItem("GeometryNodeLegacyCurveToPoints") - yield NodeItem("GeometryNodeLegacyMeshToCurve") - yield NodeItem("GeometryNodeLegacyCurveSelectHandles") - yield NodeItem("GeometryNodeLegacyCurveSetHandles") - yield NodeItem("GeometryNodeLegacyCurveSplineType") - yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeCurveLength") yield NodeItem("GeometryNodeCurveToMesh") yield NodeItem("GeometryNodeCurveToPoints") @@ -119,12 +107,6 @@ def mesh_node_items(context): space = context.space_data if not space: return - - if geometry_nodes_legacy_poll(context): - yield NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll) - yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeDualMesh") yield NodeItem("GeometryNodeExtrudeMesh") yield NodeItem("GeometryNodeFlipFaces") @@ -156,12 +138,6 @@ def geometry_node_items(context): space = context.space_data if not space: return - - if geometry_nodes_legacy_poll(context): - yield NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll) - yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeBoundBox") yield NodeItem("GeometryNodeConvexHull") yield NodeItem("GeometryNodeDeleteGeometry") @@ -185,11 +161,6 @@ def geometry_input_node_items(context): space = context.space_data if not space: return - - if geometry_nodes_legacy_poll(context): - yield NodeItem("FunctionNodeLegacyRandomFloat") - yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("FunctionNodeInputBool") yield NodeItem("GeometryNodeCollectionInfo") yield NodeItem("FunctionNodeInputColor") @@ -217,12 +188,6 @@ def geometry_material_node_items(context): space = context.space_data if not space: return - - if geometry_nodes_legacy_poll(context): - yield NodeItem("GeometryNodeLegacyMaterialAssign") - yield NodeItem("GeometryNodeLegacySelectByMaterial") - yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeReplaceMaterial") yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) yield NodeItem("GeometryNodeInputMaterialIndex") @@ -238,17 +203,6 @@ def point_node_items(context): space = context.space_data if not space: return - - if geometry_nodes_legacy_poll(context): - yield NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_legacy_poll) - yield NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_legacy_poll) - yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) - yield NodeItem("GeometryNodeDistributePointsOnFaces") yield NodeItem("GeometryNodePointsToVertices") yield NodeItem("GeometryNodePointsToVolume") @@ -356,10 +310,6 @@ def object_eevee_cycles_shader_nodes_poll(context): eevee_cycles_shader_nodes_poll(context)) -def geometry_nodes_legacy_poll(context): - return context.preferences.experimental.use_geometry_nodes_legacy - - def named_attribute_poll(context): return context.preferences.experimental.use_named_attribute_nodes @@ -666,25 +616,6 @@ texture_node_categories = [ geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ - NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeCaptureAttribute"), NodeItem("GeometryNodeAttributeDomainSize"), NodeItem("GeometryNodeAttributeStatistic"), @@ -780,9 +711,6 @@ geometry_node_categories = [ NodeItem("ShaderNodeVectorRotate"), ]), GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ - NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeLegacyVolumeToMesh", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeVolumeToMesh"), ]), GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items), diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index dee163a9797..deff45d0350 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -19,6 +19,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_constraint_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curve_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curveprofile_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curves_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_customdata_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_defs.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_documentation.h @@ -31,7 +32,6 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_modifier_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h - ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curves_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 3817fe59f95..058b0f120f7 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -44,7 +44,7 @@ int BLF_load_mem_unique(const char *name, const unsigned char *mem, int mem_size void BLF_unload(const char *name) ATTR_NONNULL(); void BLF_unload_id(int fontid); -char *BLF_display_name_from_file(const char *filename); +char *BLF_display_name_from_file(const char *filepath); /** * Check if font supports a particular glyph. @@ -279,7 +279,7 @@ void BLF_dir_free(char **dirs, int count) ATTR_NONNULL(); * * \note called from a thread, so it bypasses the normal BLF_* api (which isn't thread-safe). */ -void BLF_thumb_preview(const char *filename, +void BLF_thumb_preview(const char *filepath, const char **draw_str, const char **i18n_draw_str, unsigned char draw_str_lines, diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 043d6da7d73..2b5a2cdf606 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -129,7 +129,7 @@ bool BLF_has_glyph(int fontid, unsigned int unicode) { FontBLF *font = blf_get(fontid); if (font) { - return FT_Get_Char_Index(font->face, unicode) != 0; + return FT_Get_Char_Index(font->face, unicode) != FT_Err_Ok; } return false; } @@ -158,14 +158,14 @@ int BLF_load_unique(const char *name) return -1; } - char *filename = blf_dir_search(name); - if (!filename) { + char *filepath = blf_dir_search(name); + if (!filepath) { printf("Can't find font: %s\n", name); return -1; } - FontBLF *font = blf_font_new(name, filename); - MEM_freeN(filename); + FontBLF *font = blf_font_new(name, filepath); + MEM_freeN(filepath); if (!font) { printf("Can't load font: %s\n", name); @@ -869,9 +869,9 @@ void BLF_draw_buffer(int fontid, const char *str, const size_t str_len) BLF_draw_buffer_ex(fontid, str, str_len, NULL); } -char *BLF_display_name_from_file(const char *filename) +char *BLF_display_name_from_file(const char *filepath) { - FontBLF *font = blf_font_new("font_name", filename); + FontBLF *font = blf_font_new("font_name", filepath); if (!font) { return NULL; } diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c index 69b44ed9b01..8534a8c583f 100644 --- a/source/blender/blenfont/intern/blf_dir.c +++ b/source/blender/blenfont/intern/blf_dir.c @@ -132,12 +132,12 @@ char *blf_dir_search(const char *file) return s; } -char *blf_dir_metrics_search(const char *filename) +char *blf_dir_metrics_search(const char *filepath) { char *mfile; char *s; - mfile = BLI_strdup(filename); + mfile = BLI_strdup(filepath); s = strrchr(mfile, '.'); if (s) { if (BLI_strnlen(s, 4) < 4) { diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index c4bd22d1310..60ff5f6470f 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -1213,14 +1213,14 @@ static void blf_font_fill(FontBLF *font) font->glyph_cache_mutex = &blf_glyph_cache_mutex; } -FontBLF *blf_font_new(const char *name, const char *filename) +FontBLF *blf_font_new(const char *name, const char *filepath) { FontBLF *font; FT_Error err; char *mfile; font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new"); - err = FT_New_Face(ft_lib, filename, 0, &font->face); + err = FT_New_Face(ft_lib, filepath, 0, &font->face); if (err) { if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) { printf("Format of this font file is not supported\n"); @@ -1246,17 +1246,17 @@ FontBLF *blf_font_new(const char *name, const char *filename) return NULL; } - mfile = blf_dir_metrics_search(filename); + mfile = blf_dir_metrics_search(filepath); if (mfile) { err = FT_Attach_File(font->face, mfile); if (err) { - fprintf(stderr, "FT_Attach_File failed to load '%s' with error %d\n", filename, (int)err); + fprintf(stderr, "FT_Attach_File failed to load '%s' with error %d\n", filepath, (int)err); } MEM_freeN(mfile); } font->name = BLI_strdup(name); - font->filename = BLI_strdup(filename); + font->filepath = BLI_strdup(filepath); blf_font_fill(font); if (FT_HAS_KERNING(font->face)) { @@ -1303,7 +1303,7 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m } font->name = BLI_strdup(name); - font->filename = NULL; + font->filepath = NULL; blf_font_fill(font); return font; } @@ -1317,8 +1317,8 @@ void blf_font_free(FontBLF *font) } FT_Done_Face(font->face); - if (font->filename) { - MEM_freeN(font->filename); + if (font->filepath) { + MEM_freeN(font->filepath); } if (font->name) { MEM_freeN(font->name); @@ -1340,7 +1340,7 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi) size = (float)ft_size / 64.0f; if (font->size != size || font->dpi != dpi) { - if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == 0) { + if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == FT_Err_Ok) { font->size = size; font->dpi = dpi; } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 4810e46b1d2..28531c5afc1 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -258,7 +258,7 @@ static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index) } } - if (FT_Load_Glyph(font->face, glyph_index, load_flags) == 0) { + if (FT_Load_Glyph(font->face, glyph_index, load_flags) == FT_Err_Ok) { return font->face->glyph; } return NULL; @@ -280,7 +280,7 @@ static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph) /* Render the glyph curves to a bitmap. */ FT_Error err = FT_Render_Glyph(glyph, render_mode); - if (err != 0) { + if (err != FT_Err_Ok) { return false; } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 14e1a8f72d1..81a1460df4d 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -25,7 +25,7 @@ char *blf_dir_search(const char *file); * Some font have additional file with metrics information, * in general, the extension of the file is: `.afm` or `.pfm` */ -char *blf_dir_metrics_search(const char *filename); +char *blf_dir_metrics_search(const char *filepath); /* int blf_dir_split(const char *str, char *file, int *size); */ /* UNUSED */ int blf_font_init(void); @@ -36,7 +36,7 @@ bool blf_font_id_is_valid(int fontid); void blf_draw_buffer__start(struct FontBLF *font); void blf_draw_buffer__end(void); -struct FontBLF *blf_font_new(const char *name, const char *filename); +struct FontBLF *blf_font_new(const char *name, const char *filepath); struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size); void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 61b6edc4974..23dc2fda38c 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -147,8 +147,8 @@ typedef struct FontBLF { /* # of times this font was loaded */ unsigned int reference_count; - /* filename or NULL. */ - char *filename; + /** File-path or NULL. */ + char *filepath; /* aspect ratio or scale. */ float aspect[3]; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index 9d9a8a51d0f..0e265fb7553 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -29,7 +29,7 @@ #include "BLI_strict_flags.h" -void BLF_thumb_preview(const char *filename, +void BLF_thumb_preview(const char *filepath, const char **draw_str, const char **i18n_draw_str, const unsigned char draw_str_lines, @@ -49,9 +49,9 @@ void BLF_thumb_preview(const char *filename, FontBLF *font; /* Create a new blender font obj and fill it with default values */ - font = blf_font_new("thumb_font", filename); + font = blf_font_new("thumb_font", filepath); if (!font) { - printf("Info: Can't load font '%s', no preview possible\n", filename); + printf("Info: Can't load font '%s', no preview possible\n", filepath); return; } @@ -73,7 +73,7 @@ void BLF_thumb_preview(const char *filename, for (int i = 0; i < draw_str_lines; i++) { const char *draw_str_i18n = i18n_draw_str[i] != NULL ? i18n_draw_str[i] : draw_str[i]; const size_t draw_str_i18n_len = strlen(draw_str_i18n); - int draw_str_i18n_nbr = 0; + int draw_str_i18_count = 0; CLAMP_MIN(font_size_curr, font_size_min); if (!blf_font_size(font, (float)font_size_curr, dpi)) { @@ -91,8 +91,8 @@ void BLF_thumb_preview(const char *filename, * since many fonts will then show nothing but ugly 'missing char' in their preview). * Does not handle all cases, but much better than nothing. */ - if (blf_font_count_missing_chars(font, draw_str_i18n, draw_str_i18n_len, &draw_str_i18n_nbr) > - (draw_str_i18n_nbr / 2)) { + if (blf_font_count_missing_chars(font, draw_str_i18n, draw_str_i18n_len, &draw_str_i18_count) > + (draw_str_i18_count / 2)) { blf_font_draw_buffer(font, draw_str[i], strlen(draw_str[i]), NULL); } else { diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 500f386dcc0..8d449a124ec 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -4,15 +4,13 @@ #include <mutex> -#include "FN_cpp_type.hh" -#include "FN_generic_span.hh" -#include "FN_generic_virtual_array.hh" - #include "BKE_anonymous_attribute.hh" #include "BKE_attribute.h" #include "BLI_color.hh" #include "BLI_function_ref.hh" +#include "BLI_generic_span.hh" +#include "BLI_generic_virtual_array.hh" #include "BLI_math_vec_types.hh" /** @@ -133,9 +131,9 @@ struct AttributeInitDefault : public AttributeInit { * Note that this can be used to fill the new attribute with the default */ struct AttributeInitVArray : public AttributeInit { - blender::fn::GVArray varray; + blender::GVArray varray; - AttributeInitVArray(blender::fn::GVArray varray) + AttributeInitVArray(blender::GVArray varray) : AttributeInit(Type::VArray), varray(std::move(varray)) { } @@ -166,12 +164,6 @@ using AttributeForeachCallback = blender::FunctionRef<bool( namespace blender::bke { -using fn::CPPType; -using fn::GVArray; -using fn::GVMutableArray; - -const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); -CustomDataType cpp_type_to_custom_data_type(const CPPType &type); CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types); /** * Domains with a higher "information density" have a higher priority, @@ -239,7 +231,7 @@ class OutputAttribute { GVMutableArray varray_; AttributeDomain domain_ = ATTR_DOMAIN_AUTO; SaveFn save_; - std::unique_ptr<fn::GVMutableArray_GSpan> optional_span_varray_; + std::unique_ptr<GVMutableArray_GSpan> optional_span_varray_; bool ignore_old_values_ = false; bool save_has_been_called_ = false; @@ -256,13 +248,13 @@ class OutputAttribute { operator bool() const; GVMutableArray &operator*(); - fn::GVMutableArray *operator->(); + GVMutableArray *operator->(); GVMutableArray &varray(); AttributeDomain domain() const; const CPPType &cpp_type() const; CustomDataType custom_data_type() const; - fn::GMutableSpan as_span(); + GMutableSpan as_span(); template<typename T> MutableSpan<T> as_span(); void save(); @@ -376,27 +368,27 @@ class CustomDataAttributes { void clear(); - std::optional<blender::fn::GSpan> get_for_read(const AttributeIDRef &attribute_id) const; + std::optional<blender::GSpan> get_for_read(const AttributeIDRef &attribute_id) const; /** * Return a virtual array for a stored attribute, or a single value virtual array with the * default value if the attribute doesn't exist. If no default value is provided, the default * value for the type will be used. */ - blender::fn::GVArray get_for_read(const AttributeIDRef &attribute_id, - const CustomDataType data_type, - const void *default_value) const; + blender::GVArray get_for_read(const AttributeIDRef &attribute_id, + const CustomDataType data_type, + const void *default_value) const; template<typename T> blender::VArray<T> get_for_read(const AttributeIDRef &attribute_id, const T &default_value) const { - const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const blender::CPPType &cpp_type = blender::CPPType::get<T>(); const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); GVArray varray = this->get_for_read(attribute_id, type, &default_value); return varray.typed<T>(); } - std::optional<blender::fn::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id); + std::optional<blender::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id); bool create(const AttributeIDRef &attribute_id, const CustomDataType data_type); bool create_by_move(const AttributeIDRef &attribute_id, const CustomDataType data_type, @@ -516,7 +508,7 @@ inline GVMutableArray &OutputAttribute::operator*() return varray_; } -inline fn::GVMutableArray *OutputAttribute::operator->() +inline GVMutableArray *OutputAttribute::operator->() { return &varray_; } diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 42bff89922b..9efa64d1474 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -4,78 +4,38 @@ #include "BLI_array.hh" #include "BLI_color.hh" +#include "BLI_cpp_type.hh" #include "BLI_math_vector.h" #include "BLI_math_vector.hh" -#include "DNA_customdata_types.h" - -#include "FN_cpp_type.hh" +#include "BKE_customdata.h" namespace blender::attribute_math { -using fn::CPPType; - /** - * Utility function that simplifies calling a templated function based on a custom data type. + * Utility function that simplifies calling a templated function based on a run-time data type. */ template<typename Func> -inline void convert_to_static_type(const CustomDataType data_type, const Func &func) +inline void convert_to_static_type(const CPPType &cpp_type, const Func &func) { - switch (data_type) { - case CD_PROP_FLOAT: - func(float()); - break; - case CD_PROP_FLOAT2: - func(float2()); - break; - case CD_PROP_FLOAT3: - func(float3()); - break; - case CD_PROP_INT32: - func(int()); - break; - case CD_PROP_BOOL: - func(bool()); - break; - case CD_PROP_INT8: - func(int8_t()); - break; - case CD_PROP_COLOR: - func(ColorGeometry4f()); - break; - default: - BLI_assert_unreachable(); - break; - } + cpp_type.to_static_type_tag<float, float2, float3, int, bool, int8_t, ColorGeometry4f>( + [&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (std::is_same_v<T, void>) { + /* It's expected that the given cpp type is one of the supported ones. */ + BLI_assert_unreachable(); + } + else { + func(T()); + } + }); } template<typename Func> -inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func) +inline void convert_to_static_type(const CustomDataType data_type, const Func &func) { - if (cpp_type.is<float>()) { - func(float()); - } - else if (cpp_type.is<float2>()) { - func(float2()); - } - else if (cpp_type.is<float3>()) { - func(float3()); - } - else if (cpp_type.is<int>()) { - func(int()); - } - else if (cpp_type.is<bool>()) { - func(bool()); - } - else if (cpp_type.is<int8_t>()) { - func(int8_t()); - } - else if (cpp_type.is<ColorGeometry4f>()) { - func(ColorGeometry4f()); - } - else { - BLI_assert_unreachable(); - } + const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(data_type); + convert_to_static_type(cpp_type, func); } /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 4b84c0cfe23..a98f4802991 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -52,6 +52,9 @@ bool BKE_brush_delete(struct Main *bmain, struct Brush *brush); * Add grease pencil settings. */ void BKE_brush_init_gpencil_settings(struct Brush *brush); + +void BKE_brush_init_curves_sculpt_settings(struct Brush *brush); + struct Brush *BKE_brush_first_search(struct Main *bmain, eObjectMode ob_mode); void BKE_brush_sculpt_reset(struct Brush *brush); diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index 394d97223e3..42897c59043 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -274,7 +274,7 @@ bool BKE_nurb_valid_message(int pnts, short flag, short type, bool is_surf, - const char *dir, + int dir, char *message_dst, size_t maxncpy); diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 875cf48ed43..0f2ae8a02a6 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -12,6 +12,7 @@ #include <mutex> #include "BLI_float4x4.hh" +#include "BLI_generic_virtual_array.hh" #include "BLI_index_mask.hh" #include "BLI_math_vec_types.hh" #include "BLI_span.hh" @@ -21,10 +22,36 @@ #include "BKE_attribute_access.hh" -#include "FN_generic_virtual_array.hh" - namespace blender::bke { +template<typename T, BLI_ENABLE_IF(std::is_integral_v<T>)> +constexpr IndexRange offsets_to_range(Span<T> offsets, int64_t index) +{ + BLI_assert(index >= 0); + BLI_assert(index < offsets.size()); + + const int offset = offsets[index]; + const int offset_next = offsets[index + 1]; + return {offset, offset_next - offset}; +} + +namespace curves::nurbs { + +struct BasisCache { + /** + * For each evaluated point, the weight for all control points that influences it. + * The vector's size is the evaluated point count multiplied by the spline's order. + */ + Vector<float> weights; + /** + * For each evaluated point, an offset into the curve's control points for the start of #weights. + * In other words, the index of the first control point that influences this evaluated point. + */ + Vector<int> start_indices; +}; + +} // namespace curves::nurbs + /** * Contains derived data, caches, and other information not saved in files, besides a few pointers * to arrays that are kept in the non-runtime struct to avoid dereferencing this whenever they are @@ -32,6 +59,19 @@ namespace blender::bke { */ class CurvesGeometryRuntime { public: + /** + * Cache of offsets into the evaluated array for each curve, accounting for all previous + * evaluated points, Bezier curve vector segments, different resolutions per spline, etc. + */ + mutable Vector<int> evaluated_offsets_cache; + mutable Vector<int> bezier_evaluated_offsets; + mutable std::mutex offsets_cache_mutex; + mutable bool offsets_cache_dirty = true; + + mutable Vector<curves::nurbs::BasisCache> nurbs_basis_cache; + mutable std::mutex nurbs_basis_cache_mutex; + mutable bool nurbs_basis_cache_dirty = true; + /** Cache of evaluated positions. */ mutable Vector<float3> evaluated_position_cache; mutable std::mutex position_cache_mutex; @@ -82,31 +122,104 @@ class CurvesGeometry : public ::CurvesGeometry { * Accessors. */ - int points_size() const; - int curves_size() const; + int points_num() const; + int curves_num() const; IndexRange points_range() const; IndexRange curves_range() const; /** - * The total number of points in the evaluated poly curve. - * This can depend on the resolution attribute if it exists. + * The index of the first point in every curve. The size of this span is one larger than the + * number of curves. Consider using #points_for_curve rather than using the offsets directly. */ - int evaluated_points_size() const; + Span<int> offsets() const; + MutableSpan<int> offsets(); /** * Access a range of indices of point data for a specific curve. */ - IndexRange range_for_curve(int index) const; - IndexRange range_for_curves(IndexRange curves) const; + IndexRange points_for_curve(int index) const; + IndexRange points_for_curves(IndexRange curves) const; /** The type (#CurveType) of each curve, or potentially a single if all are the same type. */ VArray<int8_t> curve_types() const; /** Mutable access to curve types. Call #tag_topology_changed after changing any type. */ MutableSpan<int8_t> curve_types(); + bool has_curve_with_type(const CurveType type) const; + MutableSpan<float3> positions(); Span<float3> positions() const; + /** Whether the curve loops around to connect to itself, on the curve domain. */ + VArray<bool> cyclic() const; + /** Mutable access to curve cyclic values. Call #tag_topology_changed after changes. */ + MutableSpan<bool> cyclic(); + + /** + * How many evaluated points to create for each segment when evaluating Bezier, + * Catmull Rom, and NURBS curves. On the curve domain. + */ + VArray<int> resolution() const; + /** Mutable access to curve resolution. Call #tag_topology_changed after changes. */ + MutableSpan<int> resolution(); + + /** + * Handle types for Bezier control points. Call #tag_topology_changed after changes. + */ + VArray<int8_t> handle_types_left() const; + MutableSpan<int8_t> handle_types_left(); + VArray<int8_t> handle_types_right() const; + MutableSpan<int8_t> handle_types_right(); + + /** + * The positions of Bezier curve handles. Though these are really control points for the Bezier + * segments, they are stored in separate arrays to better reflect user expectations. Note that + * values may be generated automatically based on the handle types. Call #tag_positions_changed + * after changes. + */ + Span<float3> handle_positions_left() const; + MutableSpan<float3> handle_positions_left(); + Span<float3> handle_positions_right() const; + MutableSpan<float3> handle_positions_right(); + + /** + * The order (degree plus one) of each NURBS curve, on the curve domain. + * Call #tag_topology_changed after changes. + */ + VArray<int8_t> nurbs_orders() const; + MutableSpan<int8_t> nurbs_orders(); + + /** + * The automatic generation mode for each NURBS curve's knots vector, on the curve domain. + * Call #tag_topology_changed after changes. + */ + VArray<int8_t> nurbs_knots_modes() const; + MutableSpan<int8_t> nurbs_knots_modes(); + + /** + * The weight for each control point for NURBS curves. Call #tag_positions_changed after changes. + */ + Span<float> nurbs_weights() const; + MutableSpan<float> nurbs_weights(); + + /** + * The index of a triangle (#MLoopTri) that a curve is attached to. + * The index is -1, if the curve is not attached. + */ + VArray<int> surface_triangle_indices() const; + MutableSpan<int> surface_triangle_indices(); + + /** + * Barycentric coordinates of the attachment point within a triangle. + * Only the first two coordinates are stored. The third coordinate can be derived because the sum + * of the three coordinates is 1. + * + * When the triangle index is -1, this coordinate should be ignored. + * The span can be empty, when all triangle indices are -1. + */ + Span<float2> surface_triangle_coords() const; + MutableSpan<float2> surface_triangle_coords(); + /** * Calculate the largest and smallest position values, only including control points * (rather than evaluated points). The existing values of `min` and `max` are taken into account. @@ -115,25 +228,58 @@ class CurvesGeometry : public ::CurvesGeometry { */ bool bounds_min_max(float3 &min, float3 &max) const; + private: /** - * The index of the first point in every curve. The size of this span is one larger than the - * number of curves. Consider using #range_for_curve rather than using the offsets directly. + * All of the curve indices for curves with a specific type. */ - Span<int> offsets() const; - MutableSpan<int> offsets(); + IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const; - VArray<bool> cyclic() const; - MutableSpan<bool> cyclic(); + /* -------------------------------------------------------------------- + * Evaluation. + */ + + public: + /** + * The total number of points in the evaluated poly curve. + * This can depend on the resolution attribute if it exists. + */ + int evaluated_points_num() const; + + /** + * Access a range of indices of point data for a specific curve. + * Call #evaluated_offsets() first to ensure that the evaluated offsets cache is current. + */ + IndexRange evaluated_points_for_curve(int index) const; + IndexRange evaluated_points_for_curves(IndexRange curves) const; + + /** + * The index of the first evaluated point for every curve. The size of this span is one larger + * than the number of curves. Consider using #evaluated_points_for_curve rather than using the + * offsets directly. + */ + Span<int> evaluated_offsets() const; + + /** Makes sure the data described by #evaluated_offsets if necessary. */ + void ensure_evaluated_offsets() const; + + Span<float3> evaluated_positions() const; + + private: + /** + * Make sure the basis weights for NURBS curve's evaluated points are calculated. + */ + void ensure_nurbs_basis_cache() const; /* -------------------------------------------------------------------- * Operations. */ + public: /** * Change the number of elements. New values for existing attributes should be properly * initialized afterwards. */ - void resize(int point_size, int curve_size); + void resize(int points_num, int curves_num); /** Call after deforming the position attribute. */ void tag_positions_changed(); @@ -152,20 +298,178 @@ class CurvesGeometry : public ::CurvesGeometry { void remove_curves(IndexMask curves_to_delete); + /** + * Change the direction of selected curves (switch the start and end) without changing their + * shape. + */ + void reverse_curves(IndexMask curves_to_reverse); + /* -------------------------------------------------------------------- * Attributes. */ - fn::GVArray adapt_domain(const fn::GVArray &varray, - AttributeDomain from, - AttributeDomain to) const; + GVArray adapt_domain(const GVArray &varray, AttributeDomain from, AttributeDomain to) const; }; -Curves *curves_new_nomain(int point_size, int curves_size); +namespace curves { + +/** + * The number of segments between control points, accounting for the last segment of cyclic curves, + * and the fact that curves with two points cannot be cyclic. The logic is simple, but this + * function should be used to make intentions clearer. + */ +inline int curve_segment_size(const int points_num, const bool cyclic) +{ + return (cyclic && points_num > 2) ? points_num : points_num - 1; +} + +namespace bezier { + +/** + * Return true if the handles that make up a segment both have a vector type. Vector segments for + * Bezier curves have special behavior because they aren't divided into many evaluated points. + */ +bool segment_is_vector(Span<int8_t> handle_types_left, + Span<int8_t> handle_types_right, + int segment_index); + +/** + * Return true if the curve's last cylic segment has a vector type. + * This only makes a difference in the shape of cyclic curves. + */ +bool last_cylic_segment_is_vector(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right); + +/** + * Calculate offsets into the curve's evaluated points for each control point. While most control + * point edges generate the number of edges specified by the resolution, vector segments only + * generate one edge. + * + * The size of the offsets array must be the same as the number of points. The value at each index + * is the evaluated point offset including the following segment. + */ +void calculate_evaluated_offsets(Span<int8_t> handle_types_left, + Span<int8_t> handle_types_right, + bool cyclic, + int resolution, + MutableSpan<int> evaluated_offsets); + +/** + * Evaluate a cubic Bezier segment, using the "forward differencing" method. + * A generic Bezier curve is made up by four points, but in many cases the first and last points + * are referred to as the control points, and the middle points are the corresponding handles. + */ +void evaluate_segment(const float3 &point_0, + const float3 &point_1, + const float3 &point_2, + const float3 &point_3, + MutableSpan<float3> result); + +/** + * Calculate all evaluated points for the Bezier curve. + * + * \param evaluated_offsets: The index in the evaluated points array for each control point, + * including the points from the corresponding segment. Used to vary the number of evaluated + * points per segment, i.e. to make vector segment only have one edge. This is expected to be + * calculated by #calculate_evaluated_offsets, and is the reason why this function doesn't need + * arguments like "cyclic" and "resolution". + */ +void calculate_evaluated_positions(Span<float3> positions, + Span<float3> handles_left, + Span<float3> handles_right, + Span<int> evaluated_offsets, + MutableSpan<float3> evaluated_positions); + +} // namespace bezier + +namespace catmull_rom { + +/** + * Calculate the number of evaluated points that #interpolate_to_evaluated is expected to produce. + * \param points_num: The number of points in the curve. + * \param resolution: The resolution for each segment. + */ +int calculate_evaluated_size(int points_num, bool cyclic, int resolution); + +/** + * Evaluate the Catmull Rom curve. The length of the #dst span should be calculated with + * #calculate_evaluated_size and is expected to divide evenly by the #src span's segment size. + */ +void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst); + +} // namespace catmull_rom + +namespace nurbs { + +/** + * Checks the conditions that a NURBS curve needs to evaluate. + */ +bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); + +/** + * Calculate the standard evaluated size for a NURBS curve, using the standard that + * the resolution is multiplied by the number of segments between the control points. + * + * \note Though the number of evaluated points is rather arbitrary, it's useful to have a standard + * for predictability and so that cached basis weights of NURBS curves with these properties can be + * shared. + */ +int calculate_evaluated_size( + int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode); + +/** + * Calculate the length of the knot vector for a NURBS curve with the given properties. + * The knots must be longer for a cyclic curve, for example, in order to provide weights for the + * last evaluated points that are also influenced by the first control points. + */ +int knots_size(int points_num, int8_t order, bool cyclic); + +/** + * Calculate the knots for a spline given its properties, based on built-in standards defined by + * #KnotsMode. + * + * \note Theoretically any sorted values can be used for NURBS knots, but calculating based + * on standard modes allows useful presets, automatic recalculation when the number of points + * changes, and is generally more intuitive than defining the knot vector manually. + */ +void calculate_knots( + int points_num, KnotsMode mode, int8_t order, bool cyclic, MutableSpan<float> knots); + +/** + * Based on the knots, the order, and other properties of a NURBS curve, calculate a cache that can + * be used to more simply interpolate attributes to the evaluated points later. The cache includes + * two pieces of information for every evaluated point: the first control point that influences it, + * and a weight for each control point. + */ +void calculate_basis_cache(int points_num, + int evaluated_size, + int8_t order, + bool cyclic, + Span<float> knots, + BasisCache &basis_cache); + +/** + * Using a "basis cache" generated by #BasisCache, interpolate attribute values to the evaluated + * points. The number of evaluated points is determined by the #basis_cache argument. + * + * \param control_weights: An optional span of control point weights, which must have the same size + * as the number of control points in the curve if provided. Using this argument gives a NURBS + * curve the "Rational" behavior that's part of its acronym; otherwise it is a NUBS. + */ +void interpolate_to_evaluated(const BasisCache &basis_cache, + int8_t order, + Span<float> control_weights, + GSpan src, + GMutableSpan dst); + +} // namespace nurbs + +} // namespace curves + +Curves *curves_new_nomain(int points_num, int curves_num); /** * Create a new curves data-block containing a single curve with the given length and type. */ -Curves *curves_new_nomain_single(int point_size, CurveType type); +Curves *curves_new_nomain_single(int points_num, CurveType type); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 940dc3c4f6c..6b805a4c29d 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -589,7 +589,7 @@ void CustomData_layers__print(struct CustomData *data); /* External file storage */ void CustomData_external_add( - struct CustomData *data, struct ID *id, int type, int totelem, const char *filename); + struct CustomData *data, struct ID *id, int type, int totelem, const char *filepath); void CustomData_external_remove(struct CustomData *data, struct ID *id, int type, int totelem); bool CustomData_external_test(struct CustomData *data, int type); @@ -751,3 +751,12 @@ void CustomData_debug_info_from_layers(const struct CustomData *data, #ifdef __cplusplus } #endif + +#ifdef __cplusplus +# include "BLI_cpp_type.hh" + +namespace blender::bke { +const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); +CustomDataType cpp_type_to_custom_data_type(const CPPType &type); +} // namespace blender::bke +#endif diff --git a/source/blender/blenkernel/BKE_dynamicpaint.h b/source/blender/blenkernel/BKE_dynamicpaint.h index 5a1c99774fd..109d3e6d977 100644 --- a/source/blender/blenkernel/BKE_dynamicpaint.h +++ b/source/blender/blenkernel/BKE_dynamicpaint.h @@ -129,7 +129,7 @@ int dynamicPaint_calculateFrame(struct DynamicPaintSurface *surface, struct Object *cObject, int frame); void dynamicPaint_outputSurfaceImage(struct DynamicPaintSurface *surface, - char *filename, + const char *filepath, short output_layer); /* PaintPoint state */ diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 0e121068cbc..bd392057436 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -131,9 +131,9 @@ class GeometryComponent { * interpolate from one domain to another. * \return null if the interpolation is not implemented. */ - blender::fn::GVArray attribute_try_adapt_domain(const blender::fn::GVArray &varray, - const AttributeDomain from_domain, - const AttributeDomain to_domain) const + blender::GVArray attribute_try_adapt_domain(const blender::GVArray &varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { return this->attribute_try_adapt_domain_impl(varray, from_domain, to_domain); } @@ -177,17 +177,17 @@ class GeometryComponent { * and converted to the data type. Returns null when the attribute does not exist or cannot be * interpolated or converted. */ - blender::fn::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id, - AttributeDomain domain, - const CustomDataType data_type) const; + blender::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id, + AttributeDomain domain, + const CustomDataType data_type) const; /** * Get a virtual array that refers to the data of an attribute, interpolated to the given domain. * The data type is left unchanged. Returns null when the attribute does not exist or cannot be * interpolated. */ - blender::fn::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id, - AttributeDomain domain) const; + blender::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id, + AttributeDomain domain) const; /** * Get a virtual array that refers to the data of an attribute converted to the given data type. @@ -202,17 +202,17 @@ class GeometryComponent { * and converted to the data type. If that is not possible, the returned virtual array will * contain a default value. This never returns null. */ - blender::fn::GVArray attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id, - AttributeDomain domain, - const CustomDataType data_type, - const void *default_value = nullptr) const; + blender::GVArray attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id, + AttributeDomain domain, + const CustomDataType data_type, + const void *default_value = nullptr) const; /* Use instead of the method above when the type is known at compile time for type safety. */ template<typename T> blender::VArray<T> attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const T &default_value) const { - const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const blender::CPPType &cpp_type = blender::CPPType::get<T>(); const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); return this->attribute_get_for_read(attribute_id, domain, type, &default_value) .template typed<T>(); @@ -240,7 +240,7 @@ class GeometryComponent { const AttributeDomain domain, const T default_value) { - const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const blender::CPPType &cpp_type = blender::CPPType::get<T>(); const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value); } @@ -260,7 +260,7 @@ class GeometryComponent { blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only( const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) { - const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const blender::CPPType &cpp_type = blender::CPPType::get<T>(); const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); return this->attribute_try_get_for_output_only(attribute_id, domain, data_type); } @@ -268,9 +268,9 @@ class GeometryComponent { private: virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const; - virtual blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray, - AttributeDomain from_domain, - AttributeDomain to_domain) const; + virtual blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, + AttributeDomain from_domain, + AttributeDomain to_domain) const; }; template<typename T> @@ -568,9 +568,9 @@ class MeshComponent : public GeometryComponent { private: const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; - blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray, - AttributeDomain from_domain, - AttributeDomain to_domain) const final; + blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, + AttributeDomain from_domain, + AttributeDomain to_domain) const final; }; /** @@ -672,9 +672,9 @@ class CurveComponentLegacy : public GeometryComponent { private: const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; - blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray, - AttributeDomain from_domain, - AttributeDomain to_domain) const final; + blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, + AttributeDomain from_domain, + AttributeDomain to_domain) const final; }; /** @@ -729,9 +729,9 @@ class CurveComponent : public GeometryComponent { private: const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; - blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray, - AttributeDomain from_domain, - AttributeDomain to_domain) const final; + blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, + AttributeDomain from_domain, + AttributeDomain to_domain) const final; }; /** diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index e586bc4247d..60564d1282e 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -34,7 +34,8 @@ struct bGPDlayer_Mask; struct bGPDstroke; struct bGPdata; -#define GPENCIL_SIMPLIFY(scene) (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE) +#define GPENCIL_SIMPLIFY(scene) \ + ((scene->r.mode & R_SIMPLIFY) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)) #define GPENCIL_SIMPLIFY_ONPLAY(playing) \ (((playing == true) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY)) || \ ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY) == 0)) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index ea0202e3b5f..fba2512fa49 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -64,7 +64,7 @@ struct StampData *BKE_stamp_info_from_scene_static(const struct Scene *scene); * Check whether the given metadata field name translates to a known field of a stamp. */ bool BKE_stamp_is_known_field(const char *field_name); -void BKE_imbuf_stamp_info(struct RenderResult *rr, struct ImBuf *ibuf); +void BKE_imbuf_stamp_info(const struct RenderResult *rr, struct ImBuf *ibuf); void BKE_stamp_info_from_imbuf(struct RenderResult *rr, struct ImBuf *ibuf); void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, @@ -82,15 +82,14 @@ void BKE_image_stamp_buf(struct Scene *scene, int height, int channels); bool BKE_imbuf_alpha_test(struct ImBuf *ibuf); -int BKE_imbuf_write_stamp(struct Scene *scene, - struct RenderResult *rr, +int BKE_imbuf_write_stamp(const struct Scene *scene, + const struct RenderResult *rr, struct ImBuf *ibuf, const char *name, const struct ImageFormatData *imf); /** * \note imf->planes is ignored here, its assumed the image channels are already set. */ -void BKE_imbuf_write_prepare(struct ImBuf *ibuf, const struct ImageFormatData *imf); int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, const struct ImageFormatData *imf); /** * Same as #BKE_imbuf_write() but crappy workaround not to permanently modify _some_, @@ -100,43 +99,6 @@ int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf, bool save_copy); -void BKE_image_path_from_imformat(char *string, - const char *base, - const char *relbase, - int frame, - const struct ImageFormatData *im_format, - bool use_ext, - bool use_frames, - const char *suffix); -void BKE_image_path_from_imtype(char *string, - const char *base, - const char *relbase, - int frame, - char imtype, - bool use_ext, - bool use_frames, - const char *suffix); -int BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); -int BKE_image_path_ensure_ext_from_imtype(char *string, char imtype); -char BKE_image_ftype_to_imtype(int ftype, const struct ImbFormatOptions *options); -int BKE_image_imtype_to_ftype(char imtype, struct ImbFormatOptions *r_options); - -bool BKE_imtype_is_movie(char imtype); -bool BKE_imtype_supports_zbuf(char imtype); -bool BKE_imtype_supports_compress(char imtype); -bool BKE_imtype_supports_quality(char imtype); -bool BKE_imtype_requires_linear_float(char imtype); -char BKE_imtype_valid_channels(char imtype, bool write_file); -char BKE_imtype_valid_depths(char imtype); - -/** - * String is from command line `--render-format` argument, - * keep in sync with `creator_args.c` help info. - */ -char BKE_imtype_from_arg(const char *arg); - -void BKE_imformat_defaults(struct ImageFormatData *im_format); -void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const struct ImBuf *imbuf); /** * Used by sequencer too. @@ -171,10 +133,6 @@ struct RenderResult; #define IMA_SIGNAL_USER_NEW_IMAGE 6 #define IMA_SIGNAL_COLORMANAGE 7 -#define IMA_CHAN_FLAG_BW 1 -#define IMA_CHAN_FLAG_RGB 2 -#define IMA_CHAN_FLAG_ALPHA 4 - /** * Checks whether there's an image buffer for given image and user. */ diff --git a/source/blender/blenkernel/BKE_image_format.h b/source/blender/blenkernel/BKE_image_format.h new file mode 100644 index 00000000000..633af54ea4f --- /dev/null +++ b/source/blender/blenkernel/BKE_image_format.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct BlendDataReader; +struct BlendWriter; +struct ImbFormatOptions; +struct ImageFormatData; +struct ImBuf; +struct Scene; + +/* Init/Copy/Free */ + +void BKE_image_format_init(struct ImageFormatData *imf, const bool render); +void BKE_image_format_copy(struct ImageFormatData *imf_dst, const struct ImageFormatData *imf_src); +void BKE_image_format_free(struct ImageFormatData *imf); + +void BKE_image_format_blend_read_data(struct BlendDataReader *reader, struct ImageFormatData *imf); +void BKE_image_format_blend_write(struct BlendWriter *writer, struct ImageFormatData *imf); + +/* File Paths */ + +void BKE_image_path_from_imformat(char *string, + const char *base, + const char *relbase, + int frame, + const struct ImageFormatData *im_format, + bool use_ext, + bool use_frames, + const char *suffix); +void BKE_image_path_from_imtype(char *string, + const char *base, + const char *relbase, + int frame, + char imtype, + bool use_ext, + bool use_frames, + const char *suffix); +int BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); +int BKE_image_path_ensure_ext_from_imtype(char *string, char imtype); + +/* File Types */ + +#define IMA_CHAN_FLAG_BW 1 +#define IMA_CHAN_FLAG_RGB 2 +#define IMA_CHAN_FLAG_ALPHA 4 + +char BKE_ftype_to_imtype(int ftype, const struct ImbFormatOptions *options); +int BKE_imtype_to_ftype(char imtype, struct ImbFormatOptions *r_options); + +bool BKE_imtype_is_movie(char imtype); +bool BKE_imtype_supports_zbuf(char imtype); +bool BKE_imtype_supports_compress(char imtype); +bool BKE_imtype_supports_quality(char imtype); +bool BKE_imtype_requires_linear_float(char imtype); +char BKE_imtype_valid_channels(char imtype, bool write_file); +char BKE_imtype_valid_depths(char imtype); + +/** + * String is from command line `--render-format` argument, + * keep in sync with `creator_args.c` help info. + */ +char BKE_imtype_from_arg(const char *arg); + +/* Conversion between ImBuf settings. */ + +void BKE_image_format_from_imbuf(struct ImageFormatData *im_format, const struct ImBuf *imbuf); +void BKE_image_format_to_imbuf(struct ImBuf *ibuf, const struct ImageFormatData *imf); + +/* Color Management */ + +void BKE_image_format_color_management_copy(struct ImageFormatData *imf, + const struct ImageFormatData *imf_src); +void BKE_image_format_color_management_copy_from_scene(struct ImageFormatData *imf, + const struct Scene *scene); + +/* Image Output + * + * Initialize an image format that can be used for file writing, including + * color management settings from the scene. */ + +void BKE_image_format_init_for_write(struct ImageFormatData *imf, + const struct Scene *scene_src, + const struct ImageFormatData *imf_src); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_image_partial_update.hh b/source/blender/blenkernel/BKE_image_partial_update.hh index 8cbb8819551..393bf003caa 100644 --- a/source/blender/blenkernel/BKE_image_partial_update.hh +++ b/source/blender/blenkernel/BKE_image_partial_update.hh @@ -165,6 +165,7 @@ class ImageTileData : AbstractTileData { * Can be nullptr when the file doesn't exist or when the tile hasn't been initialized. */ ImBuf *tile_buffer = nullptr; + void *tile_buffer_lock = nullptr; ImageTileData(Image *image, ImageUser *image_user) : image(image) { @@ -177,14 +178,15 @@ class ImageTileData : AbstractTileData { { image_user.tile = new_tile_number; tile = BKE_image_get_tile(image, new_tile_number); - tile_buffer = BKE_image_acquire_ibuf(image, &image_user, NULL); + tile_buffer = BKE_image_acquire_ibuf(image, &image_user, &tile_buffer_lock); } void free_data() override { - BKE_image_release_ibuf(image, tile_buffer, nullptr); + BKE_image_release_ibuf(image, tile_buffer, tile_buffer_lock); tile = nullptr; tile_buffer = nullptr; + tile_buffer_lock = nullptr; } }; diff --git a/source/blender/blenkernel/BKE_image_save.h b/source/blender/blenkernel/BKE_image_save.h index ea9ed7d2d26..052fc937af9 100644 --- a/source/blender/blenkernel/BKE_image_save.h +++ b/source/blender/blenkernel/BKE_image_save.h @@ -16,6 +16,9 @@ struct Image; struct Main; struct ReportList; struct Scene; +struct RenderResult; + +/* Image datablock saving. */ typedef struct ImageSaveOptions { /* Context within which image is saved. */ @@ -36,12 +39,37 @@ typedef struct ImageSaveOptions { void BKE_image_save_options_init(struct ImageSaveOptions *opts, struct Main *bmain, struct Scene *scene); +void BKE_image_save_options_free(struct ImageSaveOptions *opts); + bool BKE_image_save(struct ReportList *reports, struct Main *bmain, struct Image *ima, struct ImageUser *iuser, struct ImageSaveOptions *opts); +/* Render saving. */ + +/** + * Save single or multi-layer OpenEXR files from the render result. + * Optionally saves only a specific view or layer. + */ +bool BKE_image_render_write_exr(struct ReportList *reports, + const struct RenderResult *rr, + const char *filepath, + const struct ImageFormatData *imf, + const bool save_as_render, + const char *view, + int layer); + +/** + * \param filepath_basis: May be used as-is, or used as a basis for multi-view images. + */ +bool BKE_image_render_write(struct ReportList *reports, + struct RenderResult *rr, + const struct Scene *scene, + const bool stamp, + const char *filepath_basis); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index da436b7d33f..bf9e7651e36 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -13,6 +13,7 @@ struct Lattice; struct ListBase; struct Main; struct Mesh; +struct MVert; struct Object; /* Kernel prototypes */ @@ -119,7 +120,8 @@ void BKE_keyblock_convert_to_curve(struct KeyBlock *kb, struct Curve *cu, struct void BKE_keyblock_update_from_mesh(struct Mesh *me, struct KeyBlock *kb); void BKE_keyblock_convert_from_mesh(struct Mesh *me, struct Key *key, struct KeyBlock *kb); -void BKE_keyblock_convert_to_mesh(struct KeyBlock *kb, struct Mesh *me); +void BKE_keyblock_convert_to_mesh(struct KeyBlock *kb, struct MVert *mvert, int totvert); + /** * Computes normals (vertices, polygons and/or loops ones) of given mesh for given shape key. * diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index ba22ab3f1de..77a3223c064 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -301,10 +301,9 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter); #define FOREACH_SELECTED_OBJECT_BEGIN(_view_layer, _v3d, _instance) \ { \ - struct ObjectsVisibleIteratorData data_ = { \ - .view_layer = _view_layer, \ - .v3d = _v3d, \ - }; \ + struct ObjectsVisibleIteratorData data_ = {NULL}; \ + data_.view_layer = _view_layer; \ + data_.v3d = _v3d; \ ITER_BEGIN (BKE_view_layer_selected_objects_iterator_begin, \ BKE_view_layer_selected_objects_iterator_next, \ BKE_view_layer_selected_objects_iterator_end, \ @@ -319,10 +318,9 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter); #define FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN(_view_layer, _v3d, _instance) \ { \ - struct ObjectsVisibleIteratorData data_ = { \ - .view_layer = _view_layer, \ - .v3d = _v3d, \ - }; \ + struct ObjectsVisibleIteratorData data_ = {NULL}; \ + data_.view_layer = _view_layer; \ + data_.v3d = _v3d; \ ITER_BEGIN (BKE_view_layer_selected_editable_objects_iterator_begin, \ BKE_view_layer_selected_editable_objects_iterator_next, \ BKE_view_layer_selected_editable_objects_iterator_end, \ @@ -337,10 +335,9 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter); #define FOREACH_VISIBLE_OBJECT_BEGIN(_view_layer, _v3d, _instance) \ { \ - struct ObjectsVisibleIteratorData data_ = { \ - .view_layer = _view_layer, \ - .v3d = _v3d, \ - }; \ + struct ObjectsVisibleIteratorData data_ = {NULL}; \ + data_.view_layer = _view_layer; \ + data_.v3d = _v3d; \ ITER_BEGIN (BKE_view_layer_visible_objects_iterator_begin, \ BKE_view_layer_visible_objects_iterator_next, \ BKE_view_layer_visible_objects_iterator_end, \ @@ -407,10 +404,9 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter); #define FOREACH_VISIBLE_BASE_BEGIN(_view_layer, _v3d, _instance) \ { \ - struct ObjectsVisibleIteratorData data_ = { \ - .view_layer = _view_layer, \ - .v3d = _v3d, \ - }; \ + struct ObjectsVisibleIteratorData data_ = {NULL}; \ + data_.view_layer = _view_layer; \ + data_.v3d = _v3d; \ ITER_BEGIN (BKE_view_layer_visible_bases_iterator_begin, \ BKE_view_layer_visible_bases_iterator_next, \ BKE_view_layer_visible_bases_iterator_end, \ @@ -440,10 +436,9 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter); IteratorBeginCb func_begin; \ IteratorCb func_next, func_end; \ void *data_in; \ - struct ObjectsVisibleIteratorData data_ = { \ - .view_layer = _view_layer, \ - .v3d = _v3d, \ - }; \ + struct ObjectsVisibleIteratorData data_ = {NULL}; \ + data_.view_layer = _view_layer; \ + data_.v3d = _v3d; \ \ if (flag == SELECT) { \ func_begin = &BKE_view_layer_selected_objects_iterator_begin; \ diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 926be9c7dbe..48cffcf8d2c 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -115,9 +115,19 @@ typedef int (*LibraryIDLinkCallback)(LibraryIDLinkCallbackData *cb_data); /* Flags for the foreach function itself. */ enum { IDWALK_NOP = 0, + /** The callback will never modify the ID pointers it processes. */ IDWALK_READONLY = (1 << 0), - IDWALK_RECURSE = (1 << 1), /* Also implies IDWALK_READONLY. */ - IDWALK_INCLUDE_UI = (1 << 2), /* Include UI pointers (from WM and screens editors). */ + /** Recurse into 'descendant' IDs. + * Each ID is only processed once. Order of ID processing is not guaranteed. + * + * Also implies IDWALK_READONLY, and excludes IDWALK_DO_INTERNAL_RUNTIME_POINTERS. + * + * NOTE: When enabled, embedded IDs are processed separately from their owner, as if they were + * regular IDs. Owner ID is not available then in the #LibraryForeachIDData callback data. + */ + IDWALK_RECURSE = (1 << 1), + /** Include UI pointers (from WM and screens editors). */ + IDWALK_INCLUDE_UI = (1 << 2), /** Do not process ID pointers inside embedded IDs. Needed by depsgraph processing e.g. */ IDWALK_IGNORE_EMBEDDED_ID = (1 << 3), diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index fd7d39fc250..b66d53b2321 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -26,6 +26,7 @@ extern "C" { struct ID; struct IDRemapper; +struct LinkNode; /* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */ @@ -133,6 +134,15 @@ void BKE_libblock_relink_ex(struct Main *bmain, void *old_idv, void *new_idv, short remap_flags) ATTR_NONNULL(1, 2); +/** + * Same as #BKE_libblock_relink_ex, but applies all rules defined in \a id_remapper to \a ids (or + * does cleanup if `ID_REMAP_TYPE_CLEANUP` is specified as \a remap_type). + */ +void BKE_libblock_relink_multiple(struct Main *bmain, + struct LinkNode *ids, + const eIDRemapType remap_type, + struct IDRemapper *id_remapper, + const short remap_flags); /** * Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 2373bb289cd..0ba9713b96d 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -472,6 +472,21 @@ void BKE_mesh_calc_normals_poly(const struct MVert *mvert, float (*r_poly_normals)[3]); /** + * Calculate face and vertex normals directly into result arrays. + * + * \note Usually #BKE_mesh_vertex_normals_ensure is the preferred way to access vertex normals, + * since they may already be calculated and cached on the mesh. + */ +void BKE_mesh_calc_normals_poly_and_vertex(const struct MVert *mvert, + int mvert_len, + const struct MLoop *mloop, + int mloop_len, + const struct MPoly *mpoly, + int mpoly_len, + float (*r_poly_normals)[3], + float (*r_vert_normals)[3]); + +/** * Calculate vertex and face normals, storing the result in custom data layers on the mesh. * * \note It is usually preferable to calculate normals lazily with diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index 9fe5bd3c531..a942f3bb7ed 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -6,8 +6,7 @@ * \ingroup bke */ -#include "FN_generic_virtual_array.hh" - +#include "BLI_generic_virtual_array.hh" #include "BLI_math_vec_types.hh" #include "BKE_attribute.h" @@ -21,11 +20,6 @@ class OutputAttribute; namespace blender::bke::mesh_surface_sample { -using fn::CPPType; -using fn::GMutableSpan; -using fn::GSpan; -using fn::GVArray; - void sample_point_attribute(const Mesh &mesh, Span<int> looptri_indices, Span<float3> bary_coords, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 904f06f2734..fa199300780 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -99,6 +99,7 @@ typedef struct bNodeSocketTemplate { * However, achieving this requires quite a few changes currently. */ #ifdef __cplusplus namespace blender { +class CPPType; namespace nodes { class NodeMultiFunctionBuilder; class GeoNodeExecParams; @@ -106,12 +107,11 @@ class NodeDeclarationBuilder; class GatherLinkSearchOpParams; } // namespace nodes namespace fn { -class CPPType; class MFDataType; } // namespace fn } // namespace blender -using CPPTypeHandle = blender::fn::CPPType; +using CPPTypeHandle = blender::CPPType; using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder); using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); using NodeDeclareFunction = void (*)(blender::nodes::NodeDeclarationBuilder &builder); @@ -1363,37 +1363,13 @@ struct TexResult; * \{ */ #define GEO_NODE_TRIANGULATE 1000 -#define GEO_NODE_LEGACY_EDGE_SPLIT 1001 #define GEO_NODE_TRANSFORM 1002 #define GEO_NODE_MESH_BOOLEAN 1003 -#define GEO_NODE_LEGACY_POINT_DISTRIBUTE 1004 -#define GEO_NODE_LEGACY_POINT_INSTANCE 1005 -#define GEO_NODE_LEGACY_SUBDIVISION_SURFACE 1006 #define GEO_NODE_OBJECT_INFO 1007 -#define GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE 1008 -#define GEO_NODE_LEGACY_ATTRIBUTE_MATH 1009 #define GEO_NODE_JOIN_GEOMETRY 1010 -#define GEO_NODE_LEGACY_ATTRIBUTE_FILL 1011 -#define GEO_NODE_LEGACY_ATTRIBUTE_MIX 1012 -#define GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP 1013 -#define GEO_NODE_LEGACY_POINT_SEPARATE 1014 -#define GEO_NODE_LEGACY_ATTRIBUTE_COMPARE 1015 -#define GEO_NODE_LEGACY_POINT_ROTATE 1016 -#define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH 1017 -#define GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR 1018 -#define GEO_NODE_LEGACY_POINT_TRANSLATE 1019 -#define GEO_NODE_LEGACY_POINT_SCALE 1020 -#define GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE 1021 -#define GEO_NODE_LEGACY_POINTS_TO_VOLUME 1022 #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 -#define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025 -#define GEO_NODE_LEGACY_VOLUME_TO_MESH 1026 -#define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 -#define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_SUBDIVIDE_MESH 1029 -#define GEO_NODE_LEGACY_ATTRIBUTE_REMOVE 1030 -#define GEO_NODE_LEGACY_ATTRIBUTE_CONVERT 1031 #define GEO_NODE_MESH_PRIMITIVE_CUBE 1032 #define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1033 #define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1034 @@ -1402,28 +1378,15 @@ struct TexResult; #define GEO_NODE_MESH_PRIMITIVE_CONE 1037 #define GEO_NODE_MESH_PRIMITIVE_LINE 1038 #define GEO_NODE_MESH_PRIMITIVE_GRID 1039 -#define GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE 1040 -#define GEO_NODE_LEGACY_ATTRIBUTE_CLAMP 1041 #define GEO_NODE_BOUNDING_BOX 1042 #define GEO_NODE_SWITCH 1043 -#define GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER 1044 #define GEO_NODE_CURVE_TO_MESH 1045 -#define GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP 1046 #define GEO_NODE_RESAMPLE_CURVE 1047 -#define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE 1048 -#define GEO_NODE_LEGACY_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_REPLACE_MATERIAL 1051 -#define GEO_NODE_LEGACY_MESH_TO_CURVE 1052 -#define GEO_NODE_LEGACY_DELETE_GEOMETRY 1053 #define GEO_NODE_CURVE_LENGTH 1054 -#define GEO_NODE_LEGACY_SELECT_BY_MATERIAL 1055 #define GEO_NODE_CONVEX_HULL 1056 -#define GEO_NODE_LEGACY_CURVE_TO_POINTS 1057 -#define GEO_NODE_LEGACY_CURVE_REVERSE 1058 #define GEO_NODE_SEPARATE_COMPONENTS 1059 -#define GEO_NODE_LEGACY_CURVE_SUBDIVIDE 1060 -#define GEO_NODE_LEGACY_RAYCAST 1061 #define GEO_NODE_CURVE_PRIMITIVE_STAR 1062 #define GEO_NODE_CURVE_PRIMITIVE_SPIRAL 1063 #define GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER 1064 @@ -1431,12 +1394,8 @@ struct TexResult; #define GEO_NODE_CURVE_PRIMITIVE_CIRCLE 1066 #define GEO_NODE_VIEWER 1067 #define GEO_NODE_CURVE_PRIMITIVE_LINE 1068 -#define GEO_NODE_LEGACY_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 #define GEO_NODE_TRIM_CURVE 1071 -#define GEO_NODE_LEGACY_CURVE_SET_HANDLES 1072 -#define GEO_NODE_LEGACY_CURVE_SPLINE_TYPE 1073 -#define GEO_NODE_LEGACY_CURVE_SELECT_HANDLES 1074 #define GEO_NODE_FILL_CURVE 1075 #define GEO_NODE_INPUT_POSITION 1076 #define GEO_NODE_SET_POSITION 1077 diff --git a/source/blender/blenkernel/BKE_packedFile.h b/source/blender/blenkernel/BKE_packedFile.h index 8433894b8c5..893aa2a5b1c 100644 --- a/source/blender/blenkernel/BKE_packedFile.h +++ b/source/blender/blenkernel/BKE_packedFile.h @@ -45,7 +45,7 @@ enum ePF_FileStatus { struct PackedFile *BKE_packedfile_duplicate(const struct PackedFile *pf_src); struct PackedFile *BKE_packedfile_new(struct ReportList *reports, - const char *filename, + const char *filepath, const char *basepath); struct PackedFile *BKE_packedfile_new_from_memory(void *mem, int memlen); @@ -102,7 +102,7 @@ int BKE_packedfile_unpack_all_libraries(struct Main *bmain, struct ReportList *r int BKE_packedfile_write_to_file(struct ReportList *reports, const char *ref_file_name, - const char *filename, + const char *filepath, struct PackedFile *pf, bool guimode); @@ -122,7 +122,7 @@ int BKE_packedfile_count_all(struct Main *bmain); * - #PF_NOFILE: the original file doesn't exist. */ enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name, - const char *filename, + const char *filepath_rel, struct PackedFile *pf); /* Read. */ diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index d06b1d43b96..a6402a1e8a1 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -225,7 +225,7 @@ bool BKE_scene_remove_render_view(struct Scene *scene, struct SceneRenderView *s /* Render profile. */ int get_render_subsurf_level(const struct RenderData *r, int lvl, bool for_render); -int get_render_child_particle_number(const struct RenderData *r, int num, bool for_render); +int get_render_child_particle_number(const struct RenderData *r, int child_num, bool for_render); bool BKE_scene_use_shading_nodes_custom(struct Scene *scene); bool BKE_scene_use_spherical_stereo(struct Scene *scene); @@ -282,7 +282,7 @@ struct SceneRenderView *BKE_scene_multiview_render_view_findindex(const struct R int view_id); const char *BKE_scene_multiview_render_view_name_get(const struct RenderData *rd, int view_id); int BKE_scene_multiview_view_id_get(const struct RenderData *rd, const char *viewname); -void BKE_scene_multiview_filepath_get(struct SceneRenderView *srv, +void BKE_scene_multiview_filepath_get(const struct SceneRenderView *srv, const char *filepath, char *r_filepath); /** diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 87f4ad6dc61..32330244c08 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -8,11 +8,10 @@ #include <mutex> -#include "FN_generic_virtual_array.hh" - #include "DNA_curves_types.h" #include "BLI_float4x4.hh" +#include "BLI_generic_virtual_array.hh" #include "BLI_math_vec_types.hh" #include "BLI_vector.hh" @@ -205,16 +204,16 @@ class Spline { * points) to arbitrary parameters in between the evaluated points. The interpolation is quite * simple, but this handles the cyclic and end point special cases. */ - void sample_with_index_factors(const blender::fn::GVArray &src, + void sample_with_index_factors(const blender::GVArray &src, blender::Span<float> index_factors, - blender::fn::GMutableSpan dst) const; + blender::GMutableSpan dst) const; template<typename T> void sample_with_index_factors(const blender::VArray<T> &src, blender::Span<float> index_factors, blender::MutableSpan<T> dst) const { this->sample_with_index_factors( - blender::fn::GVArray(src), index_factors, blender::fn::GMutableSpan(dst)); + blender::GVArray(src), index_factors, blender::GMutableSpan(dst)); } template<typename T> void sample_with_index_factors(blender::Span<T> src, @@ -229,11 +228,11 @@ class Spline { * evaluated points. For poly splines, the lifetime of the returned virtual array must not * exceed the lifetime of the input data. */ - virtual blender::fn::GVArray interpolate_to_evaluated(const blender::fn::GVArray &src) const = 0; - blender::fn::GVArray interpolate_to_evaluated(blender::fn::GSpan data) const; + virtual blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const = 0; + blender::GVArray interpolate_to_evaluated(blender::GSpan data) const; template<typename T> blender::VArray<T> interpolate_to_evaluated(blender::Span<T> data) const { - return this->interpolate_to_evaluated(blender::fn::GSpan(data)).typed<T>(); + return this->interpolate_to_evaluated(blender::GSpan(data)).typed<T>(); } protected: @@ -386,8 +385,7 @@ class BezierSpline final : public Spline { */ InterpolationData interpolation_data_from_index_factor(float index_factor) const; - virtual blender::fn::GVArray interpolate_to_evaluated( - const blender::fn::GVArray &src) const override; + virtual blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const override; void evaluate_segment(int index, int next_index, @@ -541,7 +539,7 @@ class NURBSpline final : public Spline { blender::Span<blender::float3> evaluated_positions() const final; - blender::fn::GVArray interpolate_to_evaluated(const blender::fn::GVArray &src) const final; + blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final; protected: void correct_end_tangents() const final; @@ -599,7 +597,7 @@ class PolySpline final : public Spline { * the original data. Therefore the lifetime of the returned virtual array must not be longer * than the source data. */ - blender::fn::GVArray interpolate_to_evaluated(const blender::fn::GVArray &src) const final; + blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final; protected: void correct_end_tangents() const final; diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h index 40e8ee2f999..e9de7d1532e 100644 --- a/source/blender/blenkernel/BKE_subdiv_modifier.h +++ b/source/blender/blenkernel/BKE_subdiv_modifier.h @@ -13,6 +13,10 @@ extern "C" { #endif +/* Hardcoded for until GPU shaders are automatically generated, then we will have a more + * programmatic way of detecting this. */ +#define MAX_GPU_SUBDIV_SSBOS 12 + struct Mesh; struct Object; struct Scene; diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h index 8cac196accc..29eb180a2ab 100644 --- a/source/blender/blenkernel/BKE_tracking.h +++ b/source/blender/blenkernel/BKE_tracking.h @@ -510,6 +510,7 @@ void BKE_tracking_refine_marker(struct MovieClip *clip, struct AutoTrackContext *BKE_autotrack_context_new(struct MovieClip *clip, struct MovieClipUser *user, bool is_backwards); +void BKE_autotrack_context_start(struct AutoTrackContext *context); bool BKE_autotrack_context_step(struct AutoTrackContext *context); void BKE_autotrack_context_sync(struct AutoTrackContext *context); void BKE_autotrack_context_sync_user(struct AutoTrackContext *context, struct MovieClipUser *user); diff --git a/source/blender/blenkernel/BKE_type_conversions.hh b/source/blender/blenkernel/BKE_type_conversions.hh index e66982aa04c..5152989d137 100644 --- a/source/blender/blenkernel/BKE_type_conversions.hh +++ b/source/blender/blenkernel/BKE_type_conversions.hh @@ -6,8 +6,6 @@ namespace blender::bke { -using fn::CPPType; - struct ConversionFunctions { const fn::MultiFunction *multi_function; void (*convert_single_to_initialized)(const void *src, void *dst); @@ -58,11 +56,11 @@ class DataTypeConversions { const void *from_value, void *to_value) const; - void convert_to_initialized_n(fn::GSpan from_span, fn::GMutableSpan to_span) const; + void convert_to_initialized_n(GSpan from_span, GMutableSpan to_span) const; - fn::GVArray try_convert(fn::GVArray varray, const CPPType &to_type) const; + GVArray try_convert(GVArray varray, const CPPType &to_type) const; - fn::GVMutableArray try_convert(fn::GVMutableArray varray, const CPPType &to_type) const; + GVMutableArray try_convert(GVMutableArray varray, const CPPType &to_type) const; }; const DataTypeConversions &get_implicit_type_conversions(); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 2e32652647c..8f7ef035557 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -104,15 +104,18 @@ set(SRC intern/crazyspace.c intern/cryptomatte.cc intern/curve.cc - intern/curves.cc - intern/curves_geometry.cc intern/curve_bevel.c + intern/curve_bezier.cc + intern/curve_catmull_rom.cc intern/curve_convert.c intern/curve_decimate.c intern/curve_deform.c intern/curve_eval.cc + intern/curve_nurbs.cc intern/curve_to_mesh_convert.cc intern/curveprofile.cc + intern/curves.cc + intern/curves_geometry.cc intern/customdata.cc intern/customdata_file.c intern/data_transfer.c @@ -153,10 +156,11 @@ set(SRC intern/idprop_utils.c intern/idtype.c intern/image.cc - intern/image_partial_update.cc + intern/image_format.cc intern/image_gen.c intern/image_gpu.cc - intern/image_save.c + intern/image_partial_update.cc + intern/image_save.cc intern/ipo.c intern/kelvinlet.c intern/key.c @@ -344,10 +348,10 @@ set(SRC BKE_cryptomatte.h BKE_cryptomatte.hh BKE_curve.h - BKE_curves.h - BKE_curves.hh BKE_curve_to_mesh.hh BKE_curveprofile.h + BKE_curves.h + BKE_curves.hh BKE_customdata.h BKE_customdata_file.h BKE_data_transfer.h @@ -380,6 +384,7 @@ set(SRC BKE_idprop.hh BKE_idtype.h BKE_image.h + BKE_image_format.h BKE_image_partial_update.hh BKE_image_save.h BKE_ipo.h @@ -602,6 +607,10 @@ if(WITH_IMAGE_HDR) add_definitions(-DWITH_HDR) endif() +if(WITH_IMAGE_WEBP) + add_definitions(-DWITH_WEBP) +endif() + if(WITH_CODEC_AVI) list(APPEND INC ../io/avi @@ -815,6 +824,7 @@ if(WITH_GTESTS) intern/fcurve_test.cc intern/idprop_serialize_test.cc intern/image_partial_update_test.cc + intern/image_test.cc intern/lattice_deform_test.cc intern/layer_test.cc intern/lib_id_remapper_test.cc diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index b0393ed723d..8d3649fef08 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -1022,16 +1022,16 @@ void BKE_appdir_app_templates(ListBase *templates) continue; } - struct direntry *dir; - uint totfile = BLI_filelist_dir_contents(subdir, &dir); - for (int f = 0; f < totfile; f++) { - if (!FILENAME_IS_CURRPAR(dir[f].relname) && S_ISDIR(dir[f].type)) { - char *template = BLI_strdup(dir[f].relname); + struct direntry *dirs; + const uint dir_num = BLI_filelist_dir_contents(subdir, &dirs); + for (int f = 0; f < dir_num; f++) { + if (!FILENAME_IS_CURRPAR(dirs[f].relname) && S_ISDIR(dirs[f].type)) { + char *template = BLI_strdup(dirs[f].relname); BLI_addtail(templates, BLI_genericNodeN(template)); } } - BLI_filelist_free(dir, totfile); + BLI_filelist_free(dirs, dir_num); } } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 5c2d77f20a8..0a6ef8f0f01 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2164,8 +2164,8 @@ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_m const float y = nor[1]; const float z = nor[2]; - float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */ - const float theta_alt = x * x + z * z; /* squared distance from origin in x,z plane. */ + float theta = 1.0f + y; /* Remapping Y from [-1,+1] to [0,2]. */ + const float theta_alt = x * x + z * z; /* Squared distance from origin in x,z plane. */ float rMatrix[3][3], bMatrix[3][3]; BLI_ASSERT_UNIT_V3(nor); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 2f07ee55d79..8fbab8dde25 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -28,14 +28,14 @@ static CLG_LogRef LOG = {"bke.attribute_access"}; using blender::float3; +using blender::GMutableSpan; +using blender::GSpan; +using blender::GVArrayImpl_For_GSpan; using blender::Set; using blender::StringRef; using blender::StringRefNull; using blender::bke::AttributeIDRef; using blender::bke::OutputAttribute; -using blender::fn::GMutableSpan; -using blender::fn::GSpan; -using blender::fn::GVArrayImpl_For_GSpan; namespace blender::bke { @@ -54,55 +54,6 @@ std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_i return stream; } -const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) -{ - switch (type) { - case CD_PROP_FLOAT: - return &CPPType::get<float>(); - case CD_PROP_FLOAT2: - return &CPPType::get<float2>(); - case CD_PROP_FLOAT3: - return &CPPType::get<float3>(); - case CD_PROP_INT32: - return &CPPType::get<int>(); - case CD_PROP_COLOR: - return &CPPType::get<ColorGeometry4f>(); - case CD_PROP_BOOL: - return &CPPType::get<bool>(); - case CD_PROP_INT8: - return &CPPType::get<int8_t>(); - default: - return nullptr; - } - return nullptr; -} - -CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) -{ - if (type.is<float>()) { - return CD_PROP_FLOAT; - } - if (type.is<float2>()) { - return CD_PROP_FLOAT2; - } - if (type.is<float3>()) { - return CD_PROP_FLOAT3; - } - if (type.is<int>()) { - return CD_PROP_INT32; - } - if (type.is<ColorGeometry4f>()) { - return CD_PROP_COLOR; - } - if (type.is<bool>()) { - return CD_PROP_BOOL; - } - if (type.is<int8_t>()) { - return CD_PROP_INT8; - } - return static_cast<CustomDataType>(-1); -} - static int attribute_data_type_complexity(const CustomDataType data_type) { switch (data_type) { @@ -191,14 +142,14 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains) return highest_priority_domain; } -fn::GMutableSpan OutputAttribute::as_span() +GMutableSpan OutputAttribute::as_span() { if (!optional_span_varray_) { const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(varray_, - materialize_old_values); + optional_span_varray_ = std::make_unique<GVMutableArray_GSpan>(varray_, + materialize_old_values); } - fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; + GVMutableArray_GSpan &span_varray = *optional_span_varray_; return span_varray; } @@ -917,8 +868,8 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( return {}; } -blender::fn::GVArray GeometryComponent::attribute_try_adapt_domain_impl( - const blender::fn::GVArray &varray, +blender::GVArray GeometryComponent::attribute_try_adapt_domain_impl( + const blender::GVArray &varray, const AttributeDomain from_domain, const AttributeDomain to_domain) const { @@ -1101,15 +1052,15 @@ std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( return result; } -static blender::fn::GVArray try_adapt_data_type(blender::fn::GVArray varray, - const blender::fn::CPPType &to_type) +static blender::GVArray try_adapt_data_type(blender::GVArray varray, + const blender::CPPType &to_type) { const blender::bke::DataTypeConversions &conversions = blender::bke::get_implicit_type_conversions(); return conversions.try_convert(std::move(varray), to_type); } -blender::fn::GVArray GeometryComponent::attribute_try_get_for_read( +blender::GVArray GeometryComponent::attribute_try_get_for_read( const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) const @@ -1119,7 +1070,7 @@ blender::fn::GVArray GeometryComponent::attribute_try_get_for_read( return {}; } - blender::fn::GVArray varray = std::move(attribute.varray); + blender::GVArray varray = std::move(attribute.varray); if (!ELEM(domain, ATTR_DOMAIN_AUTO, attribute.domain)) { varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); if (!varray) { @@ -1127,7 +1078,7 @@ blender::fn::GVArray GeometryComponent::attribute_try_get_for_read( } } - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); + const blender::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); BLI_assert(cpp_type != nullptr); if (varray.type() != *cpp_type) { varray = try_adapt_data_type(std::move(varray), *cpp_type); @@ -1139,8 +1090,8 @@ blender::fn::GVArray GeometryComponent::attribute_try_get_for_read( return varray; } -blender::fn::GVArray GeometryComponent::attribute_try_get_for_read( - const AttributeIDRef &attribute_id, const AttributeDomain domain) const +blender::GVArray GeometryComponent::attribute_try_get_for_read(const AttributeIDRef &attribute_id, + const AttributeDomain domain) const { if (!this->attribute_domain_supported(domain)) { return {}; @@ -1165,7 +1116,7 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( if (!attribute) { return {}; } - const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); + const blender::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); BLI_assert(type != nullptr); if (attribute.varray.type() == *type) { return attribute; @@ -1175,24 +1126,24 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain}; } -blender::fn::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef &attribute_id, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) const +blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) const { - blender::fn::GVArray varray = this->attribute_try_get_for_read(attribute_id, domain, data_type); + blender::GVArray varray = this->attribute_try_get_for_read(attribute_id, domain, data_type); if (varray) { return varray; } - const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); + const blender::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); if (default_value == nullptr) { default_value = type->default_value(); } const int domain_size = this->attribute_domain_size(domain); - return blender::fn::GVArray::ForSingle(*type, domain_size, default_value); + return blender::GVArray::ForSingle(*type, domain_size, default_value); } -class GVMutableAttribute_For_OutputAttribute : public blender::fn::GVArrayImpl_For_GSpan { +class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan { public: GeometryComponent *component; std::string attribute_name; @@ -1201,7 +1152,7 @@ class GVMutableAttribute_For_OutputAttribute : public blender::fn::GVArrayImpl_F GVMutableAttribute_For_OutputAttribute(GMutableSpan data, GeometryComponent &component, const AttributeIDRef &attribute_id) - : blender::fn::GVArrayImpl_For_GSpan(data), component(&component) + : blender::GVArrayImpl_For_GSpan(data), component(&component) { if (attribute_id.is_named()) { this->attribute_name = attribute_id.name(); diff --git a/source/blender/blenkernel/intern/bpath_test.cc b/source/blender/blenkernel/intern/bpath_test.cc index ec1737276e1..a614f9b3954 100644 --- a/source/blender/blenkernel/intern/bpath_test.cc +++ b/source/blender/blenkernel/intern/bpath_test.cc @@ -77,7 +77,7 @@ class BPathTest : public testing::Test { TEST_F(BPathTest, rebase_on_relative) { - // Test on relative paths, should be modified. + /* Test on relative paths, should be modified. */ Text *text = reinterpret_cast<Text *>(bmain->texts.first); text->filepath = BLI_strdup(TEXT_PATH_RELATIVE); @@ -92,7 +92,7 @@ TEST_F(BPathTest, rebase_on_relative) TEST_F(BPathTest, rebase_on_absolute) { - // Test on absolute paths, should not be modified. + /* Test on absolute paths, should not be modified. */ Text *text = reinterpret_cast<Text *>(bmain->texts.first); text->filepath = BLI_strdup(TEXT_PATH_ABSOLUTE); @@ -115,9 +115,9 @@ TEST_F(BPathTest, convert_to_relative) BKE_bpath_relative_convert(bmain, BASE_DIR, nullptr); - // Already relative path should not be modified. + /* Already relative path should not be modified. */ EXPECT_STREQ(text->filepath, TEXT_PATH_RELATIVE); - // Absolute path should be modified. + /* Absolute path should be modified. */ EXPECT_STREQ(movie_clip->filepath, MOVIECLIP_PATH_ABSOLUTE_MADE_RELATIVE); } @@ -131,9 +131,9 @@ TEST_F(BPathTest, convert_to_absolute) BKE_bpath_absolute_convert(bmain, BASE_DIR, nullptr); - // Relative path should be modified. + /* Relative path should be modified. */ EXPECT_STREQ(text->filepath, TEXT_PATH_RELATIVE_MADE_ABSOLUTE); - // Already absolute path should not be modified. + /* Already absolute path should not be modified. */ EXPECT_STREQ(movie_clip->filepath, MOVIECLIP_PATH_ABSOLUTE); } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 6ee6ff7f41d..ff07d061a20 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -94,6 +94,9 @@ static void brush_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c brush_dst->gpencil_settings->curve_rand_value = BKE_curvemapping_copy( brush_src->gpencil_settings->curve_rand_value); } + if (brush_src->curves_sculpt_settings != NULL) { + brush_dst->curves_sculpt_settings = MEM_dupallocN(brush_src->curves_sculpt_settings); + } /* enable fake user by default */ id_fake_user_set(&brush_dst->id); @@ -121,6 +124,9 @@ static void brush_free_data(ID *id) MEM_SAFE_FREE(brush->gpencil_settings); } + if (brush->curves_sculpt_settings != NULL) { + MEM_freeN(brush->curves_sculpt_settings); + } MEM_SAFE_FREE(brush->gradient); @@ -236,6 +242,9 @@ static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_addres BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value); } } + if (brush->curves_sculpt_settings) { + BLO_write_struct(writer, BrushCurvesSculptSettings, brush->curves_sculpt_settings); + } if (brush->gradient) { BLO_write_struct(writer, ColorBand, brush->gradient); } @@ -308,6 +317,8 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id) } } + BLO_read_data_address(reader, &brush->curves_sculpt_settings); + brush->preview = NULL; brush->icon_imbuf = NULL; } @@ -489,6 +500,10 @@ Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) brush->ob_mode = ob_mode; + if (ob_mode == OB_MODE_SCULPT_CURVES) { + BKE_brush_init_curves_sculpt_settings(brush); + } + return brush; } @@ -1537,6 +1552,14 @@ void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, const bool } } +void BKE_brush_init_curves_sculpt_settings(Brush *brush) +{ + if (brush->curves_sculpt_settings == NULL) { + brush->curves_sculpt_settings = MEM_callocN(sizeof(BrushCurvesSculptSettings), __func__); + } + brush->curves_sculpt_settings->add_amount = 1; +} + struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode) { Brush *brush; diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index af776e9dbb0..891145b47c9 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -129,7 +129,7 @@ static void collection_free_data(ID *id) { Collection *collection = (Collection *)id; - /* No animdata here. */ + /* No animation-data here. */ BKE_previewimg_free(&collection->preview); BLI_freelistN(&collection->gobject); diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 0b619c1a969..02710982564 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -62,6 +62,14 @@ using blender::IndexRange; /* local */ // static CLG_LogRef LOG = {"bke.curve"}; +enum class NURBSValidationStatus { + Valid, + AtLeastTwoPointsRequired, + MorePointsThanOrderRequired, + MoreRowsForBezierRequired, + MorePointsForBezierRequired +}; + static void curve_init_data(ID *id) { Curve *curve = (Curve *)id; @@ -4700,56 +4708,94 @@ void BKE_curve_nurbs_key_vert_tilts_apply(ListBase *lb, const float *key) } } -bool BKE_nurb_valid_message(const int pnts, - const short order, - const short flag, - const short type, - const bool is_surf, - const char *dir, - char *message_dst, - const size_t maxncpy) +static NURBSValidationStatus nurb_check_valid(const int pnts, + const short order, + const short flag, + const short type, + const bool is_surf, + int *r_points_needed) { - const char *msg_template = ""; - uint16_t points_needed = 0; - if (pnts <= 1) { - msg_template = TIP_("At least two points required."); + return NURBSValidationStatus::AtLeastTwoPointsRequired; } - else if (type == CU_NURBS) { + if (type == CU_NURBS) { if (pnts < order) { - msg_template = TIP_("Must have more control points than Order"); + return NURBSValidationStatus::MorePointsThanOrderRequired; } - else if (flag & CU_NURB_BEZIER) { + if (flag & CU_NURB_BEZIER) { + int points_needed = 0; if (flag & CU_NURB_CYCLIC) { - const uint16_t remainder = pnts % (order - 1); + const int remainder = pnts % (order - 1); points_needed = remainder > 0 ? order - 1 - remainder : 0; } else if (((flag & CU_NURB_ENDPOINT) == 0) && pnts <= order) { points_needed = order + 1 - pnts; } if (points_needed) { - msg_template = is_surf ? TIP_("%d more %s row(s) needed for Bezier") : - TIP_("%d more point(s) needed for Bezier"); + *r_points_needed = points_needed; + return is_surf ? NURBSValidationStatus::MoreRowsForBezierRequired : + NURBSValidationStatus::MorePointsForBezierRequired; } } } + return NURBSValidationStatus::Valid; +} + +bool BKE_nurb_valid_message(const int pnts, + const short order, + const short flag, + const short type, + const bool is_surf, + const int dir, + char *message_dst, + const size_t maxncpy) +{ + int points_needed; + NURBSValidationStatus status = nurb_check_valid( + pnts, order, flag, type, is_surf, &points_needed); - if (message_dst) { - BLI_snprintf(message_dst, maxncpy, msg_template, points_needed, dir); + const char *msg_template = nullptr; + switch (status) { + case NURBSValidationStatus::Valid: + message_dst[0] = 0; + return false; + case NURBSValidationStatus::AtLeastTwoPointsRequired: + if (dir == 1) { + /* Exception made for curves as their pntsv == 1. */ + message_dst[0] = 0; + return false; + } + msg_template = TIP_("At least two points required."); + break; + case NURBSValidationStatus::MorePointsThanOrderRequired: + msg_template = TIP_("Must have more control points than Order"); + break; + case NURBSValidationStatus::MoreRowsForBezierRequired: + msg_template = TIP_("%d more %s row(s) needed for Bezier"); + break; + case NURBSValidationStatus::MorePointsForBezierRequired: + msg_template = TIP_("%d more point(s) needed for Bezier"); + break; } - return msg_template[0]; + + BLI_snprintf(message_dst, maxncpy, msg_template, points_needed, dir == 0 ? "U" : "V"); + return true; } bool BKE_nurb_check_valid_u(const Nurb *nu) { - return !BKE_nurb_valid_message( - nu->pntsu, nu->orderu, nu->flagu, nu->type, nu->pntsv > 1, "U", nullptr, 0); + int points_needed; + return NURBSValidationStatus::Valid == + nurb_check_valid( + nu->pntsu, nu->orderu, nu->flagu, nu->type, nu->pntsv > 1, &points_needed); } bool BKE_nurb_check_valid_v(const Nurb *nu) { - return !BKE_nurb_valid_message( - nu->pntsv, nu->orderv, nu->flagv, nu->type, nu->pntsv > 1, "V", nullptr, 0); + int points_needed; + return NURBSValidationStatus::Valid == + nurb_check_valid( + nu->pntsv, nu->orderv, nu->flagv, nu->type, nu->pntsv > 1, &points_needed); } bool BKE_nurb_check_valid_uv(const Nurb *nu) diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc new file mode 100644 index 00000000000..c02555dcf6a --- /dev/null +++ b/source/blender/blenkernel/intern/curve_bezier.cc @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include <algorithm> + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" + +namespace blender::bke::curves::bezier { + +bool segment_is_vector(const Span<int8_t> handle_types_left, + const Span<int8_t> handle_types_right, + const int segment_index) +{ + BLI_assert(handle_types_left.index_range().drop_back(1).contains(segment_index)); + return handle_types_right[segment_index] == BEZIER_HANDLE_VECTOR && + handle_types_left[segment_index + 1] == BEZIER_HANDLE_VECTOR; +} + +bool last_cylic_segment_is_vector(const Span<int8_t> handle_types_left, + const Span<int8_t> handle_types_right) +{ + return handle_types_right.last() == BEZIER_HANDLE_VECTOR && + handle_types_left.first() == BEZIER_HANDLE_VECTOR; +} + +void calculate_evaluated_offsets(const Span<int8_t> handle_types_left, + const Span<int8_t> handle_types_right, + const bool cyclic, + const int resolution, + MutableSpan<int> evaluated_offsets) +{ + const int size = handle_types_left.size(); + BLI_assert(evaluated_offsets.size() == size); + + if (size == 1) { + evaluated_offsets.first() = 1; + return; + } + + int offset = 0; + + for (const int i : IndexRange(size - 1)) { + offset += segment_is_vector(handle_types_left, handle_types_right, i) ? 1 : resolution; + evaluated_offsets[i] = offset; + } + + if (cyclic) { + offset += last_cylic_segment_is_vector(handle_types_left, handle_types_right) ? 1 : resolution; + } + else { + offset++; + } + + evaluated_offsets.last() = offset; +} + +void evaluate_segment(const float3 &point_0, + const float3 &point_1, + const float3 &point_2, + const float3 &point_3, + MutableSpan<float3> result) +{ + BLI_assert(result.size() > 0); + const float inv_len = 1.0f / static_cast<float>(result.size()); + const float inv_len_squared = inv_len * inv_len; + const float inv_len_cubed = inv_len_squared * inv_len; + + const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len; + const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared; + const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed; + + float3 q0 = point_0; + float3 q1 = rt1 + rt2 + rt3; + float3 q2 = 2.0f * rt2 + 6.0f * rt3; + float3 q3 = 6.0f * rt3; + for (const int i : result.index_range()) { + result[i] = q0; + q0 += q1; + q1 += q2; + q2 += q3; + } +} + +void calculate_evaluated_positions(const Span<float3> positions, + const Span<float3> handles_left, + const Span<float3> handles_right, + const Span<int> evaluated_offsets, + MutableSpan<float3> evaluated_positions) +{ + BLI_assert(evaluated_offsets.last() == evaluated_positions.size()); + BLI_assert(evaluated_offsets.size() == positions.size()); + + /* Evaluate the first segment. */ + evaluate_segment(positions.first(), + handles_right.first(), + handles_left[1], + positions[1], + evaluated_positions.take_front(evaluated_offsets.first())); + + /* Give each task fewer segments as the resolution gets larger. */ + const int grain_size = std::max<int>(evaluated_positions.size() / positions.size() * 32, 1); + threading::parallel_for( + positions.index_range().drop_back(1).drop_front(1), grain_size, [&](IndexRange range) { + for (const int i : range) { + const IndexRange evaluated_range = offsets_to_range(evaluated_offsets, i - 1); + if (evaluated_range.size() == 1) { + evaluated_positions[evaluated_range.first()] = positions[i]; + } + else { + evaluate_segment(positions[i], + handles_right[i], + handles_left[i + 1], + positions[i + 1], + evaluated_positions.slice(evaluated_range)); + } + } + }); + + /* Evaluate the final cyclic segment if necessary. */ + const IndexRange last_segment_points = offsets_to_range(evaluated_offsets, positions.size() - 2); + if (last_segment_points.size() == 1) { + evaluated_positions.last() = positions.last(); + } + else { + evaluate_segment(positions.last(), + handles_right.last(), + handles_left.first(), + positions.first(), + evaluated_positions.slice(last_segment_points)); + } +} + +/** \} */ + +} // namespace blender::bke::curves::bezier diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc new file mode 100644 index 00000000000..ea3672dd56b --- /dev/null +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" + +namespace blender::bke::curves::catmull_rom { + +int calculate_evaluated_size(const int points_num, const bool cyclic, const int resolution) +{ + const int eval_size = resolution * curve_segment_size(points_num, cyclic); + /* If the curve isn't cyclic, one last point is added to the final point. */ + return (cyclic && points_num > 2) ? eval_size : eval_size + 1; +} + +/* Adapted from Cycles #catmull_rom_basis_eval function. */ +template<typename T> +static T calculate_basis(const T &a, const T &b, const T &c, const T &d, const float parameter) +{ + const float t = parameter; + const float s = 1.0f - parameter; + const float n0 = -t * s * s; + const float n1 = 2.0f + t * t * (3.0f * t - 5.0f); + const float n2 = 2.0f + s * s * (3.0f * s - 5.0f); + const float n3 = -s * t * t; + return 0.5f * (a * n0 + b * n1 + c * n2 + d * n3); +} + +template<typename T> +static void evaluate_segment(const T &a, const T &b, const T &c, const T &d, MutableSpan<T> dst) +{ + const float step = 1.0f / dst.size(); + dst.first() = b; + for (const int i : dst.index_range().drop_front(1)) { + dst[i] = calculate_basis<T>(a, b, c, d, i * step); + } +} + +template<typename T> +static void interpolate_to_evaluated(const Span<T> src, + const bool cyclic, + const int resolution, + MutableSpan<T> dst) + +{ + BLI_assert(dst.size() == calculate_evaluated_size(src.size(), cyclic, resolution)); + + /* - First deal with one and two point curves need special attention. + * - Then evaluate the first and last segment(s) whose control points need to wrap around + * to the other side of the source array. + * - Finally evaluate all of the segments in the middle in parallel. */ + + if (src.size() == 1) { + dst.first() = src.first(); + return; + } + if (src.size() == 2) { + evaluate_segment(src.first(), src.first(), src.last(), src.last(), dst); + dst.last() = src.last(); + return; + } + + if (cyclic) { + /* The first segment. */ + evaluate_segment(src.last(), src[0], src[1], src[2], dst.take_front(resolution)); + /* The second-to-last segment. */ + evaluate_segment(src.last(2), + src.last(1), + src.last(), + src.first(), + dst.take_back(resolution * 2).drop_back(resolution)); + /* The last segment. */ + evaluate_segment(src.last(1), src.last(), src[0], src[1], dst.take_back(resolution)); + } + else { + /* The first segment. */ + evaluate_segment(src[0], src[0], src[1], src[2], dst.take_front(resolution)); + /* The last segment. */ + evaluate_segment( + src.last(2), src.last(1), src.last(), src.last(), dst.drop_back(1).take_back(resolution)); + /* The final point of the last segment. */ + dst.last() = src.last(); + } + + /* Evaluate every segment that isn't the first or last. */ + const int grain_size = std::max(512 / resolution, 1); + const IndexRange inner_range = src.index_range().drop_back(2).drop_front(1); + threading::parallel_for(inner_range, grain_size, [&](IndexRange range) { + for (const int i : range) { + const IndexRange segment_range(resolution * i, resolution); + evaluate_segment(src[i - 1], src[i], src[i + 1], src[i + 2], dst.slice(segment_range)); + } + }); +} + +void interpolate_to_evaluated(const GSpan src, + const bool cyclic, + const int resolution, + GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + /* TODO: Use DefaultMixer or other generic mixing in the basis evaluation function to simplify + * supporting more types. */ + if constexpr (is_same_any_v<T, float, float2, float3, float4, int8_t, int, int64_t>) { + interpolate_to_evaluated(src.typed<T>(), cyclic, resolution, dst.typed<T>()); + } + }); +} + +} // namespace blender::bke::curves::catmull_rom diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 191a510947e..2cf83b57881 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -20,6 +20,8 @@ using blender::Array; using blender::float3; using blender::float4x4; +using blender::GVArray; +using blender::GVArray_GSpan; using blender::IndexRange; using blender::Map; using blender::MutableSpan; @@ -32,8 +34,6 @@ using blender::bke::AttributeIDRef; using blender::bke::OutputAttribute; using blender::bke::OutputAttribute_Typed; using blender::bke::ReadAttributeLookup; -using blender::fn::GVArray; -using blender::fn::GVArray_GSpan; blender::Span<SplinePtr> CurveEval::splines() const { @@ -398,7 +398,7 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) VArray<int8_t> curve_types = geometry.curve_types(); std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>(); for (const int curve_index : curve_types.index_range()) { - const IndexRange point_range = geometry.range_for_curve(curve_index); + const IndexRange point_range = geometry.points_for_curve(curve_index); std::unique_ptr<Spline> spline; switch (curve_types[curve_index]) { @@ -489,7 +489,7 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) const Spline &spline = *curve_eval.splines()[curve_index]; curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); - const IndexRange point_range = geometry.range_for_curve(curve_index); + const IndexRange point_range = geometry.points_for_curve(curve_index); switch (spline.type()) { case CURVE_TYPE_POLY: diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc new file mode 100644 index 00000000000..0114c0b45f4 --- /dev/null +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_attribute_math.hh" + +#include "BKE_curves.hh" + +namespace blender::bke::curves::nurbs { + +bool check_valid_size_and_order(const int points_num, + const int8_t order, + const bool cyclic, + const KnotsMode knots_mode) +{ + if (points_num < order) { + return false; + } + + if (ELEM(knots_mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER)) { + if (knots_mode == NURBS_KNOT_MODE_BEZIER && points_num <= order) { + return false; + } + return (!cyclic || points_num % (order - 1) == 0); + } + + return true; +} + +int calculate_evaluated_size(const int points_num, + const int8_t order, + const bool cyclic, + const int resolution, + const KnotsMode knots_mode) +{ + if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) { + return 0; + } + return resolution * curve_segment_size(points_num, cyclic); +} + +int knots_size(const int points_num, const int8_t order, const bool cyclic) +{ + if (cyclic) { + return points_num + order * 2 - 1; + } + return points_num + order; +} + +void calculate_knots(const int points_num, + const KnotsMode mode, + const int8_t order, + const bool cyclic, + MutableSpan<float> knots) +{ + BLI_assert(knots.size() == knots_size(points_num, order, cyclic)); + UNUSED_VARS_NDEBUG(points_num); + + const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); + const bool is_end_point = ELEM(mode, NURBS_KNOT_MODE_ENDPOINT, NURBS_KNOT_MODE_ENDPOINT_BEZIER); + /* Inner knots are always repeated once except on Bezier case. */ + const int repeat_inner = is_bezier ? order - 1 : 1; + /* How many times to repeat 0.0 at the beginning of knot. */ + const int head = is_end_point ? (order - (cyclic ? 1 : 0)) : + (is_bezier ? min_ii(2, repeat_inner) : 1); + /* Number of knots replicating widths of the starting knots. + * Covers both Cyclic and EndPoint cases. */ + const int tail = cyclic ? 2 * order - 1 : (is_end_point ? order : 0); + + int r = head; + float current = 0.0f; + + const int offset = is_end_point && cyclic ? 1 : 0; + if (offset) { + knots[0] = current; + current += 1.0f; + } + + for (const int i : IndexRange(offset, knots.size() - offset - tail)) { + knots[i] = current; + r--; + if (r == 0) { + current += 1.0; + r = repeat_inner; + } + } + + const int tail_index = knots.size() - tail; + for (const int i : IndexRange(tail)) { + knots[tail_index + i] = current + (knots[i] - knots[0]); + } +} + +static void calculate_basis_for_point(const float parameter, + const int points_num, + const int degree, + const Span<float> knots, + MutableSpan<float> r_weights, + int &r_start_index) +{ + const int order = degree + 1; + + int start = 0; + int end = 0; + for (const int i : IndexRange(points_num + degree)) { + const bool knots_equal = knots[i] == knots[i + 1]; + if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) { + continue; + } + + start = std::max(i - degree, 0); + end = i; + break; + } + + Array<float, 12> buffer(order * 2, 0.0f); + + buffer[end - start] = 1.0f; + + for (const int i_order : IndexRange(2, degree)) { + if (end + i_order >= knots.size()) { + end = points_num + degree - i_order; + } + for (const int i : IndexRange(end - start + 1)) { + const int knot_index = start + i; + + float new_basis = 0.0f; + if (buffer[i] != 0.0f) { + new_basis += ((parameter - knots[knot_index]) * buffer[i]) / + (knots[knot_index + i_order - 1] - knots[knot_index]); + } + + if (buffer[i + 1] != 0.0f) { + new_basis += ((knots[knot_index + i_order] - parameter) * buffer[i + 1]) / + (knots[knot_index + i_order] - knots[knot_index + 1]); + } + + buffer[i] = new_basis; + } + } + + buffer.as_mutable_span().drop_front(end - start + 1).fill(0.0f); + r_weights.copy_from(buffer.as_span().take_front(order)); + r_start_index = start; +} + +void calculate_basis_cache(const int points_num, + const int evaluated_size, + const int8_t order, + const bool cyclic, + const Span<float> knots, + BasisCache &basis_cache) +{ + BLI_assert(points_num > 0); + + const int8_t degree = order - 1; + + basis_cache.weights.resize(evaluated_size * order); + basis_cache.start_indices.resize(evaluated_size); + + if (evaluated_size == 0) { + return; + } + + MutableSpan<float> basis_weights(basis_cache.weights); + MutableSpan<int> basis_start_indices(basis_cache.start_indices); + + const int last_control_point_index = cyclic ? points_num + degree : points_num; + const int evaluated_segment_size = curve_segment_size(evaluated_size, cyclic); + + const float start = knots[degree]; + const float end = knots[last_control_point_index]; + const float step = (end - start) / evaluated_segment_size; + for (const int i : IndexRange(evaluated_size)) { + /* Clamp parameter due to floating point inaccuracy. */ + const float parameter = std::clamp(start + step * i, knots[0], knots[points_num + degree]); + + MutableSpan<float> point_weights = basis_weights.slice(i * order, order); + + calculate_basis_for_point( + parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]); + } +} + +template<typename T> +static void interpolate_to_evaluated(const BasisCache &basis_cache, + const int8_t order, + const Span<T> src, + MutableSpan<T> dst) +{ + attribute_math::DefaultMixer<T> mixer{dst}; + + for (const int i : dst.index_range()) { + Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); + + for (const int j : point_weights.index_range()) { + const int point_index = (basis_cache.start_indices[i] + j) % src.size(); + mixer.mix_in(i, src[point_index], point_weights[j]); + } + } + + mixer.finalize(); +} + +template<typename T> +static void interpolate_to_evaluated_rational(const BasisCache &basis_cache, + const int8_t order, + const Span<float> control_weights, + const Span<T> src, + MutableSpan<T> dst) +{ + attribute_math::DefaultMixer<T> mixer{dst}; + + for (const int i : dst.index_range()) { + Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); + + for (const int j : point_weights.index_range()) { + const int point_index = (basis_cache.start_indices[i] + j) % src.size(); + const float weight = point_weights[j] * control_weights[point_index]; + mixer.mix_in(i, src[point_index], weight); + } + } + + mixer.finalize(); +} + +void interpolate_to_evaluated(const BasisCache &basis_cache, + const int8_t order, + const Span<float> control_weights, + const GSpan src, + GMutableSpan dst) +{ + BLI_assert(dst.size() == basis_cache.start_indices.size()); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { + if (control_weights.is_empty()) { + interpolate_to_evaluated(basis_cache, order, src.typed<T>(), dst.typed<T>()); + } + else { + interpolate_to_evaluated_rational( + basis_cache, order, control_weights, src.typed<T>(), dst.typed<T>()); + } + } + }); +} + +} // namespace blender::bke::curves::nurbs diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 5d80ef47908..9b22a4c9726 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -16,9 +16,6 @@ #include "BKE_curve_to_mesh.hh" -using blender::fn::GMutableSpan; -using blender::fn::GSpan; - namespace blender::bke { /** Information about the creation of one curve spline and profile spline combination. */ diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 838f7f28e93..82db1176759 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -366,19 +366,19 @@ void BKE_curves_batch_cache_free(Curves *curves) namespace blender::bke { -Curves *curves_new_nomain(const int point_size, const int curves_size) +Curves *curves_new_nomain(const int points_num, const int curves_num) { Curves *curves = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); - geometry.resize(point_size, curves_size); + geometry.resize(points_num, curves_num); return curves; } -Curves *curves_new_nomain_single(const int point_size, const CurveType type) +Curves *curves_new_nomain_single(const int points_num, const CurveType type) { - Curves *curves = curves_new_nomain(point_size, 1); + Curves *curves = curves_new_nomain(points_num, 1); CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); - geometry.offsets().last() = point_size; + geometry.offsets().last() = points_num; geometry.curve_types().first() = type; return curves; } diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index dd91e788e5a..5bb6a97fa49 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -4,9 +4,13 @@ * \ingroup bke */ +#include <mutex> +#include <utility> + #include "MEM_guardedalloc.h" #include "BLI_bounds.hh" +#include "BLI_index_mask_ops.hh" #include "DNA_curves_types.h" @@ -19,6 +23,16 @@ static const std::string ATTR_POSITION = "position"; static const std::string ATTR_RADIUS = "radius"; static const std::string ATTR_CURVE_TYPE = "curve_type"; static const std::string ATTR_CYCLIC = "cyclic"; +static const std::string ATTR_RESOLUTION = "resolution"; +static const std::string ATTR_HANDLE_TYPE_LEFT = "handle_type_left"; +static const std::string ATTR_HANDLE_TYPE_RIGHT = "handle_type_right"; +static const std::string ATTR_HANDLE_POSITION_LEFT = "handle_left"; +static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right"; +static const std::string ATTR_NURBS_ORDER = "nurbs_order"; +static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight"; +static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode"; +static const std::string ATTR_SURFACE_TRIANGLE_INDEX = "surface_triangle_index"; +static const std::string ATTR_SURFACE_TRIANGLE_COORDINATE = "surface_triangle_coordinate"; /* -------------------------------------------------------------------- */ /** \name Constructors/Destructor @@ -135,30 +149,24 @@ CurvesGeometry::~CurvesGeometry() /** \name Accessors * \{ */ -int CurvesGeometry::points_size() const +int CurvesGeometry::points_num() const { return this->point_size; } -int CurvesGeometry::curves_size() const +int CurvesGeometry::curves_num() const { return this->curve_size; } IndexRange CurvesGeometry::points_range() const { - return IndexRange(this->points_size()); + return IndexRange(this->points_num()); } IndexRange CurvesGeometry::curves_range() const { - return IndexRange(this->curves_size()); -} - -int CurvesGeometry::evaluated_points_size() const -{ - /* TODO: Implement when there are evaluated points. */ - return 0; + return IndexRange(this->curves_num()); } -IndexRange CurvesGeometry::range_for_curve(const int index) const +IndexRange CurvesGeometry::points_for_curve(const int index) const { BLI_assert(this->curve_size > 0); BLI_assert(this->curve_offsets != nullptr); @@ -167,7 +175,7 @@ IndexRange CurvesGeometry::range_for_curve(const int index) const return {offset, offset_next - offset}; } -IndexRange CurvesGeometry::range_for_curves(const IndexRange curves) const +IndexRange CurvesGeometry::points_for_curves(const IndexRange curves) const { BLI_assert(this->curve_size > 0); BLI_assert(this->curve_offsets != nullptr); @@ -178,7 +186,7 @@ IndexRange CurvesGeometry::range_for_curves(const IndexRange curves) const static int domain_size(const CurvesGeometry &curves, const AttributeDomain domain) { - return domain == ATTR_DOMAIN_POINT ? curves.points_size() : curves.curves_size(); + return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num(); } static CustomData &domain_custom_data(CurvesGeometry &curves, const AttributeDomain domain) @@ -210,6 +218,22 @@ static VArray<T> get_varray_attribute(const CurvesGeometry &curves, } template<typename T> +static Span<T> get_span_attribute(const CurvesGeometry &curves, + const AttributeDomain domain, + const StringRefNull name) +{ + const int size = domain_size(curves, domain); + const CustomData &custom_data = domain_custom_data(curves, domain); + const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); + + T *data = (T *)CustomData_get_layer_named(&custom_data, type, name.c_str()); + if (data == nullptr) { + return {}; + } + return {data, size}; +} + +template<typename T> static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves, const AttributeDomain domain, const StringRefNull name) @@ -239,6 +263,20 @@ MutableSpan<int8_t> CurvesGeometry::curve_types() return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_CURVE_TYPE); } +bool CurvesGeometry::has_curve_with_type(const CurveType type) const +{ + const VArray<int8_t> curve_types = this->curve_types(); + if (curve_types.is_single()) { + return curve_types.get_internal_single() == type; + } + if (curve_types.is_span()) { + return curve_types.get_internal_span().contains(type); + } + /* The curves types array should be a single value or a span. */ + BLI_assert_unreachable(); + return false; +} + MutableSpan<float3> CurvesGeometry::positions() { this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( @@ -269,16 +307,370 @@ MutableSpan<bool> CurvesGeometry::cyclic() return get_mutable_attribute<bool>(*this, ATTR_DOMAIN_CURVE, ATTR_CYCLIC); } -void CurvesGeometry::resize(const int point_size, const int curve_size) +VArray<int> CurvesGeometry::resolution() const +{ + return get_varray_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_RESOLUTION, 12); +} + +MutableSpan<int> CurvesGeometry::resolution() +{ + return get_mutable_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_RESOLUTION); +} + +VArray<int8_t> CurvesGeometry::handle_types_left() const +{ + return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0); +} +MutableSpan<int8_t> CurvesGeometry::handle_types_left() +{ + return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT); +} + +VArray<int8_t> CurvesGeometry::handle_types_right() const +{ + return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_RIGHT, 0); +} +MutableSpan<int8_t> CurvesGeometry::handle_types_right() +{ + return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_RIGHT); +} + +Span<float3> CurvesGeometry::handle_positions_left() const +{ + return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_LEFT); +} +MutableSpan<float3> CurvesGeometry::handle_positions_left() +{ + return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_LEFT); +} + +Span<float3> CurvesGeometry::handle_positions_right() const +{ + return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_RIGHT); +} +MutableSpan<float3> CurvesGeometry::handle_positions_right() +{ + return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_RIGHT); +} + +VArray<int8_t> CurvesGeometry::nurbs_orders() const +{ + return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_ORDER, 4); +} +MutableSpan<int8_t> CurvesGeometry::nurbs_orders() +{ + return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_ORDER); +} + +Span<float> CurvesGeometry::nurbs_weights() const +{ + return get_span_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_NURBS_WEIGHT); +} +MutableSpan<float> CurvesGeometry::nurbs_weights() +{ + return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_NURBS_WEIGHT); +} + +VArray<int8_t> CurvesGeometry::nurbs_knots_modes() const +{ + return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_KNOTS_MODE, 0); +} +MutableSpan<int8_t> CurvesGeometry::nurbs_knots_modes() +{ + return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_KNOTS_MODE); +} + +VArray<int> CurvesGeometry::surface_triangle_indices() const +{ + return get_varray_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_INDEX, -1); +} + +MutableSpan<int> CurvesGeometry::surface_triangle_indices() +{ + return get_mutable_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_INDEX); +} + +Span<float2> CurvesGeometry::surface_triangle_coords() const +{ + return get_span_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_COORDINATE); +} + +MutableSpan<float2> CurvesGeometry::surface_triangle_coords() +{ + return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_COORDINATE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Evaluation + * \{ */ + +template<typename SizeFn> void build_offsets(MutableSpan<int> offsets, const SizeFn &size_fn) +{ + int offset = 0; + for (const int i : offsets.drop_back(1).index_range()) { + offsets[i] = offset; + offset += size_fn(i); + } + offsets.last() = offset; +} + +static void calculate_evaluated_offsets(const CurvesGeometry &curves, + MutableSpan<int> offsets, + MutableSpan<int> bezier_evaluated_offsets) +{ + VArray<int8_t> types = curves.curve_types(); + VArray<int> resolution = curves.resolution(); + VArray<bool> cyclic = curves.cyclic(); + + VArray_Span<int8_t> handle_types_left{curves.handle_types_left()}; + VArray_Span<int8_t> handle_types_right{curves.handle_types_right()}; + + VArray<int8_t> nurbs_orders = curves.nurbs_orders(); + VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes(); + + build_offsets(offsets, [&](const int curve_index) -> int { + const IndexRange points = curves.points_for_curve(curve_index); + switch (types[curve_index]) { + case CURVE_TYPE_CATMULL_ROM: + return curves::catmull_rom::calculate_evaluated_size( + points.size(), cyclic[curve_index], resolution[curve_index]); + case CURVE_TYPE_POLY: + return points.size(); + case CURVE_TYPE_BEZIER: + curves::bezier::calculate_evaluated_offsets(handle_types_left.slice(points), + handle_types_right.slice(points), + cyclic[curve_index], + resolution[curve_index], + bezier_evaluated_offsets.slice(points)); + return bezier_evaluated_offsets[points.last()]; + case CURVE_TYPE_NURBS: + return curves::nurbs::calculate_evaluated_size(points.size(), + nurbs_orders[curve_index], + cyclic[curve_index], + resolution[curve_index], + KnotsMode(nurbs_knots_modes[curve_index])); + } + BLI_assert_unreachable(); + return 0; + }); +} + +int CurvesGeometry::evaluated_points_num() const +{ + /* This could avoid calculating offsets in the future in simple circumstances. */ + return this->evaluated_offsets().last(); +} + +IndexRange CurvesGeometry::evaluated_points_for_curve(int index) const +{ + BLI_assert(!this->runtime->offsets_cache_dirty); + return offsets_to_range(this->runtime->evaluated_offsets_cache.as_span(), index); +} + +IndexRange CurvesGeometry::evaluated_points_for_curves(const IndexRange curves) const +{ + BLI_assert(!this->runtime->offsets_cache_dirty); + BLI_assert(this->curve_size > 0); + const int offset = this->runtime->evaluated_offsets_cache[curves.start()]; + const int offset_next = this->runtime->evaluated_offsets_cache[curves.one_after_last()]; + return {offset, offset_next - offset}; +} + +void CurvesGeometry::ensure_evaluated_offsets() const { - if (point_size != this->point_size) { - CustomData_realloc(&this->point_data, point_size); - this->point_size = point_size; + if (!this->runtime->offsets_cache_dirty) { + return; } - if (curve_size != this->curve_size) { - CustomData_realloc(&this->curve_data, curve_size); - this->curve_size = curve_size; - this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curve_size + 1)); + + /* A double checked lock. */ + std::scoped_lock lock{this->runtime->offsets_cache_mutex}; + if (!this->runtime->offsets_cache_dirty) { + return; + } + + threading::isolate_task([&]() { + this->runtime->evaluated_offsets_cache.resize(this->curves_num() + 1); + + if (this->has_curve_with_type(CURVE_TYPE_BEZIER)) { + this->runtime->bezier_evaluated_offsets.resize(this->points_num()); + } + else { + this->runtime->bezier_evaluated_offsets.clear_and_make_inline(); + } + + calculate_evaluated_offsets( + *this, this->runtime->evaluated_offsets_cache, this->runtime->bezier_evaluated_offsets); + }); + + this->runtime->offsets_cache_dirty = false; +} + +Span<int> CurvesGeometry::evaluated_offsets() const +{ + this->ensure_evaluated_offsets(); + return this->runtime->evaluated_offsets_cache; +} + +IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, + Vector<int64_t> &r_indices) const +{ + + VArray<int8_t> types = this->curve_types(); + if (types.is_single()) { + if (types.get_internal_single() == type) { + return IndexMask(types.size()); + } + return {}; + } + Span<int8_t> types_span = types.get_internal_span(); + return index_mask_ops::find_indices_based_on_predicate( + IndexMask(types.size()), 1024, r_indices, [&](const int index) { + return types_span[index] == type; + }); +} + +void CurvesGeometry::ensure_nurbs_basis_cache() const +{ + if (!this->runtime->nurbs_basis_cache_dirty) { + return; + } + + /* A double checked lock. */ + std::scoped_lock lock{this->runtime->nurbs_basis_cache_mutex}; + if (!this->runtime->nurbs_basis_cache_dirty) { + return; + } + + threading::isolate_task([&]() { + Vector<int64_t> nurbs_indices; + const IndexMask nurbs_mask = this->indices_for_curve_type(CURVE_TYPE_NURBS, nurbs_indices); + if (nurbs_mask.is_empty()) { + return; + } + + this->runtime->nurbs_basis_cache.resize(this->curves_num()); + MutableSpan<curves::nurbs::BasisCache> basis_caches(this->runtime->nurbs_basis_cache); + + VArray<bool> cyclic = this->cyclic(); + VArray<int8_t> orders = this->nurbs_orders(); + VArray<int8_t> knots_modes = this->nurbs_knots_modes(); + + threading::parallel_for(nurbs_mask.index_range(), 64, [&](const IndexRange range) { + for (const int curve_index : nurbs_mask.slice(range)) { + const IndexRange points = this->points_for_curve(curve_index); + const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); + + const int8_t order = orders[curve_index]; + const bool is_cyclic = cyclic[curve_index]; + const KnotsMode mode = KnotsMode(knots_modes[curve_index]); + + const int knots_size = curves::nurbs::knots_size(points.size(), order, is_cyclic); + Array<float> knots(knots_size); + curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots); + curves::nurbs::calculate_basis_cache(points.size(), + evaluated_points.size(), + order, + is_cyclic, + knots, + basis_caches[curve_index]); + } + }); + }); + + this->runtime->nurbs_basis_cache_dirty = false; +} + +Span<float3> CurvesGeometry::evaluated_positions() const +{ + if (!this->runtime->position_cache_dirty) { + return this->runtime->evaluated_position_cache; + } + + /* A double checked lock. */ + std::scoped_lock lock{this->runtime->position_cache_mutex}; + if (!this->runtime->position_cache_dirty) { + return this->runtime->evaluated_position_cache; + } + + threading::isolate_task([&]() { + this->runtime->evaluated_position_cache.resize(this->evaluated_points_num()); + MutableSpan<float3> evaluated_positions = this->runtime->evaluated_position_cache; + + VArray<int8_t> types = this->curve_types(); + VArray<bool> cyclic = this->cyclic(); + VArray<int> resolution = this->resolution(); + Span<float3> positions = this->positions(); + + Span<float3> handle_positions_left = this->handle_positions_left(); + Span<float3> handle_positions_right = this->handle_positions_right(); + Span<int> bezier_evaluated_offsets = this->runtime->bezier_evaluated_offsets; + + VArray<int8_t> nurbs_orders = this->nurbs_orders(); + Span<float> nurbs_weights = this->nurbs_weights(); + + this->ensure_nurbs_basis_cache(); + + threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { + for (const int curve_index : curves_range) { + const IndexRange points = this->points_for_curve(curve_index); + const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); + + switch (types[curve_index]) { + case CURVE_TYPE_CATMULL_ROM: + curves::catmull_rom::interpolate_to_evaluated( + positions.slice(points), + cyclic[curve_index], + resolution[curve_index], + evaluated_positions.slice(evaluated_points)); + break; + case CURVE_TYPE_POLY: + evaluated_positions.slice(evaluated_points).copy_from(positions.slice(points)); + break; + case CURVE_TYPE_BEZIER: + curves::bezier::calculate_evaluated_positions( + positions.slice(points), + handle_positions_left.slice(points), + handle_positions_right.slice(points), + bezier_evaluated_offsets.slice(points), + evaluated_positions.slice(evaluated_points)); + break; + case CURVE_TYPE_NURBS: { + curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index], + nurbs_orders[curve_index], + nurbs_weights.slice(points), + positions.slice(points), + evaluated_positions.slice(evaluated_points)); + break; + } + default: + BLI_assert_unreachable(); + break; + } + } + }); + }); + + return this->runtime->evaluated_position_cache; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operations + * \{ */ + +void CurvesGeometry::resize(const int points_num, const int curves_num) +{ + if (points_num != this->point_size) { + CustomData_realloc(&this->point_data, points_num); + this->point_size = points_num; + } + if (curves_num != this->curve_size) { + CustomData_realloc(&this->curve_data, curves_num); + this->curve_size = curves_num; + this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); this->update_customdata_pointers(); @@ -295,15 +687,16 @@ void CurvesGeometry::tag_topology_changed() this->runtime->position_cache_dirty = true; this->runtime->tangent_cache_dirty = true; this->runtime->normal_cache_dirty = true; + this->runtime->offsets_cache_dirty = true; + this->runtime->nurbs_basis_cache_dirty = true; } void CurvesGeometry::tag_normals_changed() { this->runtime->normal_cache_dirty = true; } -void CurvesGeometry::translate(const float3 &translation) +static void translate_positions(MutableSpan<float3> positions, const float3 &translation) { - MutableSpan<float3> positions = this->positions(); threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) { for (float3 &position : positions.slice(range)) { position += translation; @@ -311,9 +704,8 @@ void CurvesGeometry::translate(const float3 &translation) }); } -void CurvesGeometry::transform(const float4x4 &matrix) +static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix) { - MutableSpan<float3> positions = this->positions(); threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) { for (float3 &position : positions.slice(range)) { position = matrix * position; @@ -321,11 +713,37 @@ void CurvesGeometry::transform(const float4x4 &matrix) }); } +void CurvesGeometry::translate(const float3 &translation) +{ + /* Use `as_const` because the non-const functions can add the handle attributes. */ + translate_positions(this->positions(), translation); + if (!std::as_const(*this).handle_positions_left().is_empty()) { + translate_positions(this->handle_positions_left(), translation); + } + if (!std::as_const(*this).handle_positions_right().is_empty()) { + translate_positions(this->handle_positions_right(), translation); + } + this->tag_positions_changed(); +} + +void CurvesGeometry::transform(const float4x4 &matrix) +{ + /* Use `as_const` because the non-const functions can add the handle attributes. */ + transform_positions(this->positions(), matrix); + if (!std::as_const(*this).handle_positions_left().is_empty()) { + transform_positions(this->handle_positions_left(), matrix); + } + if (!std::as_const(*this).handle_positions_right().is_empty()) { + transform_positions(this->handle_positions_right(), matrix); + } + this->tag_positions_changed(); +} + static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves) { Span<float3> positions = curves.positions(); if (curves.radius) { - Span<float> radii{curves.radius, curves.points_size()}; + Span<float> radii{curves.radius, curves.points_num()}; return bounds::min_max_with_radii(positions, radii); } return bounds::min_max(positions); @@ -382,7 +800,7 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, new_curve_ranges.append(IndexRange(new_tot_curves, curve_range.size())); new_tot_curves += curve_range.size(); - const IndexRange old_point_range = curves.range_for_curves(curve_range); + const IndexRange old_point_range = curves.points_for_curves(curve_range); old_point_ranges.append(old_point_range); new_point_ranges.append(IndexRange(new_tot_points, old_point_range.size())); new_tot_points += old_point_range.size(); @@ -480,6 +898,106 @@ void CurvesGeometry::remove_curves(const IndexMask curves_to_delete) *this = copy_with_removed_curves(*this, curves_to_delete); } +template<typename T> +static void reverse_curve_point_data(const CurvesGeometry &curves, + const IndexMask curve_selection, + MutableSpan<T> data) +{ + threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) { + for (const int curve_i : curve_selection.slice(range)) { + data.slice(curves.points_for_curve(curve_i)).reverse(); + } + }); +} + +template<typename T> +static void reverse_swap_curve_point_data(const CurvesGeometry &curves, + const IndexMask curve_selection, + MutableSpan<T> data_a, + MutableSpan<T> data_b) +{ + threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) { + for (const int curve_i : curve_selection.slice(range)) { + const IndexRange points = curves.points_for_curve(curve_i); + MutableSpan<T> a = data_a.slice(points); + MutableSpan<T> b = data_b.slice(points); + for (const int i : IndexRange(points.size() / 2)) { + const int end_index = points.size() - 1 - i; + std::swap(a[end_index], b[i]); + std::swap(b[end_index], a[i]); + } + } + }); +} + +static bool layer_matches_name_and_type(const CustomDataLayer &layer, + const StringRef name, + const CustomDataType type) +{ + if (layer.type != type) { + return false; + } + return layer.name == name; +} + +void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse) +{ + CustomData_duplicate_referenced_layers(&this->point_data, this->points_num()); + + /* Collect the Bezier handle attributes while iterating through the point custom data layers; + * they need special treatment later. */ + MutableSpan<float3> positions_left; + MutableSpan<float3> positions_right; + MutableSpan<int8_t> types_left; + MutableSpan<int8_t> types_right; + + for (const int layer_i : IndexRange(this->point_data.totlayer)) { + CustomDataLayer &layer = this->point_data.layers[layer_i]; + + if (positions_left.is_empty() && + layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_LEFT, CD_PROP_FLOAT3)) { + positions_left = {static_cast<float3 *>(layer.data), this->points_num()}; + continue; + } + if (positions_right.is_empty() && + layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_RIGHT, CD_PROP_FLOAT3)) { + positions_right = {static_cast<float3 *>(layer.data), this->points_num()}; + continue; + } + if (types_left.is_empty() && + layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_LEFT, CD_PROP_INT8)) { + types_left = {static_cast<int8_t *>(layer.data), this->points_num()}; + continue; + } + if (types_right.is_empty() && + layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_RIGHT, CD_PROP_INT8)) { + types_right = {static_cast<int8_t *>(layer.data), this->points_num()}; + continue; + } + + const CustomDataType data_type = static_cast<CustomDataType>(layer.type); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + reverse_curve_point_data<T>( + *this, curves_to_reverse, {static_cast<T *>(layer.data), this->points_num()}); + }); + } + + /* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the + * values for the left and right must swap. Use a utility to swap and reverse at the same time, + * to avoid loading the attribute twice. Generally we can expect the right layer to exist when + * the left does, but there's no need to count on it, so check for both attributes. */ + + if (!positions_left.is_empty() && !positions_right.is_empty()) { + reverse_swap_curve_point_data(*this, curves_to_reverse, positions_left, positions_right); + } + if (!types_left.is_empty() && !types_right.is_empty()) { + reverse_swap_curve_point_data(*this, curves_to_reverse, types_left, types_right); + } + + this->tag_topology_changed(); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -499,8 +1017,8 @@ static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, MutableSpan<T> r_values) { attribute_math::DefaultMixer<T> mixer(r_values); - for (const int i_curve : IndexRange(curves.curves_size())) { - for (const int i_point : curves.range_for_curve(i_curve)) { + for (const int i_curve : IndexRange(curves.curves_num())) { + for (const int i_point : curves.points_for_curve(i_curve)) { mixer.mix_in(i_curve, old_values[i_point]); } } @@ -520,8 +1038,8 @@ void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, MutableSpan<bool> r_values) { r_values.fill(true); - for (const int i_curve : IndexRange(curves.curves_size())) { - for (const int i_point : curves.range_for_curve(i_curve)) { + for (const int i_curve : IndexRange(curves.curves_num())) { + for (const int i_point : curves.points_for_curve(i_curve)) { if (!old_values[i_point]) { r_values[i_curve] = false; break; @@ -537,7 +1055,7 @@ static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves, attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { - Array<T> values(curves.curves_size()); + Array<T> values(curves.curves_num()); adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } @@ -557,8 +1075,8 @@ static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves, const VArray<T> &old_values, MutableSpan<T> r_values) { - for (const int i_curve : IndexRange(curves.curves_size())) { - r_values.slice(curves.range_for_curve(i_curve)).fill(old_values[i_curve]); + for (const int i_curve : IndexRange(curves.curves_num())) { + r_values.slice(curves.points_for_curve(i_curve)).fill(old_values[i_curve]); } } @@ -568,16 +1086,16 @@ static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves, GVArray new_varray; attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { using T = decltype(dummy); - Array<T> values(curves.points_size()); + Array<T> values(curves.points_num()); adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); }); return new_varray; } -fn::GVArray CurvesGeometry::adapt_domain(const fn::GVArray &varray, - const AttributeDomain from, - const AttributeDomain to) const +GVArray CurvesGeometry::adapt_domain(const GVArray &varray, + const AttributeDomain from, + const AttributeDomain to) const { if (!varray) { return {}; diff --git a/source/blender/blenkernel/intern/curves_geometry_test.cc b/source/blender/blenkernel/intern/curves_geometry_test.cc index 3a43c0c8102..bc99785de1c 100644 --- a/source/blender/blenkernel/intern/curves_geometry_test.cc +++ b/source/blender/blenkernel/intern/curves_geometry_test.cc @@ -46,7 +46,7 @@ TEST(curves_geometry, Move) CurvesGeometry other = std::move(curves); /* The old curves should be empty, and the offsets are expected to be null. */ - EXPECT_EQ(curves.points_size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(curves.points_num(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(curves.curve_offsets, nullptr); /* NOLINT: bugprone-use-after-move */ /* Just a basic check that the new curves work okay. */ @@ -63,4 +63,324 @@ TEST(curves_geometry, Move) EXPECT_EQ(second_other.offsets().data(), offsets_data); } +TEST(curves_geometry, CatmullRomEvaluation) +{ + CurvesGeometry curves(4, 1); + curves.curve_types().fill(CURVE_TYPE_CATMULL_ROM); + curves.resolution().fill(12); + curves.offsets().last() = 4; + curves.cyclic().fill(false); + + MutableSpan<float3> positions = curves.positions(); + positions[0] = {1, 1, 0}; + positions[1] = {0, 1, 0}; + positions[2] = {0, 0, 0}; + positions[3] = {-1, 0, 0}; + + Span<float3> evaluated_positions = curves.evaluated_positions(); + static const Array<float3> result_1{{ + {1, 1, 0}, + {0.948495, 1.00318, 0}, + {0.87963, 1.01157, 0}, + {0.796875, 1.02344, 0}, + {0.703704, 1.03704, 0}, + {0.603588, 1.05064, 0}, + {0.5, 1.0625, 0}, + {0.396412, 1.07089, 0}, + {0.296296, 1.07407, 0}, + {0.203125, 1.07031, 0}, + {0.12037, 1.05787, 0}, + {0.0515046, 1.03501, 0}, + {0, 1, 0}, + {-0.0318287, 0.948495, 0}, + {-0.0462963, 0.87963, 0}, + {-0.046875, 0.796875, 0}, + {-0.037037, 0.703704, 0}, + {-0.0202546, 0.603588, 0}, + {0, 0.5, 0}, + {0.0202546, 0.396412, 0}, + {0.037037, 0.296296, 0}, + {0.046875, 0.203125, 0}, + {0.0462963, 0.12037, 0}, + {0.0318287, 0.0515046, 0}, + {0, 0, 0}, + {-0.0515046, -0.0350116, 0}, + {-0.12037, -0.0578704, 0}, + {-0.203125, -0.0703125, 0}, + {-0.296296, -0.0740741, 0}, + {-0.396412, -0.0708912, 0}, + {-0.5, -0.0625, 0}, + {-0.603588, -0.0506366, 0}, + {-0.703704, -0.037037, 0}, + {-0.796875, -0.0234375, 0}, + {-0.87963, -0.0115741, 0}, + {-0.948495, -0.00318287, 0}, + {-1, 0, 0}, + }}; + for (const int i : evaluated_positions.index_range()) { + EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f); + } + + /* Changing the positions shouldn't cause the evaluated positions array to be reallocated. */ + curves.tag_positions_changed(); + curves.evaluated_positions(); + EXPECT_EQ(curves.evaluated_positions().data(), evaluated_positions.data()); + + /* Call recalculation (which shouldn't happen because low-level accessors don't tag caches). */ + EXPECT_EQ(evaluated_positions[12].x, 0.0f); + EXPECT_EQ(evaluated_positions[12].y, 1.0f); + + positions[0] = {1, 0, 0}; + positions[1] = {1, 1, 0}; + positions[2] = {0, 1, 0}; + positions[3] = {0, 0, 0}; + curves.cyclic().fill(true); + + /* Tag topology changed because the new cyclic value is different. */ + curves.tag_topology_changed(); + + /* Retrieve the data again since the size should be larger than last time (one more segment). */ + evaluated_positions = curves.evaluated_positions(); + static const Array<float3> result_2{{ + {1, 0, 0}, + {1.03819, 0.0515046, 0}, + {1.06944, 0.12037, 0}, + {1.09375, 0.203125, 0}, + {1.11111, 0.296296, 0}, + {1.12153, 0.396412, 0}, + {1.125, 0.5, 0}, + {1.12153, 0.603588, 0}, + {1.11111, 0.703704, 0}, + {1.09375, 0.796875, 0}, + {1.06944, 0.87963, 0}, + {1.03819, 0.948495, 0}, + {1, 1, 0}, + {0.948495, 1.03819, 0}, + {0.87963, 1.06944, 0}, + {0.796875, 1.09375, 0}, + {0.703704, 1.11111, 0}, + {0.603588, 1.12153, 0}, + {0.5, 1.125, 0}, + {0.396412, 1.12153, 0}, + {0.296296, 1.11111, 0}, + {0.203125, 1.09375, 0}, + {0.12037, 1.06944, 0}, + {0.0515046, 1.03819, 0}, + {0, 1, 0}, + {-0.0381944, 0.948495, 0}, + {-0.0694444, 0.87963, 0}, + {-0.09375, 0.796875, 0}, + {-0.111111, 0.703704, 0}, + {-0.121528, 0.603588, 0}, + {-0.125, 0.5, 0}, + {-0.121528, 0.396412, 0}, + {-0.111111, 0.296296, 0}, + {-0.09375, 0.203125, 0}, + {-0.0694444, 0.12037, 0}, + {-0.0381944, 0.0515046, 0}, + {0, 0, 0}, + {0.0515046, -0.0381944, 0}, + {0.12037, -0.0694444, 0}, + {0.203125, -0.09375, 0}, + {0.296296, -0.111111, 0}, + {0.396412, -0.121528, 0}, + {0.5, -0.125, 0}, + {0.603588, -0.121528, 0}, + {0.703704, -0.111111, 0}, + {0.796875, -0.09375, 0}, + {0.87963, -0.0694444, 0}, + {0.948495, -0.0381944, 0}, + }}; + for (const int i : evaluated_positions.index_range()) { + EXPECT_V3_NEAR(evaluated_positions[i], result_2[i], 1e-5f); + } +} + +TEST(curves_geometry, CatmullRomTwoPointCyclic) +{ + CurvesGeometry curves(2, 1); + curves.curve_types().fill(CURVE_TYPE_CATMULL_ROM); + curves.resolution().fill(12); + curves.offsets().last() = 2; + curves.cyclic().fill(true); + + /* The cyclic value should be ignored when there are only two control points. There should + * be 12 evaluated points for the single segment and an extra for the last point. */ + EXPECT_EQ(curves.evaluated_points_num(), 13); +} + +TEST(curves_geometry, BezierPositionEvaluation) +{ + CurvesGeometry curves(2, 1); + curves.curve_types().fill(CURVE_TYPE_BEZIER); + curves.resolution().fill(12); + curves.offsets().last() = 2; + + MutableSpan<float3> handles_left = curves.handle_positions_left(); + MutableSpan<float3> handles_right = curves.handle_positions_right(); + MutableSpan<float3> positions = curves.positions(); + positions.first() = {-1, 0, 0}; + positions.last() = {1, 0, 0}; + handles_right.first() = {-0.5f, 0.5f, 0.0f}; + handles_left.last() = {0, 0, 0}; + + /* Dangling handles shouldn't be used in a non-cyclic curve. */ + handles_left.first() = {100, 100, 100}; + handles_right.last() = {100, 100, 100}; + + Span<float3> evaluated_positions = curves.evaluated_positions(); + static const Array<float3> result_1{{ + {-1, 0, 0}, + {-0.874711, 0.105035, 0}, + {-0.747685, 0.173611, 0}, + {-0.617188, 0.210937, 0}, + {-0.481481, 0.222222, 0}, + {-0.338831, 0.212674, 0}, + {-0.1875, 0.1875, 0}, + {-0.0257524, 0.15191, 0}, + {0.148148, 0.111111, 0}, + {0.335937, 0.0703125, 0}, + {0.539352, 0.0347222, 0}, + {0.760127, 0.00954859, 0}, + {1, 0, 0}, + }}; + for (const int i : evaluated_positions.index_range()) { + EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f); + } + + curves.resize(4, 2); + curves.curve_types().fill(CURVE_TYPE_BEZIER); + curves.resolution().fill(9); + curves.offsets().last() = 4; + handles_left = curves.handle_positions_left(); + handles_right = curves.handle_positions_right(); + positions = curves.positions(); + positions[2] = {-1, 1, 0}; + positions[3] = {1, 1, 0}; + handles_right[2] = {-0.5f, 1.5f, 0.0f}; + handles_left[3] = {0, 1, 0}; + + /* Dangling handles shouldn't be used in a non-cyclic curve. */ + handles_left[2] = {-100, -100, -100}; + handles_right[3] = {-100, -100, -100}; + + evaluated_positions = curves.evaluated_positions(); + EXPECT_EQ(evaluated_positions.size(), 20); + static const Array<float3> result_2{{ + {-1, 0, 0}, + {-0.832647, 0.131687, 0}, + {-0.66118, 0.201646, 0}, + {-0.481481, 0.222222, 0}, + {-0.289438, 0.205761, 0}, + {-0.0809327, 0.164609, 0}, + {0.148148, 0.111111, 0}, + {0.40192, 0.0576133, 0}, + {0.684499, 0.016461, 0}, + {1, 0, 0}, + {-1, 1, 0}, + {-0.832647, 1.13169, 0}, + {-0.66118, 1.20165, 0}, + {-0.481481, 1.22222, 0}, + {-0.289438, 1.20576, 0}, + {-0.0809327, 1.16461, 0}, + {0.148148, 1.11111, 0}, + {0.40192, 1.05761, 0}, + {0.684499, 1.01646, 0}, + {1, 1, 0}, + }}; + for (const int i : evaluated_positions.index_range()) { + EXPECT_V3_NEAR(evaluated_positions[i], result_2[i], 1e-5f); + } +} + +TEST(curves_geometry, NURBSEvaluation) +{ + CurvesGeometry curves(4, 1); + curves.curve_types().fill(CURVE_TYPE_NURBS); + curves.resolution().fill(10); + curves.offsets().last() = 4; + + MutableSpan<float3> positions = curves.positions(); + positions[0] = {1, 1, 0}; + positions[1] = {0, 1, 0}; + positions[2] = {0, 0, 0}; + positions[3] = {-1, 0, 0}; + + Span<float3> evaluated_positions = curves.evaluated_positions(); + static const Array<float3> result_1{{ + {0.166667, 0.833333, 0}, {0.150006, 0.815511, 0}, {0.134453, 0.796582, 0}, + {0.119924, 0.776627, 0}, {0.106339, 0.75573, 0}, {0.0936146, 0.733972, 0}, + {0.0816693, 0.711434, 0}, {0.0704211, 0.6882, 0}, {0.0597879, 0.66435, 0}, + {0.0496877, 0.639968, 0}, {0.0400385, 0.615134, 0}, {0.0307584, 0.589931, 0}, + {0.0217653, 0.564442, 0}, {0.0129772, 0.538747, 0}, {0.00431208, 0.512929, 0}, + {-0.00431208, 0.487071, 0}, {-0.0129772, 0.461253, 0}, {-0.0217653, 0.435558, 0}, + {-0.0307584, 0.410069, 0}, {-0.0400385, 0.384866, 0}, {-0.0496877, 0.360032, 0}, + {-0.0597878, 0.33565, 0}, {-0.0704211, 0.3118, 0}, {-0.0816693, 0.288566, 0}, + {-0.0936146, 0.266028, 0}, {-0.106339, 0.24427, 0}, {-0.119924, 0.223373, 0}, + {-0.134453, 0.203418, 0}, {-0.150006, 0.184489, 0}, {-0.166667, 0.166667, 0}, + }}; + for (const int i : evaluated_positions.index_range()) { + EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f); + } + + /* Test a cyclic curve. */ + curves.cyclic().fill(true); + curves.tag_topology_changed(); + evaluated_positions = curves.evaluated_positions(); + static const Array<float3> result_2{{ + {0.166667, 0.833333, 0}, {0.121333, 0.778667, 0}, + {0.084, 0.716, 0}, {0.0526667, 0.647333, 0}, + {0.0253333, 0.574667, 0}, {0, 0.5, 0}, + {-0.0253333, 0.425333, 0}, {-0.0526667, 0.352667, 0}, + {-0.084, 0.284, 0}, {-0.121333, 0.221333, 0}, + {-0.166667, 0.166667, 0}, {-0.221, 0.121667, 0}, + {-0.281333, 0.0866667, 0}, {-0.343667, 0.0616666, 0}, + {-0.404, 0.0466667, 0}, {-0.458333, 0.0416667, 0}, + {-0.502667, 0.0466667, 0}, {-0.533, 0.0616666, 0}, + {-0.545333, 0.0866667, 0}, {-0.535667, 0.121667, 0}, + {-0.5, 0.166667, 0}, {-0.436, 0.221334, 0}, + {-0.348, 0.284, 0}, {-0.242, 0.352667, 0}, + {-0.124, 0.425333, 0}, {0, 0.5, 0}, + {0.124, 0.574667, 0}, {0.242, 0.647333, 0}, + {0.348, 0.716, 0}, {0.436, 0.778667, 0}, + {0.5, 0.833333, 0}, {0.535667, 0.878334, 0}, + {0.545333, 0.913333, 0}, {0.533, 0.938333, 0}, + {0.502667, 0.953333, 0}, {0.458333, 0.958333, 0}, + {0.404, 0.953333, 0}, {0.343667, 0.938333, 0}, + {0.281333, 0.913333, 0}, {0.221, 0.878333, 0}, + }}; + for (const int i : evaluated_positions.index_range()) { + EXPECT_V3_NEAR(evaluated_positions[i], result_2[i], 1e-5f); + } + + /* Test a circular cyclic curve with weights. */ + positions[0] = {1, 0, 0}; + positions[1] = {1, 1, 0}; + positions[2] = {0, 1, 0}; + positions[3] = {0, 0, 0}; + curves.nurbs_weights().fill(1.0f); + curves.nurbs_weights()[0] = 4.0f; + curves.tag_positions_changed(); + static const Array<float3> result_3{{ + {0.888889, 0.555556, 0}, {0.837792, 0.643703, 0}, {0.773885, 0.727176, 0}, + {0.698961, 0.800967, 0}, {0.616125, 0.860409, 0}, {0.529412, 0.901961, 0}, + {0.443152, 0.923773, 0}, {0.361289, 0.925835, 0}, {0.286853, 0.909695, 0}, + {0.221722, 0.877894, 0}, {0.166667, 0.833333, 0}, {0.122106, 0.778278, 0}, + {0.0903055, 0.713148, 0}, {0.0741654, 0.638711, 0}, {0.0762274, 0.556847, 0}, + {0.0980392, 0.470588, 0}, {0.139591, 0.383875, 0}, {0.199032, 0.301039, 0}, + {0.272824, 0.226114, 0}, {0.356297, 0.162208, 0}, {0.444444, 0.111111, 0}, + {0.531911, 0.0731388, 0}, {0.612554, 0.0468976, 0}, {0.683378, 0.0301622, 0}, + {0.74391, 0.0207962, 0}, {0.794872, 0.017094, 0}, {0.837411, 0.017839, 0}, + {0.872706, 0.0222583, 0}, {0.901798, 0.0299677, 0}, {0.925515, 0.0409445, 0}, + {0.944444, 0.0555556, 0}, {0.959056, 0.0744855, 0}, {0.970032, 0.0982019, 0}, + {0.977742, 0.127294, 0}, {0.982161, 0.162589, 0}, {0.982906, 0.205128, 0}, + {0.979204, 0.256091, 0}, {0.969838, 0.316622, 0}, {0.953102, 0.387446, 0}, + {0.926861, 0.468089, 0}, + }}; + evaluated_positions = curves.evaluated_positions(); + for (const int i : evaluated_positions.index_range()) { + EXPECT_V3_NEAR(evaluated_positions[i], result_3[i], 1e-5f); + } +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index b348e18a6a8..bf6f05300f8 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -18,9 +18,11 @@ #include "DNA_meshdata_types.h" #include "BLI_bitmap.h" +#include "BLI_color.hh" #include "BLI_endian_switch.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" +#include "BLI_math_vector.hh" #include "BLI_mempool.h" #include "BLI_path_util.h" #include "BLI_string.h" @@ -60,6 +62,10 @@ BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)nullptr)->typemap) == CD_NUMTYPES, " static CLG_LogRef LOG = {"bke.customdata"}; +/* -------------------------------------------------------------------- */ +/** \name Mesh Mask Utilities + * \{ */ + void CustomData_MeshMasks_update(CustomData_MeshMasks *mask_dst, const CustomData_MeshMasks *mask_src) { @@ -80,7 +86,12 @@ bool CustomData_MeshMasks_are_matching(const CustomData_MeshMasks *mask_ref, ((mask_required->lmask & mask_ref->lmask) == mask_required->lmask)); } -/********************* Layer type information **********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Layer Type Information + * \{ */ + struct LayerTypeInfo { int size; /* the memory size of one element of this layer's data */ @@ -162,6 +173,12 @@ struct LayerTypeInfo { int (*layers_max)(); }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MDeformVert, #CD_MDEFORMVERT) + * \{ */ + static void layerCopy_mdeformvert(const void *source, void *dest, int count) { int i, size = sizeof(MDeformVert); @@ -313,6 +330,12 @@ static void layerInterp_mdeformvert(const void **sources, } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#vec3f, #CD_NORMAL) + * \{ */ + static void layerInterp_normal(const void **sources, const float *weights, const float *UNUSED(sub_weights), @@ -369,6 +392,12 @@ static void layerCopyValue_normal(const void *source, } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MTFace, #CD_MTFACE) + * \{ */ + static void layerCopy_tface(const void *source, void *dest, int count) { const MTFace *source_tf = (const MTFace *)source; @@ -434,6 +463,12 @@ static int layerMaxNum_tface() return MAX_MTFACE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MFloatProperty, #CD_PROP_FLOAT) + * \{ */ + static void layerCopy_propFloat(const void *source, void *dest, int count) { memcpy(dest, source, sizeof(MFloatProperty) * count); @@ -471,16 +506,34 @@ static bool layerValidate_propFloat(void *data, const uint totitems, const bool return has_errors; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MIntProperty, #CD_PROP_INT32) + * \{ */ + static void layerCopy_propInt(const void *source, void *dest, int count) { memcpy(dest, source, sizeof(MIntProperty) * count); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MStringProperty, #CD_PROP_STRING) + * \{ */ + static void layerCopy_propString(const void *source, void *dest, int count) { memcpy(dest, source, sizeof(MStringProperty) * count); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#OrigSpaceFace, #CD_ORIGSPACE) + * \{ */ + static void layerCopy_origspace_face(const void *source, void *dest, int count) { const OrigSpaceFace *source_tf = (const OrigSpaceFace *)source; @@ -539,6 +592,12 @@ static void layerDefault_origspace_face(void *data, int count) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MDisps, #CD_MDISPS) + * \{ */ + static void layerSwap_mdisps(void *data, const int *ci) { MDisps *s = static_cast<MDisps *>(data); @@ -651,6 +710,13 @@ static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, int return size; } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (`float`, #CD_PAINT_MASK) + * \{ */ + static void layerInterp_paint_mask(const void **sources, const float *weights, const float *UNUSED(sub_weights), @@ -666,6 +732,12 @@ static void layerInterp_paint_mask(const void **sources, *(float *)dest = mask; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#GridPaintMask, #CD_GRID_PAINT_MASK) + * \{ */ + static void layerCopy_grid_paint_mask(const void *source, void *dest, int count) { const GridPaintMask *s = static_cast<const GridPaintMask *>(source); @@ -1177,6 +1249,12 @@ static void layerInterp_shapekey(const void **sources, copy_v3_v3((float *)dest, co); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MVertSkin, #CD_MVERT_SKIN) + * \{ */ + static void layerDefault_mvert_skin(void *data, int count) { MVertSkin *vs = static_cast<MVertSkin *>(data); @@ -1214,6 +1292,12 @@ static void layerInterp_mvert_skin(const void **sources, vs_dst->flag &= ~MVERT_SKIN_ROOT; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (`short[4][3]`, #CD_TESSLOOPNORMAL) + * \{ */ + static void layerSwap_flnor(void *data, const int *corner_indices) { short(*flnors)[4][3] = static_cast<short(*)[4][3]>(data); @@ -1227,6 +1311,12 @@ static void layerSwap_flnor(void *data, const int *corner_indices) memcpy(flnors, nors, sizeof(nors)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (`int`, #CD_FACEMAP) + * \{ */ + static void layerDefault_fmap(void *data, int count) { int *fmap_num = (int *)data; @@ -1235,6 +1325,12 @@ static void layerDefault_fmap(void *data, int count) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MPropCol, #CD_PROP_COLOR) + * \{ */ + static void layerCopyValue_propcol(const void *source, void *dest, const int mixmode, @@ -1358,6 +1454,12 @@ static int layerMaxNum_propcol() return MAX_MCOL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#vec3f, #CD_PROP_FLOAT3) + * \{ */ + static void layerInterp_propfloat3(const void **sources, const float *weights, const float *UNUSED(sub_weights), @@ -1405,6 +1507,12 @@ static bool layerValidate_propfloat3(void *data, const uint totitems, const bool return has_errors; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#vec2f, #CD_PROP_FLOAT2) + * \{ */ + static void layerInterp_propfloat2(const void **sources, const float *weights, const float *UNUSED(sub_weights), @@ -1450,6 +1558,12 @@ static bool layerValidate_propfloat2(void *data, const uint totitems, const bool return has_errors; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (`bool`, #CD_PROP_BOOL) + * \{ */ + static void layerInterp_propbool(const void **sources, const float *weights, const float *UNUSED(sub_weights), @@ -2051,7 +2165,12 @@ void customData_mask_layers__print(const CustomData_MeshMasks *mask) } } -/********************* CustomData functions *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Functions + * \{ */ + static void customData_update_offsets(CustomData *data); static CustomDataLayer *customData_add_layer__internal(CustomData *data, @@ -4468,14 +4587,18 @@ void CustomData_layers__print(CustomData *data) printf("}\n"); } -/****************************** External Files *******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name External Files + * \{ */ -static void customdata_external_filename(char filename[FILE_MAX], +static void customdata_external_filename(char filepath[FILE_MAX], ID *id, CustomDataExternal *external) { - BLI_strncpy(filename, external->filename, FILE_MAX); - BLI_path_abs(filename, ID_BLEND_PATH_FROM_GLOBAL(id)); + BLI_strncpy(filepath, external->filepath, FILE_MAX); + BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(id)); } void CustomData_external_reload(CustomData *data, ID *UNUSED(id), CustomDataMask mask, int totelem) @@ -4500,7 +4623,7 @@ void CustomData_external_read(CustomData *data, ID *id, CustomDataMask mask, int { CustomDataExternal *external = data->external; CustomDataLayer *layer; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; int update = 0; if (!external) { @@ -4526,12 +4649,12 @@ void CustomData_external_read(CustomData *data, ID *id, CustomDataMask mask, int return; } - customdata_external_filename(filename, id, external); + customdata_external_filename(filepath, id, external); CDataFile *cdf = cdf_create(CDF_TYPE_MESH); - if (!cdf_read_open(cdf, filename)) { + if (!cdf_read_open(cdf, filepath)) { cdf_free(cdf); - CLOG_ERROR(&LOG, "Failed to read %s layer from %s.", layerType_getName(layer->type), filename); + CLOG_ERROR(&LOG, "Failed to read %s layer from %s.", layerType_getName(layer->type), filepath); return; } @@ -4574,7 +4697,7 @@ void CustomData_external_write( { CustomDataExternal *external = data->external; int update = 0; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; if (!external) { return; @@ -4599,7 +4722,7 @@ void CustomData_external_write( /* make sure data is read before we try to write */ CustomData_external_read(data, id, mask, totelem); - customdata_external_filename(filename, id, external); + customdata_external_filename(filepath, id, external); CDataFile *cdf = cdf_create(CDF_TYPE_MESH); @@ -4619,8 +4742,8 @@ void CustomData_external_write( } } - if (!cdf_write_open(cdf, filename)) { - CLOG_ERROR(&LOG, "Failed to open %s for writing.", filename); + if (!cdf_write_open(cdf, filepath)) { + CLOG_ERROR(&LOG, "Failed to open %s for writing.", filepath); cdf_free(cdf); return; } @@ -4648,7 +4771,7 @@ void CustomData_external_write( } if (i != data->totlayer) { - CLOG_ERROR(&LOG, "Failed to write data to %s.", filename); + CLOG_ERROR(&LOG, "Failed to write data to %s.", filepath); cdf_write_close(cdf); cdf_free(cdf); return; @@ -4673,7 +4796,7 @@ void CustomData_external_write( } void CustomData_external_add( - CustomData *data, ID *UNUSED(id), int type, int UNUSED(totelem), const char *filename) + CustomData *data, ID *UNUSED(id), int type, int UNUSED(totelem), const char *filepath) { CustomDataExternal *external = data->external; @@ -4692,7 +4815,7 @@ void CustomData_external_add( external = MEM_cnew<CustomDataExternal>(__func__); data->external = external; } - BLI_strncpy(external->filename, filename, sizeof(external->filename)); + BLI_strncpy(external->filepath, filepath, sizeof(external->filepath)); layer->flag |= CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY; } @@ -4732,7 +4855,12 @@ bool CustomData_external_test(CustomData *data, int type) return (layer->flag & CD_FLAG_EXTERNAL) != 0; } -/* ********** Mesh-to-mesh data transfer ********** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh-to-Mesh Data Transfer + * \{ */ + static void copy_bit_flag(void *dst, const void *src, const size_t data_size, const uint64_t flag) { #define COPY_BIT_FLAG(_type, _dst, _src, _f) \ @@ -5011,6 +5139,12 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, MEM_SAFE_FREE(tmp_data_src); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data IO + * \{ */ + static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external) { if (mdlist) { @@ -5205,6 +5339,12 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count) CustomData_update_typemap(data); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data Debugging + * \{ */ + #ifndef NDEBUG void CustomData_debug_info_from_layers(const CustomData *data, const char *indent, DynStr *dynstr) @@ -5234,3 +5374,66 @@ void CustomData_debug_info_from_layers(const CustomData *data, const char *inden } #endif /* NDEBUG */ + +/** \} */ + +namespace blender::bke { + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data C++ API + * \{ */ + +const blender::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return &CPPType::get<float>(); + case CD_PROP_FLOAT2: + return &CPPType::get<float2>(); + case CD_PROP_FLOAT3: + return &CPPType::get<float3>(); + case CD_PROP_INT32: + return &CPPType::get<int>(); + case CD_PROP_COLOR: + return &CPPType::get<ColorGeometry4f>(); + case CD_PROP_BOOL: + return &CPPType::get<bool>(); + case CD_PROP_INT8: + return &CPPType::get<int8_t>(); + default: + return nullptr; + } + return nullptr; +} + +CustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type) +{ + if (type.is<float>()) { + return CD_PROP_FLOAT; + } + if (type.is<float2>()) { + return CD_PROP_FLOAT2; + } + if (type.is<float3>()) { + return CD_PROP_FLOAT3; + } + if (type.is<int>()) { + return CD_PROP_INT32; + } + if (type.is<ColorGeometry4f>()) { + return CD_PROP_COLOR; + } + if (type.is<bool>()) { + return CD_PROP_BOOL; + } + if (type.is<int8_t>()) { + return CD_PROP_INT8; + } + return static_cast<CustomDataType>(-1); +} + +/** \} */ + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index ce07e501897..0f5814c0a23 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -44,6 +44,7 @@ #include "BKE_dynamicpaint.h" #include "BKE_effect.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" @@ -3265,7 +3266,7 @@ static void dynamic_paint_output_surface_image_wetmap_cb( } void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, - char *filename, + const char *filepath, short output_layer) { ImBuf *ibuf = NULL; @@ -3285,7 +3286,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, format = R_IMF_IMTYPE_PNG; } #endif - BLI_strncpy(output_file, filename, sizeof(output_file)); + BLI_strncpy(output_file, filepath, sizeof(output_file)); BKE_image_path_ensure_ext_from_imtype(output_file, format); /* Validate output file path */ @@ -5244,7 +5245,6 @@ static void dynamic_paint_effect_shrink_cb(void *__restrict userdata, PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; const PaintPoint *prevPoint = data->prevPoint; const float eff_scale = data->eff_scale; - float totalAlpha = 0.0f; const int *n_index = sData->adj_data->n_index; const int *n_target = sData->adj_data->n_target; @@ -5257,8 +5257,6 @@ static void dynamic_paint_effect_shrink_cb(void *__restrict userdata, const PaintPoint *pPoint_prev = &prevPoint[n_target[n_idx]]; float a_factor, ea_factor, w_factor; - totalAlpha += pPoint_prev->e_color[3]; - /* Check if neighboring point has lower alpha, * if so, decrease this point's alpha as well. */ if (pPoint->color[3] <= 0.0f && pPoint->e_color[3] <= 0.0f && pPoint->wetness <= 0.0f) { diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index ca9d758c692..81e73b6cf2c 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1754,7 +1754,7 @@ static void update_distances(int index, /* Count ray mesh misses (i.e. no face hit) and cases where the ray direction matches the face * normal direction. From this information it can be derived whether a cell is inside or * outside the mesh. */ - int miss_cnt = 0, dir_cnt = 0; + int miss_count = 0, dir_count = 0; for (int i = 0; i < ARRAY_SIZE(ray_dirs); i++) { BVHTreeRayHit hit_tree = {0}; @@ -1773,14 +1773,14 @@ static void update_distances(int index, /* Ray did not hit mesh. * Current point definitely not inside mesh. Inside mesh as all rays have to hit. */ if (hit_tree.index == -1) { - miss_cnt++; + miss_count++; /* Skip this ray since nothing was hit. */ continue; } /* Ray and normal are pointing in opposite directions. */ if (dot_v3v3(ray_dirs[i], hit_tree.no) <= 0) { - dir_cnt++; + dir_count++; } if (hit_tree.dist < min_dist) { @@ -1790,7 +1790,7 @@ static void update_distances(int index, /* Point lies inside mesh. Use negative sign for distance value. * This "if statement" has 2 conditions that can be true for points outside mesh. */ - if (!(miss_cnt > 0 || dir_cnt == ARRAY_SIZE(ray_dirs))) { + if (!(miss_count > 0 || dir_count == ARRAY_SIZE(ray_dirs))) { min_dist = (-1.0f) * fabsf(min_dist); } diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 0926d65b306..f409389e463 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -14,10 +14,10 @@ #include "attribute_access_intern.hh" -using blender::fn::GMutableSpan; -using blender::fn::GSpan; -using blender::fn::GVArray; -using blender::fn::GVArray_GSpan; +using blender::GMutableSpan; +using blender::GSpan; +using blender::GVArray; +using blender::GVArray_GSpan; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index 86cbea9a9bb..27689d70c77 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -15,7 +15,7 @@ #include "attribute_access_intern.hh" -using blender::fn::GVArray; +using blender::GVArray; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -236,10 +236,10 @@ int CurveComponent::attribute_domain_size(const AttributeDomain domain) const const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( curves_->geometry); if (domain == ATTR_DOMAIN_POINT) { - return geometry.points_size(); + return geometry.points_num(); } if (domain == ATTR_DOMAIN_CURVE) { - return geometry.curves_size(); + return geometry.curves_num(); } return 0; } diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 0cb2b0e812b..0dc6f486d28 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -20,18 +20,18 @@ #include "attribute_access_intern.hh" -#include "FN_cpp_type_make.hh" +#include "BLI_cpp_type_make.hh" using blender::float4x4; +using blender::GSpan; using blender::IndexMask; using blender::Map; using blender::MutableSpan; using blender::Set; using blender::Span; using blender::VectorSet; -using blender::fn::GSpan; -MAKE_CPP_TYPE(InstanceReference, InstanceReference, CPPTypeFlags::None) +BLI_CPP_TYPE_MAKE(InstanceReference, InstanceReference, CPPTypeFlags::None) /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -164,7 +164,7 @@ void InstancesComponent::remove_instances(const IndexMask mask) GSpan src = *src_attributes.get_for_read(id); dst_attributes.create(id, meta_data.data_type); - fn::GMutableSpan dst = *dst_attributes.get_for_write(id); + GMutableSpan dst = *dst_attributes.get_for_write(id); attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index cbd7ec9155a..2bfe984462c 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -260,7 +260,7 @@ static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { using T = decltype(dummy); new_varray = VArray<T>::ForFunc(mesh.totloop, - [mesh, varray = varray.typed<T>()](const int64_t loop_index) { + [&mesh, varray = varray.typed<T>()](const int64_t loop_index) { const int vertex_index = mesh.mloop[loop_index].v; return varray[vertex_index]; }); @@ -276,7 +276,7 @@ static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { if constexpr (std::is_same_v<T, bool>) { new_varray = VArray<T>::ForFunc( - mesh.totpoly, [mesh, varray = varray.typed<bool>()](const int face_index) { + mesh.totpoly, [&mesh, varray = varray.typed<bool>()](const int face_index) { /* A face is selected if all of its corners were selected. */ const MPoly &poly = mesh.mpoly[face_index]; for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { @@ -289,7 +289,7 @@ static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray } else { new_varray = VArray<T>::ForFunc( - mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) { + mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) { T return_value; attribute_math::DefaultMixer<T> mixer({&return_value, 1}); const MPoly &poly = mesh.mpoly[face_index]; @@ -530,7 +530,7 @@ static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray & if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { if constexpr (std::is_same_v<T, bool>) { new_varray = VArray<T>::ForFunc( - mesh.totpoly, [mesh, varray = varray.typed<bool>()](const int face_index) { + mesh.totpoly, [&mesh, varray = varray.typed<bool>()](const int face_index) { /* A face is selected if all of its vertices were selected. */ const MPoly &poly = mesh.mpoly[face_index]; for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { @@ -544,7 +544,7 @@ static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray & } else { new_varray = VArray<T>::ForFunc( - mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) { + mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) { T return_value; attribute_math::DefaultMixer<T> mixer({&return_value, 1}); const MPoly &poly = mesh.mpoly[face_index]; @@ -571,14 +571,14 @@ static GVArray adapt_mesh_domain_point_to_edge(const Mesh &mesh, const GVArray & if constexpr (std::is_same_v<T, bool>) { /* An edge is selected if both of its vertices were selected. */ new_varray = VArray<bool>::ForFunc( - mesh.totedge, [mesh, varray = varray.typed<bool>()](const int edge_index) { + mesh.totedge, [&mesh, varray = varray.typed<bool>()](const int edge_index) { const MEdge &edge = mesh.medge[edge_index]; return varray[edge.v1] && varray[edge.v2]; }); } else { new_varray = VArray<T>::ForFunc( - mesh.totedge, [mesh, varray = varray.typed<T>()](const int edge_index) { + mesh.totedge, [&mesh, varray = varray.typed<T>()](const int edge_index) { T return_value; attribute_math::DefaultMixer<T> mixer({&return_value, 1}); const MEdge &edge = mesh.medge[edge_index]; @@ -713,7 +713,7 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v if constexpr (std::is_same_v<T, bool>) { /* A face is selected if all of its edges are selected. */ new_varray = VArray<bool>::ForFunc( - mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) { + mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) { const MPoly &poly = mesh.mpoly[face_index]; for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { const MLoop &loop = mesh.mloop[loop_index]; @@ -726,7 +726,7 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v } else { new_varray = VArray<T>::ForFunc( - mesh.totpoly, [mesh, varray = varray.typed<T>()](const int face_index) { + mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) { T return_value; attribute_math::DefaultMixer<T> mixer({&return_value, 1}); const MPoly &poly = mesh.mpoly[face_index]; @@ -746,8 +746,8 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v } // namespace blender::bke -blender::fn::GVArray MeshComponent::attribute_try_adapt_domain_impl( - const blender::fn::GVArray &varray, +blender::GVArray MeshComponent::attribute_try_adapt_domain_impl( + const blender::GVArray &varray, const AttributeDomain from_domain, const AttributeDomain to_domain) const { @@ -944,20 +944,71 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> { if (dverts_ == nullptr) { return 0.0f; } - const MDeformVert &dvert = dverts_[index]; - for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) { - if (weight.def_nr == dvert_index_) { - return weight.weight; - } + if (const MDeformWeight *weight = this->find_weight_at_index(index)) { + return weight->weight; } return 0.0f; - ; } void set(const int64_t index, const float value) override { - MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_); - weight->weight = value; + MDeformVert &dvert = dverts_[index]; + if (value == 0.0f) { + if (MDeformWeight *weight = this->find_weight_at_index(index)) { + weight->weight = 0.0f; + } + } + else { + MDeformWeight *weight = BKE_defvert_ensure_index(&dvert, dvert_index_); + weight->weight = value; + } + } + + void set_all(Span<float> src) override + { + for (const int64_t index : src.index_range()) { + this->set(index, src[index]); + } + } + + void materialize(IndexMask mask, MutableSpan<float> r_span) const override + { + if (dverts_ == nullptr) { + return r_span.fill_indices(mask, 0.0f); + } + for (const int64_t index : mask) { + if (const MDeformWeight *weight = this->find_weight_at_index(index)) { + r_span[index] = weight->weight; + } + else { + r_span[index] = 0.0f; + } + } + } + + void materialize_to_uninitialized(IndexMask mask, MutableSpan<float> r_span) const override + { + this->materialize(mask, r_span); + } + + private: + MDeformWeight *find_weight_at_index(const int64_t index) + { + for (MDeformWeight &weight : MutableSpan(dverts_[index].dw, dverts_[index].totweight)) { + if (weight.def_nr == dvert_index_) { + return &weight; + } + } + return nullptr; + } + const MDeformWeight *find_weight_at_index(const int64_t index) const + { + for (const MDeformWeight &weight : Span(dverts_[index].dw, dverts_[index].totweight)) { + if (weight.def_nr == dvert_index_) { + return &weight; + } + } + return nullptr; } }; diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index ff08b596770..44ffd64e475 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -7,7 +7,6 @@ #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_pointcloud.h" -#include "BKE_spline.hh" #include "DNA_collection_types.h" #include "DNA_layer_types.h" diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index e4ed2a40f10..a5eff1f9d5a 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -451,6 +451,10 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, } /* TODO: Implement feature point preservation. */ int count = stroke_march_count(gps, dist, sharp_threshold); + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; + if (is_cyclic) { + count--; + } bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled"); @@ -499,6 +503,9 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, &ratio_result, &index_from, &index_to)) > -1) { + if (is_cyclic && next_point_index == 0) { + break; /* last point finished */ + } pt2 = &new_pt[i]; copy_v3_v3(&pt2->x, last_coord); new_pt[i].pressure = pressure; @@ -533,10 +540,9 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, gps->dvert = new_dv; } + BLI_assert(i == count); gps->totpoints = i; - gps->flag &= (~GP_STROKE_CYCLIC); - /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gpd, gps); diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 4a1cbdba42a..b65f3416f0f 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -9,6 +9,7 @@ #include <cmath> #include <cstdio> #include <cstring> +#include <ctime> #include <fcntl.h> #ifndef WIN32 # include <unistd.h> @@ -16,7 +17,8 @@ # include <io.h> #endif -#include <ctime> +#include <regex> +#include <string> #include "BLI_array.hh" @@ -29,10 +31,7 @@ #include "IMB_imbuf_types.h" #include "IMB_metadata.h" #include "IMB_moviecache.h" - -#ifdef WITH_OPENEXR -# include "intern/openexr/openexr_multi.h" -#endif +#include "IMB_openexr.h" /* Allow using deprecated functionality for .blend file I/O. */ #define DNA_DEPRECATED_ALLOW @@ -68,6 +67,7 @@ #include "BKE_icons.h" #include "BKE_idtype.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" @@ -239,7 +239,7 @@ static void image_foreach_cache(ID *id, auto gputexture_offset = [image](int target, int eye, int resolution) { constexpr size_t base_offset = offsetof(Image, gputexture); - const auto first = &image->gputexture[0][0][0]; + struct GPUTexture **first = &image->gputexture[0][0][0]; const size_t array_offset = sizeof(*first) * (&image->gputexture[target][eye][resolution] - first); return base_offset + array_offset; @@ -489,9 +489,9 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat /** \name Image Cache * \{ */ -typedef struct ImageCacheKey { +struct ImageCacheKey { int index; -} ImageCacheKey; +}; static unsigned int imagecache_hashhash(const void *key_v) { @@ -1490,607 +1490,6 @@ void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) /** \name Read and Write * \{ */ -int BKE_image_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options) -{ - memset(r_options, 0, sizeof(*r_options)); - - if (imtype == R_IMF_IMTYPE_TARGA) { - return IMB_FTYPE_TGA; - } - if (imtype == R_IMF_IMTYPE_RAWTGA) { - r_options->flag = RAWTGA; - return IMB_FTYPE_TGA; - } - if (imtype == R_IMF_IMTYPE_IRIS) { - return IMB_FTYPE_IMAGIC; - } -#ifdef WITH_HDR - if (imtype == R_IMF_IMTYPE_RADHDR) { - return IMB_FTYPE_RADHDR; - } -#endif - if (imtype == R_IMF_IMTYPE_PNG) { - r_options->quality = 15; - return IMB_FTYPE_PNG; - } -#ifdef WITH_DDS - if (imtype == R_IMF_IMTYPE_DDS) { - return IMB_FTYPE_DDS; - } -#endif - if (imtype == R_IMF_IMTYPE_BMP) { - return IMB_FTYPE_BMP; - } -#ifdef WITH_TIFF - if (imtype == R_IMF_IMTYPE_TIFF) { - return IMB_FTYPE_TIF; - } -#endif - if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { - return IMB_FTYPE_OPENEXR; - } -#ifdef WITH_CINEON - if (imtype == R_IMF_IMTYPE_CINEON) { - return IMB_FTYPE_CINEON; - } - if (imtype == R_IMF_IMTYPE_DPX) { - return IMB_FTYPE_DPX; - } -#endif -#ifdef WITH_OPENJPEG - if (imtype == R_IMF_IMTYPE_JP2) { - r_options->flag |= JP2_JP2; - r_options->quality = 90; - return IMB_FTYPE_JP2; - } -#endif - - r_options->quality = 90; - return IMB_FTYPE_JPG; -} - -char BKE_image_ftype_to_imtype(const int ftype, const ImbFormatOptions *options) -{ - if (ftype == IMB_FTYPE_NONE) { - return R_IMF_IMTYPE_TARGA; - } - if (ftype == IMB_FTYPE_IMAGIC) { - return R_IMF_IMTYPE_IRIS; - } -#ifdef WITH_HDR - if (ftype == IMB_FTYPE_RADHDR) { - return R_IMF_IMTYPE_RADHDR; - } -#endif - if (ftype == IMB_FTYPE_PNG) { - return R_IMF_IMTYPE_PNG; - } -#ifdef WITH_DDS - if (ftype == IMB_FTYPE_DDS) { - return R_IMF_IMTYPE_DDS; - } -#endif - if (ftype == IMB_FTYPE_BMP) { - return R_IMF_IMTYPE_BMP; - } -#ifdef WITH_TIFF - if (ftype == IMB_FTYPE_TIF) { - return R_IMF_IMTYPE_TIFF; - } -#endif - if (ftype == IMB_FTYPE_OPENEXR) { - return R_IMF_IMTYPE_OPENEXR; - } -#ifdef WITH_CINEON - if (ftype == IMB_FTYPE_CINEON) { - return R_IMF_IMTYPE_CINEON; - } - if (ftype == IMB_FTYPE_DPX) { - return R_IMF_IMTYPE_DPX; - } -#endif - if (ftype == IMB_FTYPE_TGA) { - if (options && (options->flag & RAWTGA)) { - return R_IMF_IMTYPE_RAWTGA; - } - - return R_IMF_IMTYPE_TARGA; - } -#ifdef WITH_OPENJPEG - if (ftype == IMB_FTYPE_JP2) { - return R_IMF_IMTYPE_JP2; - } -#endif - - return R_IMF_IMTYPE_JPEG90; -} - -bool BKE_imtype_is_movie(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_AVIRAW: - case R_IMF_IMTYPE_AVIJPEG: - case R_IMF_IMTYPE_FFMPEG: - case R_IMF_IMTYPE_H264: - case R_IMF_IMTYPE_THEORA: - case R_IMF_IMTYPE_XVID: - return true; - } - return false; -} - -bool BKE_imtype_supports_zbuf(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_IRIZ: - case R_IMF_IMTYPE_OPENEXR: /* But not #R_IMF_IMTYPE_MULTILAYER. */ - return true; - } - return false; -} - -bool BKE_imtype_supports_compress(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_PNG: - return true; - } - return false; -} - -bool BKE_imtype_supports_quality(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_JPEG90: - case R_IMF_IMTYPE_JP2: - case R_IMF_IMTYPE_AVIJPEG: - return true; - } - return false; -} - -bool BKE_imtype_requires_linear_float(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_CINEON: - case R_IMF_IMTYPE_DPX: - case R_IMF_IMTYPE_RADHDR: - case R_IMF_IMTYPE_OPENEXR: - case R_IMF_IMTYPE_MULTILAYER: - return true; - } - return false; -} - -char BKE_imtype_valid_channels(const char imtype, bool write_file) -{ - char chan_flag = IMA_CHAN_FLAG_RGB; /* Assume all support RGB. */ - - /* Alpha. */ - switch (imtype) { - case R_IMF_IMTYPE_BMP: - if (write_file) { - break; - } - ATTR_FALLTHROUGH; - case R_IMF_IMTYPE_TARGA: - case R_IMF_IMTYPE_RAWTGA: - case R_IMF_IMTYPE_IRIS: - case R_IMF_IMTYPE_PNG: - case R_IMF_IMTYPE_TIFF: - case R_IMF_IMTYPE_OPENEXR: - case R_IMF_IMTYPE_MULTILAYER: - case R_IMF_IMTYPE_DDS: - case R_IMF_IMTYPE_JP2: - case R_IMF_IMTYPE_DPX: - chan_flag |= IMA_CHAN_FLAG_ALPHA; - break; - } - - /* BW. */ - switch (imtype) { - case R_IMF_IMTYPE_BMP: - case R_IMF_IMTYPE_PNG: - case R_IMF_IMTYPE_JPEG90: - case R_IMF_IMTYPE_TARGA: - case R_IMF_IMTYPE_RAWTGA: - case R_IMF_IMTYPE_TIFF: - case R_IMF_IMTYPE_IRIS: - chan_flag |= IMA_CHAN_FLAG_BW; - break; - } - - return chan_flag; -} - -char BKE_imtype_valid_depths(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_RADHDR: - return R_IMF_CHAN_DEPTH_32; - case R_IMF_IMTYPE_TIFF: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; - case R_IMF_IMTYPE_OPENEXR: - return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; - case R_IMF_IMTYPE_MULTILAYER: - return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; - /* NOTE: CINEON uses an unusual 10bits-LOG per channel. */ - case R_IMF_IMTYPE_DPX: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_10 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; - case R_IMF_IMTYPE_CINEON: - return R_IMF_CHAN_DEPTH_10; - case R_IMF_IMTYPE_JP2: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; - case R_IMF_IMTYPE_PNG: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; - /* Most formats are 8bit only. */ - default: - return R_IMF_CHAN_DEPTH_8; - } -} - -char BKE_imtype_from_arg(const char *imtype_arg) -{ - if (STREQ(imtype_arg, "TGA")) { - return R_IMF_IMTYPE_TARGA; - } - if (STREQ(imtype_arg, "IRIS")) { - return R_IMF_IMTYPE_IRIS; - } -#ifdef WITH_DDS - if (STREQ(imtype_arg, "DDS")) { - return R_IMF_IMTYPE_DDS; - } -#endif - if (STREQ(imtype_arg, "JPEG")) { - return R_IMF_IMTYPE_JPEG90; - } - if (STREQ(imtype_arg, "IRIZ")) { - return R_IMF_IMTYPE_IRIZ; - } - if (STREQ(imtype_arg, "RAWTGA")) { - return R_IMF_IMTYPE_RAWTGA; - } - if (STREQ(imtype_arg, "AVIRAW")) { - return R_IMF_IMTYPE_AVIRAW; - } - if (STREQ(imtype_arg, "AVIJPEG")) { - return R_IMF_IMTYPE_AVIJPEG; - } - if (STREQ(imtype_arg, "PNG")) { - return R_IMF_IMTYPE_PNG; - } - if (STREQ(imtype_arg, "BMP")) { - return R_IMF_IMTYPE_BMP; - } -#ifdef WITH_HDR - if (STREQ(imtype_arg, "HDR")) { - return R_IMF_IMTYPE_RADHDR; - } -#endif -#ifdef WITH_TIFF - if (STREQ(imtype_arg, "TIFF")) { - return R_IMF_IMTYPE_TIFF; - } -#endif -#ifdef WITH_OPENEXR - if (STREQ(imtype_arg, "OPEN_EXR")) { - return R_IMF_IMTYPE_OPENEXR; - } - if (STREQ(imtype_arg, "OPEN_EXR_MULTILAYER")) { - return R_IMF_IMTYPE_MULTILAYER; - } - if (STREQ(imtype_arg, "EXR")) { - return R_IMF_IMTYPE_OPENEXR; - } - if (STREQ(imtype_arg, "MULTILAYER")) { - return R_IMF_IMTYPE_MULTILAYER; - } -#endif - if (STREQ(imtype_arg, "FFMPEG")) { - return R_IMF_IMTYPE_FFMPEG; - } -#ifdef WITH_CINEON - if (STREQ(imtype_arg, "CINEON")) { - return R_IMF_IMTYPE_CINEON; - } - if (STREQ(imtype_arg, "DPX")) { - return R_IMF_IMTYPE_DPX; - } -#endif -#ifdef WITH_OPENJPEG - if (STREQ(imtype_arg, "JP2")) { - return R_IMF_IMTYPE_JP2; - } -#endif - - return R_IMF_IMTYPE_INVALID; -} - -static bool do_add_image_extension(char *string, - const char imtype, - const ImageFormatData *im_format) -{ - const char *extension = nullptr; - const char *extension_test; - (void)im_format; /* may be unused, depends on build options */ - - if (imtype == R_IMF_IMTYPE_IRIS) { - if (!BLI_path_extension_check(string, extension_test = ".rgb")) { - extension = extension_test; - } - } - else if (imtype == R_IMF_IMTYPE_IRIZ) { - if (!BLI_path_extension_check(string, extension_test = ".rgb")) { - extension = extension_test; - } - } -#ifdef WITH_HDR - else if (imtype == R_IMF_IMTYPE_RADHDR) { - if (!BLI_path_extension_check(string, extension_test = ".hdr")) { - extension = extension_test; - } - } -#endif - else if (ELEM(imtype, - R_IMF_IMTYPE_PNG, - R_IMF_IMTYPE_FFMPEG, - R_IMF_IMTYPE_H264, - R_IMF_IMTYPE_THEORA, - R_IMF_IMTYPE_XVID)) { - if (!BLI_path_extension_check(string, extension_test = ".png")) { - extension = extension_test; - } - } -#ifdef WITH_DDS - else if (imtype == R_IMF_IMTYPE_DDS) { - if (!BLI_path_extension_check(string, extension_test = ".dds")) { - extension = extension_test; - } - } -#endif - else if (ELEM(imtype, R_IMF_IMTYPE_TARGA, R_IMF_IMTYPE_RAWTGA)) { - if (!BLI_path_extension_check(string, extension_test = ".tga")) { - extension = extension_test; - } - } - else if (imtype == R_IMF_IMTYPE_BMP) { - if (!BLI_path_extension_check(string, extension_test = ".bmp")) { - extension = extension_test; - } - } -#ifdef WITH_TIFF - else if (imtype == R_IMF_IMTYPE_TIFF) { - if (!BLI_path_extension_check_n(string, extension_test = ".tif", ".tiff", nullptr)) { - extension = extension_test; - } - } -#endif -#ifdef WITH_OPENIMAGEIO - else if (imtype == R_IMF_IMTYPE_PSD) { - if (!BLI_path_extension_check(string, extension_test = ".psd")) { - extension = extension_test; - } - } -#endif -#ifdef WITH_OPENEXR - else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { - if (!BLI_path_extension_check(string, extension_test = ".exr")) { - extension = extension_test; - } - } -#endif -#ifdef WITH_CINEON - else if (imtype == R_IMF_IMTYPE_CINEON) { - if (!BLI_path_extension_check(string, extension_test = ".cin")) { - extension = extension_test; - } - } - else if (imtype == R_IMF_IMTYPE_DPX) { - if (!BLI_path_extension_check(string, extension_test = ".dpx")) { - extension = extension_test; - } - } -#endif -#ifdef WITH_OPENJPEG - else if (imtype == R_IMF_IMTYPE_JP2) { - if (im_format) { - if (im_format->jp2_codec == R_IMF_JP2_CODEC_JP2) { - if (!BLI_path_extension_check(string, extension_test = ".jp2")) { - extension = extension_test; - } - } - else if (im_format->jp2_codec == R_IMF_JP2_CODEC_J2K) { - if (!BLI_path_extension_check(string, extension_test = ".j2c")) { - extension = extension_test; - } - } - else { - BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); - } - } - else { - if (!BLI_path_extension_check(string, extension_test = ".jp2")) { - extension = extension_test; - } - } - } -#endif - else { // R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG, R_IMF_IMTYPE_JPEG90 etc - if (!(BLI_path_extension_check_n(string, extension_test = ".jpg", ".jpeg", nullptr))) { - extension = extension_test; - } - } - - if (extension) { - /* prefer this in many cases to avoid .png.tga, but in certain cases it breaks */ - /* remove any other known image extension */ - if (BLI_path_extension_check_array(string, imb_ext_image)) { - return BLI_path_extension_replace(string, FILE_MAX, extension); - } - - return BLI_path_extension_ensure(string, FILE_MAX, extension); - } - - return false; -} - -int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) -{ - return do_add_image_extension(string, im_format->imtype, im_format); -} - -int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) -{ - return do_add_image_extension(string, imtype, nullptr); -} - -void BKE_imformat_defaults(ImageFormatData *im_format) -{ - memset(im_format, 0, sizeof(*im_format)); - im_format->planes = R_IMF_PLANES_RGBA; - im_format->imtype = R_IMF_IMTYPE_PNG; - im_format->depth = R_IMF_CHAN_DEPTH_8; - im_format->quality = 90; - im_format->compress = 15; - - BKE_color_managed_display_settings_init(&im_format->display_settings); - BKE_color_managed_view_settings_init_default(&im_format->view_settings, - &im_format->display_settings); -} - -void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const ImBuf *imbuf) -{ - int ftype = imbuf->ftype; - int custom_flags = imbuf->foptions.flag; - char quality = imbuf->foptions.quality; - - BKE_imformat_defaults(im_format); - - /* file type */ - - if (ftype == IMB_FTYPE_IMAGIC) { - im_format->imtype = R_IMF_IMTYPE_IRIS; - } -#ifdef WITH_HDR - else if (ftype == IMB_FTYPE_RADHDR) { - im_format->imtype = R_IMF_IMTYPE_RADHDR; - } -#endif - else if (ftype == IMB_FTYPE_PNG) { - im_format->imtype = R_IMF_IMTYPE_PNG; - - if (custom_flags & PNG_16BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - - im_format->compress = quality; - } - -#ifdef WITH_DDS - else if (ftype == IMB_FTYPE_DDS) { - im_format->imtype = R_IMF_IMTYPE_DDS; - } -#endif - else if (ftype == IMB_FTYPE_BMP) { - im_format->imtype = R_IMF_IMTYPE_BMP; - } -#ifdef WITH_TIFF - else if (ftype == IMB_FTYPE_TIF) { - im_format->imtype = R_IMF_IMTYPE_TIFF; - if (custom_flags & TIF_16BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - if (custom_flags & TIF_COMPRESS_NONE) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_NONE; - } - if (custom_flags & TIF_COMPRESS_DEFLATE) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_DEFLATE; - } - if (custom_flags & TIF_COMPRESS_LZW) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_LZW; - } - if (custom_flags & TIF_COMPRESS_PACKBITS) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_PACKBITS; - } - } -#endif - -#ifdef WITH_OPENEXR - else if (ftype == IMB_FTYPE_OPENEXR) { - im_format->imtype = R_IMF_IMTYPE_OPENEXR; - if (custom_flags & OPENEXR_HALF) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - if (custom_flags & OPENEXR_COMPRESS) { - im_format->exr_codec = R_IMF_EXR_CODEC_ZIP; /* Can't determine compression */ - } - if (imbuf->zbuf_float) { - im_format->flag |= R_IMF_FLAG_ZBUF; - } - } -#endif - -#ifdef WITH_CINEON - else if (ftype == IMB_FTYPE_CINEON) { - im_format->imtype = R_IMF_IMTYPE_CINEON; - } - else if (ftype == IMB_FTYPE_DPX) { - im_format->imtype = R_IMF_IMTYPE_DPX; - } -#endif - else if (ftype == IMB_FTYPE_TGA) { - if (custom_flags & RAWTGA) { - im_format->imtype = R_IMF_IMTYPE_RAWTGA; - } - else { - im_format->imtype = R_IMF_IMTYPE_TARGA; - } - } -#ifdef WITH_OPENJPEG - else if (ftype == IMB_FTYPE_JP2) { - im_format->imtype = R_IMF_IMTYPE_JP2; - im_format->quality = quality; - - if (custom_flags & JP2_16BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - else if (custom_flags & JP2_12BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_12; - } - - if (custom_flags & JP2_YCC) { - im_format->jp2_flag |= R_IMF_JP2_FLAG_YCC; - } - - if (custom_flags & JP2_CINE) { - im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_PRESET; - if (custom_flags & JP2_CINE_48FPS) { - im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_48; - } - } - - if (custom_flags & JP2_JP2) { - im_format->jp2_codec = R_IMF_JP2_CODEC_JP2; - } - else if (custom_flags & JP2_J2K) { - im_format->jp2_codec = R_IMF_JP2_CODEC_J2K; - } - else { - BLI_assert_msg(0, "Unsupported jp2 codec was specified in file type"); - } - } -#endif - - else { - im_format->imtype = R_IMF_IMTYPE_JPEG90; - im_format->quality = quality; - } - - /* planes */ - im_format->planes = imbuf->planes; -} - #define STAMP_NAME_SIZE ((MAX_ID_NAME - 2) + 16) /* could allow access externally - 512 is for long names, * STAMP_NAME_SIZE is for id names, allowing them some room for description */ @@ -2943,9 +2342,9 @@ static void metadata_get_field(void *data, const char *propname, char *propvalue IMB_metadata_get_field(imbuf->metadata, propname, propvalue, len); } -void BKE_imbuf_stamp_info(RenderResult *rr, struct ImBuf *ibuf) +void BKE_imbuf_stamp_info(const RenderResult *rr, ImBuf *ibuf) { - struct StampData *stamp_data = rr->stamp_data; + StampData *stamp_data = const_cast<StampData *>(rr->stamp_data); IMB_metadata_ensure(&ibuf->metadata); BKE_stamp_info_callback(ibuf, stamp_data, metadata_set_field, false); } @@ -2959,12 +2358,12 @@ static void metadata_copy_custom_fields(const char *field, const char *value, vo BKE_render_result_stamp_data(rr, field, value); } -void BKE_stamp_info_from_imbuf(RenderResult *rr, struct ImBuf *ibuf) +void BKE_stamp_info_from_imbuf(RenderResult *rr, ImBuf *ibuf) { if (rr->stamp_data == nullptr) { rr->stamp_data = MEM_cnew<StampData>("RenderResult.stamp_data"); } - struct StampData *stamp_data = rr->stamp_data; + StampData *stamp_data = rr->stamp_data; IMB_metadata_ensure(&ibuf->metadata); BKE_stamp_info_callback(ibuf, stamp_data, metadata_get_field, true); /* Copy render engine specific settings. */ @@ -2994,171 +2393,9 @@ bool BKE_imbuf_alpha_test(ImBuf *ibuf) return false; } -void BKE_imbuf_write_prepare(ImBuf *ibuf, const ImageFormatData *imf) -{ - char imtype = imf->imtype; - char compress = imf->compress; - char quality = imf->quality; - - /* initialize all from image format */ - ibuf->foptions.flag = 0; - - if (imtype == R_IMF_IMTYPE_IRIS) { - ibuf->ftype = IMB_FTYPE_IMAGIC; - } -#ifdef WITH_HDR - else if (imtype == R_IMF_IMTYPE_RADHDR) { - ibuf->ftype = IMB_FTYPE_RADHDR; - } -#endif - else if (ELEM(imtype, - R_IMF_IMTYPE_PNG, - R_IMF_IMTYPE_FFMPEG, - R_IMF_IMTYPE_H264, - R_IMF_IMTYPE_THEORA, - R_IMF_IMTYPE_XVID)) { - ibuf->ftype = IMB_FTYPE_PNG; - - if (imtype == R_IMF_IMTYPE_PNG) { - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= PNG_16BIT; - } - - ibuf->foptions.quality = compress; - } - } -#ifdef WITH_DDS - else if (imtype == R_IMF_IMTYPE_DDS) { - ibuf->ftype = IMB_FTYPE_DDS; - } -#endif - else if (imtype == R_IMF_IMTYPE_BMP) { - ibuf->ftype = IMB_FTYPE_BMP; - } -#ifdef WITH_TIFF - else if (imtype == R_IMF_IMTYPE_TIFF) { - ibuf->ftype = IMB_FTYPE_TIF; - - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= TIF_16BIT; - } - if (imf->tiff_codec == R_IMF_TIFF_CODEC_NONE) { - ibuf->foptions.flag |= TIF_COMPRESS_NONE; - } - else if (imf->tiff_codec == R_IMF_TIFF_CODEC_DEFLATE) { - ibuf->foptions.flag |= TIF_COMPRESS_DEFLATE; - } - else if (imf->tiff_codec == R_IMF_TIFF_CODEC_LZW) { - ibuf->foptions.flag |= TIF_COMPRESS_LZW; - } - else if (imf->tiff_codec == R_IMF_TIFF_CODEC_PACKBITS) { - ibuf->foptions.flag |= TIF_COMPRESS_PACKBITS; - } - } -#endif -#ifdef WITH_OPENEXR - else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { - ibuf->ftype = IMB_FTYPE_OPENEXR; - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= OPENEXR_HALF; - } - ibuf->foptions.flag |= (imf->exr_codec & OPENEXR_COMPRESS); - - if (!(imf->flag & R_IMF_FLAG_ZBUF)) { - /* Signal for exr saving. */ - IMB_freezbuffloatImBuf(ibuf); - } - } -#endif -#ifdef WITH_CINEON - else if (imtype == R_IMF_IMTYPE_CINEON) { - ibuf->ftype = IMB_FTYPE_CINEON; - if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { - ibuf->foptions.flag |= CINEON_LOG; - } - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= CINEON_16BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_12) { - ibuf->foptions.flag |= CINEON_12BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_10) { - ibuf->foptions.flag |= CINEON_10BIT; - } - } - else if (imtype == R_IMF_IMTYPE_DPX) { - ibuf->ftype = IMB_FTYPE_DPX; - if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { - ibuf->foptions.flag |= CINEON_LOG; - } - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= CINEON_16BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_12) { - ibuf->foptions.flag |= CINEON_12BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_10) { - ibuf->foptions.flag |= CINEON_10BIT; - } - } -#endif - else if (imtype == R_IMF_IMTYPE_TARGA) { - ibuf->ftype = IMB_FTYPE_TGA; - } - else if (imtype == R_IMF_IMTYPE_RAWTGA) { - ibuf->ftype = IMB_FTYPE_TGA; - ibuf->foptions.flag = RAWTGA; - } -#ifdef WITH_OPENJPEG - else if (imtype == R_IMF_IMTYPE_JP2) { - if (quality < 10) { - quality = 90; - } - ibuf->ftype = IMB_FTYPE_JP2; - ibuf->foptions.quality = quality; - - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= JP2_16BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_12) { - ibuf->foptions.flag |= JP2_12BIT; - } - - if (imf->jp2_flag & R_IMF_JP2_FLAG_YCC) { - ibuf->foptions.flag |= JP2_YCC; - } - - if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_PRESET) { - ibuf->foptions.flag |= JP2_CINE; - if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_48) { - ibuf->foptions.flag |= JP2_CINE_48FPS; - } - } - - if (imf->jp2_codec == R_IMF_JP2_CODEC_JP2) { - ibuf->foptions.flag |= JP2_JP2; - } - else if (imf->jp2_codec == R_IMF_JP2_CODEC_J2K) { - ibuf->foptions.flag |= JP2_J2K; - } - else { - BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); - } - } -#endif - else { - /* #R_IMF_IMTYPE_JPEG90, etc. fallback to JPEG image. */ - if (quality < 10) { - quality = 90; - } - ibuf->ftype = IMB_FTYPE_JPG; - ibuf->foptions.quality = quality; - } -} - int BKE_imbuf_write(ImBuf *ibuf, const char *name, const ImageFormatData *imf) { - BKE_imbuf_write_prepare(ibuf, imf); + BKE_image_format_to_imbuf(ibuf, imf); BLI_make_existing_file(name); @@ -3190,8 +2427,8 @@ int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, cons return ok; } -int BKE_imbuf_write_stamp(Scene *scene, - struct RenderResult *rr, +int BKE_imbuf_write_stamp(const Scene *scene, + const struct RenderResult *rr, ImBuf *ibuf, const char *name, const struct ImageFormatData *imf) @@ -3203,60 +2440,6 @@ int BKE_imbuf_write_stamp(Scene *scene, return BKE_imbuf_write(ibuf, name, imf); } -static void do_makepicstring(char *string, - const char *base, - const char *relbase, - int frame, - const char imtype, - const ImageFormatData *im_format, - const bool use_ext, - const bool use_frames, - const char *suffix) -{ - if (string == nullptr) { - return; - } - BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ - BLI_path_abs(string, relbase); - - if (use_frames) { - BLI_path_frame(string, frame, 4); - } - - if (suffix) { - BLI_path_suffix(string, FILE_MAX, suffix, ""); - } - - if (use_ext) { - do_add_image_extension(string, imtype, im_format); - } -} - -void BKE_image_path_from_imformat(char *string, - const char *base, - const char *relbase, - int frame, - const ImageFormatData *im_format, - const bool use_ext, - const bool use_frames, - const char *suffix) -{ - do_makepicstring( - string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames, suffix); -} - -void BKE_image_path_from_imtype(char *string, - const char *base, - const char *relbase, - int frame, - const char imtype, - const bool use_ext, - const bool use_frames, - const char *suffix) -{ - do_makepicstring(string, base, relbase, frame, imtype, nullptr, use_ext, use_frames, suffix); -} - struct anim *openanim_noload(const char *name, int flags, int streamindex, @@ -3930,14 +3113,15 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i int max_udim = 0; int id; - struct direntry *dir; - uint totfile = BLI_filelist_dir_contents(dirname, &dir); - for (int i = 0; i < totfile; i++) { - if (!(dir[i].type & S_IFREG)) { + struct direntry *dirs; + const uint dirs_num = BLI_filelist_dir_contents(dirname, &dirs); + for (int i = 0; i < dirs_num; i++) { + if (!(dirs[i].type & S_IFREG)) { continue; } - if (!BKE_image_get_tile_number_from_filepath(dir[i].relname, udim_pattern, tile_format, &id)) { + if (!BKE_image_get_tile_number_from_filepath( + dirs[i].relname, udim_pattern, tile_format, &id)) { continue; } @@ -3950,7 +3134,7 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i min_udim = min_ii(min_udim, id); max_udim = max_ii(max_udim, id); } - BLI_filelist_free(dir, totfile); + BLI_filelist_free(dirs, dirs_num); MEM_SAFE_FREE(udim_pattern); if (is_udim && min_udim <= IMA_UDIM_MAX) { @@ -4135,69 +3319,24 @@ void BKE_image_ensure_tile_token(char *filename) return; } - /* Is there a sequence of digits in the filename? */ - ushort digits; - char head[FILE_MAX], tail[FILE_MAX]; - BLI_path_sequence_decode(filename, head, tail, &digits); - if (digits == 4) { - sprintf(filename, "%s<UDIM>%s", head, tail); - return; - } + std::string path(filename); + std::smatch match; - /* Is there a sequence like u##_v#### in the filename? */ - uint cur = 0; - uint name_end = strlen(filename); - uint u_digits = 0; - uint v_digits = 0; - uint u_start = (uint)-1; - bool u_found = false; - bool v_found = false; - bool sep_found = false; - while (cur < name_end) { - if (filename[cur] == 'u') { - u_found = true; - u_digits = 0; - u_start = cur; - } - else if (filename[cur] == 'v') { - v_found = true; - v_digits = 0; - } - else if (u_found && !v_found) { - if (isdigit(filename[cur]) && u_digits < 2) { - u_digits++; - } - else if (filename[cur] == '_') { - sep_found = true; - } - else { - u_found = false; - } - } - else if (u_found && u_digits > 0 && v_found) { - if (isdigit(filename[cur])) { - if (v_digits < 4) { - v_digits++; - } - else { - u_found = false; - v_found = false; - } - } - else if (v_digits > 0) { - break; - } - } - - cur++; + /* General 4-digit "udim" pattern. As this format is susceptible to ambiguity + * with other digit sequences, we can leverage the supported range of roughly + * 1000 through 2000 to provide better detection. + */ + std::regex pattern(R"((^|.*?\D)([12]\d{3})(\D.*))"); + if (std::regex_search(path, match, pattern)) { + BLI_strncpy(filename, match.format("$1<UDIM>$3").c_str(), FILE_MAX); + return; } - if (u_found && sep_found && v_found && (u_digits + v_digits > 1)) { - const char *token = "<UVTILE>"; - const size_t token_length = strlen(token); - memmove(filename + u_start + token_length, filename + cur, name_end - cur); - memcpy(filename + u_start, token, token_length); - filename[u_start + token_length + (name_end - cur)] = '\0'; + /* General `u##_v###` `uvtile` pattern. */ + pattern = std::regex(R"((.*)(u\d{1,2}_v\d{1,3})(\D.*))"); + if (std::regex_search(path, match, pattern)) { + BLI_strncpy(filename, match.format("$1<UVTILE>$3").c_str(), FILE_MAX); + return; } } @@ -4212,15 +3351,15 @@ bool BKE_image_tile_filepath_exists(const char *filepath) char *udim_pattern = BKE_image_get_tile_strformat(filepath, &tile_format); bool found = false; - struct direntry *dir; - uint totfile = BLI_filelist_dir_contents(dirname, &dir); - for (int i = 0; i < totfile; i++) { - if (!(dir[i].type & S_IFREG)) { + struct direntry *dirs; + const uint dirs_num = BLI_filelist_dir_contents(dirname, &dirs); + for (int i = 0; i < dirs_num; i++) { + if (!(dirs[i].type & S_IFREG)) { continue; } int id; - if (!BKE_image_get_tile_number_from_filepath(dir[i].path, udim_pattern, tile_format, &id)) { + if (!BKE_image_get_tile_number_from_filepath(dirs[i].path, udim_pattern, tile_format, &id)) { continue; } @@ -4231,7 +3370,7 @@ bool BKE_image_tile_filepath_exists(const char *filepath) found = true; break; } - BLI_filelist_free(dir, totfile); + BLI_filelist_free(dirs, dirs_num); MEM_SAFE_FREE(udim_pattern); return found; @@ -5105,31 +4244,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc RE_AcquireResultImage(re, &rres, actview); } else if ((slot = BKE_image_get_renderslot(ima, ima->render_slot))->render) { - /* Unfortunately each field needs to be set individually because RenderResult - * contains volatile fields and using memcpy would invoke undefined behavior with c++. */ - rres.next = slot->render->next; - rres.prev = slot->render->prev; - rres.rectx = slot->render->rectx; - rres.recty = slot->render->recty; - rres.sample_nr = slot->render->sample_nr; - rres.rect32 = slot->render->rect32; - rres.rectf = slot->render->rectf; - rres.rectz = slot->render->rectz; - rres.tilerect = slot->render->tilerect; - rres.xof = slot->render->xof; - rres.yof = slot->render->yof; - rres.layers = slot->render->layers; - rres.views = slot->render->views; - rres.renrect.xmin = slot->render->renrect.xmin; - rres.renrect.xmax = slot->render->renrect.xmax; - rres.renrect.ymin = slot->render->renrect.ymin; - rres.renrect.ymax = slot->render->renrect.ymax; - rres.renlay = slot->render->renlay; - rres.framenr = slot->render->framenr; - rres.text = slot->render->text; - rres.error = slot->render->error; - rres.stamp_data = slot->render->stamp_data; - rres.passes_allocated = slot->render->passes_allocated; + rres = *(slot->render); rres.have_combined = ((RenderView *)rres.views.first)->rectf != nullptr; } @@ -5292,7 +4407,7 @@ static int image_get_multiview_index(Image *ima, ImageUser *iuser) } if (is_backdrop) { if (BKE_image_is_stereo(ima)) { - /* backdrop hackaround (since there is no iuser */ + /* Backdrop hack / workaround (since there is no `iuser`). */ return ima->eye; } } @@ -5939,7 +5054,7 @@ void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, b bool BKE_image_has_alpha(Image *image) { void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, nullptr, &lock); const int planes = (ibuf ? ibuf->planes : 0); BKE_image_release_ibuf(image, ibuf, lock); @@ -6140,8 +5255,8 @@ bool BKE_image_buffer_format_writable(ImBuf *ibuf) { ImageFormatData im_format; ImbFormatOptions options_dummy; - BKE_imbuf_to_image_format(&im_format, ibuf); - return (BKE_image_imtype_to_ftype(im_format.imtype, &options_dummy) == ibuf->ftype); + BKE_image_format_from_imbuf(&im_format, ibuf); + return (BKE_imtype_to_ftype(im_format.imtype, &options_dummy) == ibuf->ftype); } void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions *options) diff --git a/source/blender/blenkernel/intern/image_format.cc b/source/blender/blenkernel/intern/image_format.cc new file mode 100644 index 00000000000..3ff0b3da963 --- /dev/null +++ b/source/blender/blenkernel/intern/image_format.cc @@ -0,0 +1,966 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include <cstring> + +#include "DNA_defaults.h" +#include "DNA_scene_types.h" + +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BKE_colortools.h" +#include "BKE_image_format.h" + +/* Init/Copy/Free */ + +void BKE_image_format_init(ImageFormatData *imf, const bool render) +{ + *imf = *DNA_struct_default_get(ImageFormatData); + + BKE_color_managed_display_settings_init(&imf->display_settings); + + if (render) { + BKE_color_managed_view_settings_init_render( + &imf->view_settings, &imf->display_settings, "Filmic"); + } + else { + BKE_color_managed_view_settings_init_default(&imf->view_settings, &imf->display_settings); + } + + BKE_color_managed_colorspace_settings_init(&imf->linear_colorspace_settings); +} + +void BKE_image_format_copy(ImageFormatData *imf_dst, const ImageFormatData *imf_src) +{ + memcpy(imf_dst, imf_src, sizeof(*imf_dst)); + BKE_color_managed_display_settings_copy(&imf_dst->display_settings, &imf_src->display_settings); + BKE_color_managed_view_settings_copy(&imf_dst->view_settings, &imf_src->view_settings); + BKE_color_managed_colorspace_settings_copy(&imf_dst->linear_colorspace_settings, + &imf_src->linear_colorspace_settings); +} + +void BKE_image_format_free(ImageFormatData *imf) +{ + BKE_color_managed_view_settings_free(&imf->view_settings); +} + +void BKE_image_format_blend_read_data(BlendDataReader *reader, ImageFormatData *imf) +{ + BKE_color_managed_view_settings_blend_read_data(reader, &imf->view_settings); +} + +void BKE_image_format_blend_write(BlendWriter *writer, ImageFormatData *imf) +{ + BKE_color_managed_view_settings_blend_write(writer, &imf->view_settings); +} + +/* File Types */ + +int BKE_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options) +{ + memset(r_options, 0, sizeof(*r_options)); + + if (imtype == R_IMF_IMTYPE_TARGA) { + return IMB_FTYPE_TGA; + } + if (imtype == R_IMF_IMTYPE_RAWTGA) { + r_options->flag = RAWTGA; + return IMB_FTYPE_TGA; + } + if (imtype == R_IMF_IMTYPE_IRIS) { + return IMB_FTYPE_IMAGIC; + } +#ifdef WITH_HDR + if (imtype == R_IMF_IMTYPE_RADHDR) { + return IMB_FTYPE_RADHDR; + } +#endif + if (imtype == R_IMF_IMTYPE_PNG) { + r_options->quality = 15; + return IMB_FTYPE_PNG; + } +#ifdef WITH_DDS + if (imtype == R_IMF_IMTYPE_DDS) { + return IMB_FTYPE_DDS; + } +#endif + if (imtype == R_IMF_IMTYPE_BMP) { + return IMB_FTYPE_BMP; + } +#ifdef WITH_TIFF + if (imtype == R_IMF_IMTYPE_TIFF) { + return IMB_FTYPE_TIF; + } +#endif + if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + return IMB_FTYPE_OPENEXR; + } +#ifdef WITH_CINEON + if (imtype == R_IMF_IMTYPE_CINEON) { + return IMB_FTYPE_CINEON; + } + if (imtype == R_IMF_IMTYPE_DPX) { + return IMB_FTYPE_DPX; + } +#endif +#ifdef WITH_OPENJPEG + if (imtype == R_IMF_IMTYPE_JP2) { + r_options->flag |= JP2_JP2; + r_options->quality = 90; + return IMB_FTYPE_JP2; + } +#endif +#ifdef WITH_WEBP + if (imtype == R_IMF_IMTYPE_WEBP) { + r_options->quality = 90; + return IMB_FTYPE_WEBP; + } +#endif + + r_options->quality = 90; + return IMB_FTYPE_JPG; +} + +char BKE_ftype_to_imtype(const int ftype, const ImbFormatOptions *options) +{ + if (ftype == IMB_FTYPE_NONE) { + return R_IMF_IMTYPE_TARGA; + } + if (ftype == IMB_FTYPE_IMAGIC) { + return R_IMF_IMTYPE_IRIS; + } +#ifdef WITH_HDR + if (ftype == IMB_FTYPE_RADHDR) { + return R_IMF_IMTYPE_RADHDR; + } +#endif + if (ftype == IMB_FTYPE_PNG) { + return R_IMF_IMTYPE_PNG; + } +#ifdef WITH_DDS + if (ftype == IMB_FTYPE_DDS) { + return R_IMF_IMTYPE_DDS; + } +#endif + if (ftype == IMB_FTYPE_BMP) { + return R_IMF_IMTYPE_BMP; + } +#ifdef WITH_TIFF + if (ftype == IMB_FTYPE_TIF) { + return R_IMF_IMTYPE_TIFF; + } +#endif + if (ftype == IMB_FTYPE_OPENEXR) { + return R_IMF_IMTYPE_OPENEXR; + } +#ifdef WITH_CINEON + if (ftype == IMB_FTYPE_CINEON) { + return R_IMF_IMTYPE_CINEON; + } + if (ftype == IMB_FTYPE_DPX) { + return R_IMF_IMTYPE_DPX; + } +#endif + if (ftype == IMB_FTYPE_TGA) { + if (options && (options->flag & RAWTGA)) { + return R_IMF_IMTYPE_RAWTGA; + } + + return R_IMF_IMTYPE_TARGA; + } +#ifdef WITH_OPENJPEG + if (ftype == IMB_FTYPE_JP2) { + return R_IMF_IMTYPE_JP2; + } +#endif +#ifdef WITH_WEBP + if (ftype == IMB_FTYPE_WEBP) { + return R_IMF_IMTYPE_WEBP; + } +#endif + + return R_IMF_IMTYPE_JPEG90; +} + +bool BKE_imtype_is_movie(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_AVIRAW: + case R_IMF_IMTYPE_AVIJPEG: + case R_IMF_IMTYPE_FFMPEG: + case R_IMF_IMTYPE_H264: + case R_IMF_IMTYPE_THEORA: + case R_IMF_IMTYPE_XVID: + return true; + } + return false; +} + +bool BKE_imtype_supports_zbuf(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_IRIZ: + case R_IMF_IMTYPE_OPENEXR: /* but not R_IMF_IMTYPE_MULTILAYER */ + return true; + } + return false; +} + +bool BKE_imtype_supports_compress(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_PNG: + return true; + } + return false; +} + +bool BKE_imtype_supports_quality(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_JPEG90: + case R_IMF_IMTYPE_JP2: + case R_IMF_IMTYPE_AVIJPEG: + case R_IMF_IMTYPE_WEBP: + return true; + } + return false; +} + +bool BKE_imtype_requires_linear_float(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_CINEON: + case R_IMF_IMTYPE_DPX: + case R_IMF_IMTYPE_RADHDR: + case R_IMF_IMTYPE_OPENEXR: + case R_IMF_IMTYPE_MULTILAYER: + return true; + } + return false; +} + +char BKE_imtype_valid_channels(const char imtype, bool write_file) +{ + char chan_flag = IMA_CHAN_FLAG_RGB; /* Assume all support RGB. */ + + /* Alpha. */ + switch (imtype) { + case R_IMF_IMTYPE_BMP: + if (write_file) { + break; + } + ATTR_FALLTHROUGH; + case R_IMF_IMTYPE_TARGA: + case R_IMF_IMTYPE_RAWTGA: + case R_IMF_IMTYPE_IRIS: + case R_IMF_IMTYPE_PNG: + case R_IMF_IMTYPE_TIFF: + case R_IMF_IMTYPE_OPENEXR: + case R_IMF_IMTYPE_MULTILAYER: + case R_IMF_IMTYPE_DDS: + case R_IMF_IMTYPE_JP2: + case R_IMF_IMTYPE_DPX: + case R_IMF_IMTYPE_WEBP: + chan_flag |= IMA_CHAN_FLAG_ALPHA; + break; + } + + /* BW. */ + switch (imtype) { + case R_IMF_IMTYPE_BMP: + case R_IMF_IMTYPE_PNG: + case R_IMF_IMTYPE_JPEG90: + case R_IMF_IMTYPE_TARGA: + case R_IMF_IMTYPE_RAWTGA: + case R_IMF_IMTYPE_TIFF: + case R_IMF_IMTYPE_IRIS: + chan_flag |= IMA_CHAN_FLAG_BW; + break; + } + + return chan_flag; +} + +char BKE_imtype_valid_depths(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_RADHDR: + return R_IMF_CHAN_DEPTH_32; + case R_IMF_IMTYPE_TIFF: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; + case R_IMF_IMTYPE_OPENEXR: + return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; + case R_IMF_IMTYPE_MULTILAYER: + return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; + /* NOTE: CINEON uses an unusual 10bits-LOG per channel. */ + case R_IMF_IMTYPE_DPX: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_10 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; + case R_IMF_IMTYPE_CINEON: + return R_IMF_CHAN_DEPTH_10; + case R_IMF_IMTYPE_JP2: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; + case R_IMF_IMTYPE_PNG: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; + /* Most formats are 8bit only. */ + default: + return R_IMF_CHAN_DEPTH_8; + } +} + +char BKE_imtype_from_arg(const char *imtype_arg) +{ + if (STREQ(imtype_arg, "TGA")) { + return R_IMF_IMTYPE_TARGA; + } + if (STREQ(imtype_arg, "IRIS")) { + return R_IMF_IMTYPE_IRIS; + } +#ifdef WITH_DDS + if (STREQ(imtype_arg, "DDS")) { + return R_IMF_IMTYPE_DDS; + } +#endif + if (STREQ(imtype_arg, "JPEG")) { + return R_IMF_IMTYPE_JPEG90; + } + if (STREQ(imtype_arg, "IRIZ")) { + return R_IMF_IMTYPE_IRIZ; + } + if (STREQ(imtype_arg, "RAWTGA")) { + return R_IMF_IMTYPE_RAWTGA; + } + if (STREQ(imtype_arg, "AVIRAW")) { + return R_IMF_IMTYPE_AVIRAW; + } + if (STREQ(imtype_arg, "AVIJPEG")) { + return R_IMF_IMTYPE_AVIJPEG; + } + if (STREQ(imtype_arg, "PNG")) { + return R_IMF_IMTYPE_PNG; + } + if (STREQ(imtype_arg, "BMP")) { + return R_IMF_IMTYPE_BMP; + } +#ifdef WITH_HDR + if (STREQ(imtype_arg, "HDR")) { + return R_IMF_IMTYPE_RADHDR; + } +#endif +#ifdef WITH_TIFF + if (STREQ(imtype_arg, "TIFF")) { + return R_IMF_IMTYPE_TIFF; + } +#endif +#ifdef WITH_OPENEXR + if (STREQ(imtype_arg, "OPEN_EXR")) { + return R_IMF_IMTYPE_OPENEXR; + } + if (STREQ(imtype_arg, "OPEN_EXR_MULTILAYER")) { + return R_IMF_IMTYPE_MULTILAYER; + } + if (STREQ(imtype_arg, "EXR")) { + return R_IMF_IMTYPE_OPENEXR; + } + if (STREQ(imtype_arg, "MULTILAYER")) { + return R_IMF_IMTYPE_MULTILAYER; + } +#endif + if (STREQ(imtype_arg, "FFMPEG")) { + return R_IMF_IMTYPE_FFMPEG; + } +#ifdef WITH_CINEON + if (STREQ(imtype_arg, "CINEON")) { + return R_IMF_IMTYPE_CINEON; + } + if (STREQ(imtype_arg, "DPX")) { + return R_IMF_IMTYPE_DPX; + } +#endif +#ifdef WITH_OPENJPEG + if (STREQ(imtype_arg, "JP2")) { + return R_IMF_IMTYPE_JP2; + } +#endif +#ifdef WITH_WEBP + if (STREQ(imtype_arg, "WEBP")) { + return R_IMF_IMTYPE_WEBP; + } +#endif + + return R_IMF_IMTYPE_INVALID; +} + +/* File Paths */ + +static bool do_add_image_extension(char *string, + const char imtype, + const ImageFormatData *im_format) +{ + const char *extension = nullptr; + const char *extension_test; + (void)im_format; /* may be unused, depends on build options */ + + if (imtype == R_IMF_IMTYPE_IRIS) { + if (!BLI_path_extension_check(string, extension_test = ".rgb")) { + extension = extension_test; + } + } + else if (imtype == R_IMF_IMTYPE_IRIZ) { + if (!BLI_path_extension_check(string, extension_test = ".rgb")) { + extension = extension_test; + } + } +#ifdef WITH_HDR + else if (imtype == R_IMF_IMTYPE_RADHDR) { + if (!BLI_path_extension_check(string, extension_test = ".hdr")) { + extension = extension_test; + } + } +#endif + else if (ELEM(imtype, + R_IMF_IMTYPE_PNG, + R_IMF_IMTYPE_FFMPEG, + R_IMF_IMTYPE_H264, + R_IMF_IMTYPE_THEORA, + R_IMF_IMTYPE_XVID)) { + if (!BLI_path_extension_check(string, extension_test = ".png")) { + extension = extension_test; + } + } +#ifdef WITH_DDS + else if (imtype == R_IMF_IMTYPE_DDS) { + if (!BLI_path_extension_check(string, extension_test = ".dds")) { + extension = extension_test; + } + } +#endif + else if (ELEM(imtype, R_IMF_IMTYPE_TARGA, R_IMF_IMTYPE_RAWTGA)) { + if (!BLI_path_extension_check(string, extension_test = ".tga")) { + extension = extension_test; + } + } + else if (imtype == R_IMF_IMTYPE_BMP) { + if (!BLI_path_extension_check(string, extension_test = ".bmp")) { + extension = extension_test; + } + } +#ifdef WITH_TIFF + else if (imtype == R_IMF_IMTYPE_TIFF) { + if (!BLI_path_extension_check_n(string, extension_test = ".tif", ".tiff", nullptr)) { + extension = extension_test; + } + } +#endif +#ifdef WITH_OPENIMAGEIO + else if (imtype == R_IMF_IMTYPE_PSD) { + if (!BLI_path_extension_check(string, extension_test = ".psd")) { + extension = extension_test; + } + } +#endif +#ifdef WITH_OPENEXR + else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + if (!BLI_path_extension_check(string, extension_test = ".exr")) { + extension = extension_test; + } + } +#endif +#ifdef WITH_CINEON + else if (imtype == R_IMF_IMTYPE_CINEON) { + if (!BLI_path_extension_check(string, extension_test = ".cin")) { + extension = extension_test; + } + } + else if (imtype == R_IMF_IMTYPE_DPX) { + if (!BLI_path_extension_check(string, extension_test = ".dpx")) { + extension = extension_test; + } + } +#endif +#ifdef WITH_OPENJPEG + else if (imtype == R_IMF_IMTYPE_JP2) { + if (im_format) { + if (im_format->jp2_codec == R_IMF_JP2_CODEC_JP2) { + if (!BLI_path_extension_check(string, extension_test = ".jp2")) { + extension = extension_test; + } + } + else if (im_format->jp2_codec == R_IMF_JP2_CODEC_J2K) { + if (!BLI_path_extension_check(string, extension_test = ".j2c")) { + extension = extension_test; + } + } + else { + BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); + } + } + else { + if (!BLI_path_extension_check(string, extension_test = ".jp2")) { + extension = extension_test; + } + } + } +#endif +#ifdef WITH_WEBP + else if (imtype == R_IMF_IMTYPE_WEBP) { + if (!BLI_path_extension_check(string, extension_test = ".webp")) + extension = extension_test; + } +#endif + else { // R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG, R_IMF_IMTYPE_JPEG90 etc + if (!(BLI_path_extension_check_n(string, extension_test = ".jpg", ".jpeg", nullptr))) { + extension = extension_test; + } + } + + if (extension) { + /* prefer this in many cases to avoid .png.tga, but in certain cases it breaks */ + /* remove any other known image extension */ + if (BLI_path_extension_check_array(string, imb_ext_image)) { + return BLI_path_extension_replace(string, FILE_MAX, extension); + } + + return BLI_path_extension_ensure(string, FILE_MAX, extension); + } + + return false; +} + +int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) +{ + return do_add_image_extension(string, im_format->imtype, im_format); +} + +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) +{ + return do_add_image_extension(string, imtype, nullptr); +} + +static void do_makepicstring(char *string, + const char *base, + const char *relbase, + int frame, + const char imtype, + const ImageFormatData *im_format, + const bool use_ext, + const bool use_frames, + const char *suffix) +{ + if (string == nullptr) { + return; + } + BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ + BLI_path_abs(string, relbase); + + if (use_frames) { + BLI_path_frame(string, frame, 4); + } + + if (suffix) { + BLI_path_suffix(string, FILE_MAX, suffix, ""); + } + + if (use_ext) { + do_add_image_extension(string, imtype, im_format); + } +} + +void BKE_image_path_from_imformat(char *string, + const char *base, + const char *relbase, + int frame, + const ImageFormatData *im_format, + const bool use_ext, + const bool use_frames, + const char *suffix) +{ + do_makepicstring( + string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames, suffix); +} + +void BKE_image_path_from_imtype(char *string, + const char *base, + const char *relbase, + int frame, + const char imtype, + const bool use_ext, + const bool use_frames, + const char *suffix) +{ + do_makepicstring(string, base, relbase, frame, imtype, nullptr, use_ext, use_frames, suffix); +} + +/* ImBuf Conversion */ + +void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf) +{ + /* Write to ImBuf in preparation for file writing. */ + char imtype = imf->imtype; + char compress = imf->compress; + char quality = imf->quality; + + /* initialize all from image format */ + ibuf->foptions.flag = 0; + + if (imtype == R_IMF_IMTYPE_IRIS) { + ibuf->ftype = IMB_FTYPE_IMAGIC; + } +#ifdef WITH_HDR + else if (imtype == R_IMF_IMTYPE_RADHDR) { + ibuf->ftype = IMB_FTYPE_RADHDR; + } +#endif + else if (ELEM(imtype, + R_IMF_IMTYPE_PNG, + R_IMF_IMTYPE_FFMPEG, + R_IMF_IMTYPE_H264, + R_IMF_IMTYPE_THEORA, + R_IMF_IMTYPE_XVID)) { + ibuf->ftype = IMB_FTYPE_PNG; + + if (imtype == R_IMF_IMTYPE_PNG) { + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= PNG_16BIT; + } + + ibuf->foptions.quality = compress; + } + } +#ifdef WITH_DDS + else if (imtype == R_IMF_IMTYPE_DDS) { + ibuf->ftype = IMB_FTYPE_DDS; + } +#endif + else if (imtype == R_IMF_IMTYPE_BMP) { + ibuf->ftype = IMB_FTYPE_BMP; + } +#ifdef WITH_TIFF + else if (imtype == R_IMF_IMTYPE_TIFF) { + ibuf->ftype = IMB_FTYPE_TIF; + + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= TIF_16BIT; + } + if (imf->tiff_codec == R_IMF_TIFF_CODEC_NONE) { + ibuf->foptions.flag |= TIF_COMPRESS_NONE; + } + else if (imf->tiff_codec == R_IMF_TIFF_CODEC_DEFLATE) { + ibuf->foptions.flag |= TIF_COMPRESS_DEFLATE; + } + else if (imf->tiff_codec == R_IMF_TIFF_CODEC_LZW) { + ibuf->foptions.flag |= TIF_COMPRESS_LZW; + } + else if (imf->tiff_codec == R_IMF_TIFF_CODEC_PACKBITS) { + ibuf->foptions.flag |= TIF_COMPRESS_PACKBITS; + } + } +#endif +#ifdef WITH_OPENEXR + else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + ibuf->ftype = IMB_FTYPE_OPENEXR; + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= OPENEXR_HALF; + } + ibuf->foptions.flag |= (imf->exr_codec & OPENEXR_COMPRESS); + + if (!(imf->flag & R_IMF_FLAG_ZBUF)) { + /* Signal for exr saving. */ + IMB_freezbuffloatImBuf(ibuf); + } + } +#endif +#ifdef WITH_CINEON + else if (imtype == R_IMF_IMTYPE_CINEON) { + ibuf->ftype = IMB_FTYPE_CINEON; + if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { + ibuf->foptions.flag |= CINEON_LOG; + } + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= CINEON_16BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_12) { + ibuf->foptions.flag |= CINEON_12BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_10) { + ibuf->foptions.flag |= CINEON_10BIT; + } + } + else if (imtype == R_IMF_IMTYPE_DPX) { + ibuf->ftype = IMB_FTYPE_DPX; + if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { + ibuf->foptions.flag |= CINEON_LOG; + } + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= CINEON_16BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_12) { + ibuf->foptions.flag |= CINEON_12BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_10) { + ibuf->foptions.flag |= CINEON_10BIT; + } + } +#endif + else if (imtype == R_IMF_IMTYPE_TARGA) { + ibuf->ftype = IMB_FTYPE_TGA; + } + else if (imtype == R_IMF_IMTYPE_RAWTGA) { + ibuf->ftype = IMB_FTYPE_TGA; + ibuf->foptions.flag = RAWTGA; + } +#ifdef WITH_OPENJPEG + else if (imtype == R_IMF_IMTYPE_JP2) { + if (quality < 10) { + quality = 90; + } + ibuf->ftype = IMB_FTYPE_JP2; + ibuf->foptions.quality = quality; + + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= JP2_16BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_12) { + ibuf->foptions.flag |= JP2_12BIT; + } + + if (imf->jp2_flag & R_IMF_JP2_FLAG_YCC) { + ibuf->foptions.flag |= JP2_YCC; + } + + if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_PRESET) { + ibuf->foptions.flag |= JP2_CINE; + if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_48) { + ibuf->foptions.flag |= JP2_CINE_48FPS; + } + } + + if (imf->jp2_codec == R_IMF_JP2_CODEC_JP2) { + ibuf->foptions.flag |= JP2_JP2; + } + else if (imf->jp2_codec == R_IMF_JP2_CODEC_J2K) { + ibuf->foptions.flag |= JP2_J2K; + } + else { + BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); + } + } +#endif +#ifdef WITH_WEBP + else if (imtype == R_IMF_IMTYPE_WEBP) { + ibuf->ftype = IMB_FTYPE_WEBP; + ibuf->foptions.quality = quality; + } +#endif + else { + /* #R_IMF_IMTYPE_JPEG90, etc. default to JPEG. */ + if (quality < 10) { + quality = 90; + } + ibuf->ftype = IMB_FTYPE_JPG; + ibuf->foptions.quality = quality; + } +} + +void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf) +{ + /* Read from ImBuf after file read. */ + int ftype = imbuf->ftype; + int custom_flags = imbuf->foptions.flag; + char quality = imbuf->foptions.quality; + + BKE_image_format_init(im_format, false); + + /* file type */ + if (ftype == IMB_FTYPE_IMAGIC) { + im_format->imtype = R_IMF_IMTYPE_IRIS; + } +#ifdef WITH_HDR + else if (ftype == IMB_FTYPE_RADHDR) { + im_format->imtype = R_IMF_IMTYPE_RADHDR; + } +#endif + else if (ftype == IMB_FTYPE_PNG) { + im_format->imtype = R_IMF_IMTYPE_PNG; + + if (custom_flags & PNG_16BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + + im_format->compress = quality; + } + +#ifdef WITH_DDS + else if (ftype == IMB_FTYPE_DDS) { + im_format->imtype = R_IMF_IMTYPE_DDS; + } +#endif + else if (ftype == IMB_FTYPE_BMP) { + im_format->imtype = R_IMF_IMTYPE_BMP; + } +#ifdef WITH_TIFF + else if (ftype == IMB_FTYPE_TIF) { + im_format->imtype = R_IMF_IMTYPE_TIFF; + if (custom_flags & TIF_16BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + if (custom_flags & TIF_COMPRESS_NONE) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_NONE; + } + if (custom_flags & TIF_COMPRESS_DEFLATE) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_DEFLATE; + } + if (custom_flags & TIF_COMPRESS_LZW) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_LZW; + } + if (custom_flags & TIF_COMPRESS_PACKBITS) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_PACKBITS; + } + } +#endif + +#ifdef WITH_OPENEXR + else if (ftype == IMB_FTYPE_OPENEXR) { + im_format->imtype = R_IMF_IMTYPE_OPENEXR; + if (custom_flags & OPENEXR_HALF) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + if (custom_flags & OPENEXR_COMPRESS) { + im_format->exr_codec = R_IMF_EXR_CODEC_ZIP; /* Can't determine compression */ + } + if (imbuf->zbuf_float) { + im_format->flag |= R_IMF_FLAG_ZBUF; + } + } +#endif + +#ifdef WITH_CINEON + else if (ftype == IMB_FTYPE_CINEON) { + im_format->imtype = R_IMF_IMTYPE_CINEON; + } + else if (ftype == IMB_FTYPE_DPX) { + im_format->imtype = R_IMF_IMTYPE_DPX; + } +#endif + else if (ftype == IMB_FTYPE_TGA) { + if (custom_flags & RAWTGA) { + im_format->imtype = R_IMF_IMTYPE_RAWTGA; + } + else { + im_format->imtype = R_IMF_IMTYPE_TARGA; + } + } +#ifdef WITH_OPENJPEG + else if (ftype == IMB_FTYPE_JP2) { + im_format->imtype = R_IMF_IMTYPE_JP2; + im_format->quality = quality; + + if (custom_flags & JP2_16BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + else if (custom_flags & JP2_12BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_12; + } + + if (custom_flags & JP2_YCC) { + im_format->jp2_flag |= R_IMF_JP2_FLAG_YCC; + } + + if (custom_flags & JP2_CINE) { + im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_PRESET; + if (custom_flags & JP2_CINE_48FPS) { + im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_48; + } + } + + if (custom_flags & JP2_JP2) { + im_format->jp2_codec = R_IMF_JP2_CODEC_JP2; + } + else if (custom_flags & JP2_J2K) { + im_format->jp2_codec = R_IMF_JP2_CODEC_J2K; + } + else { + BLI_assert_msg(0, "Unsupported jp2 codec was specified in file type"); + } + } +#endif +#ifdef WITH_WEBP + else if (ftype == IMB_FTYPE_WEBP) { + im_format->imtype = R_IMF_IMTYPE_WEBP; + im_format->quality = quality; + } +#endif + + else { + im_format->imtype = R_IMF_IMTYPE_JPEG90; + im_format->quality = quality; + } + + /* planes */ + im_format->planes = imbuf->planes; +} + +/* Color Management */ + +void BKE_image_format_color_management_copy(ImageFormatData *imf, const ImageFormatData *imf_src) +{ + BKE_color_managed_view_settings_free(&imf->view_settings); + + BKE_color_managed_display_settings_copy(&imf->display_settings, &imf_src->display_settings); + BKE_color_managed_view_settings_copy(&imf->view_settings, &imf_src->view_settings); + BKE_color_managed_colorspace_settings_copy(&imf->linear_colorspace_settings, + &imf_src->linear_colorspace_settings); +} + +void BKE_image_format_color_management_copy_from_scene(ImageFormatData *imf, const Scene *scene) +{ + BKE_color_managed_view_settings_free(&imf->view_settings); + + BKE_color_managed_display_settings_copy(&imf->display_settings, &scene->display_settings); + BKE_color_managed_view_settings_copy(&imf->view_settings, &scene->view_settings); + STRNCPY(imf->linear_colorspace_settings.name, + IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR)); +} + +/* Output */ + +void BKE_image_format_init_for_write(ImageFormatData *imf, + const Scene *scene_src, + const ImageFormatData *imf_src) +{ + *imf = (imf_src) ? *imf_src : scene_src->r.im_format; + + if (imf_src && imf_src->color_management == R_IMF_COLOR_MANAGEMENT_OVERRIDE) { + /* Use settings specific to one node, image save operation, etc. */ + BKE_color_managed_display_settings_copy(&imf->display_settings, &imf_src->display_settings); + BKE_color_managed_view_settings_copy(&imf->view_settings, &imf_src->view_settings); + BKE_color_managed_colorspace_settings_copy(&imf->linear_colorspace_settings, + &imf_src->linear_colorspace_settings); + } + else if (scene_src->r.im_format.color_management == R_IMF_COLOR_MANAGEMENT_OVERRIDE) { + /* Use scene settings specific to render output. */ + BKE_color_managed_display_settings_copy(&imf->display_settings, + &scene_src->r.im_format.display_settings); + BKE_color_managed_view_settings_copy(&imf->view_settings, + &scene_src->r.im_format.view_settings); + BKE_color_managed_colorspace_settings_copy(&imf->linear_colorspace_settings, + &scene_src->r.im_format.linear_colorspace_settings); + } + else { + /* Use general scene settings also used for display. */ + BKE_color_managed_display_settings_copy(&imf->display_settings, &scene_src->display_settings); + BKE_color_managed_view_settings_copy(&imf->view_settings, &scene_src->view_settings); + STRNCPY(imf->linear_colorspace_settings.name, + IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR)); + } +} diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc index 4606a14ab69..c77ee77a5f2 100644 --- a/source/blender/blenkernel/intern/image_partial_update.cc +++ b/source/blender/blenkernel/intern/image_partial_update.cc @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2021 Blender Foundation. */ /** - * \file image_gpu_partial_update.cc + * \file * \ingroup bke * * To reduce the overhead of image processing this file contains a mechanism to detect areas of the diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c deleted file mode 100644 index ae8f4c044fe..00000000000 --- a/source/blender/blenkernel/intern/image_save.c +++ /dev/null @@ -1,441 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -#include <errno.h> -#include <string.h> - -#include "BLI_listbase.h" -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "DNA_image_types.h" - -#include "MEM_guardedalloc.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "BKE_colortools.h" -#include "BKE_image.h" -#include "BKE_image_save.h" -#include "BKE_main.h" -#include "BKE_report.h" -#include "BKE_scene.h" - -#include "RE_pipeline.h" - -void BKE_image_save_options_init(ImageSaveOptions *opts, Main *bmain, Scene *scene) -{ - memset(opts, 0, sizeof(*opts)); - - opts->bmain = bmain; - opts->scene = scene; - - BKE_imformat_defaults(&opts->im_format); -} - -static void image_save_post(ReportList *reports, - Image *ima, - ImBuf *ibuf, - int ok, - ImageSaveOptions *opts, - int save_copy, - const char *filepath, - bool *r_colorspace_changed) -{ - if (!ok) { - BKE_reportf(reports, RPT_ERROR, "Could not write image: %s", strerror(errno)); - return; - } - - if (save_copy) { - return; - } - - if (opts->do_newpath) { - BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); - BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath)); - } - - ibuf->userflags &= ~IB_BITMAPDIRTY; - - /* change type? */ - if (ima->type == IMA_TYPE_R_RESULT) { - ima->type = IMA_TYPE_IMAGE; - - /* workaround to ensure the render result buffer is no longer used - * by this image, otherwise can crash when a new render result is - * created. */ - if (ibuf->rect && !(ibuf->mall & IB_rect)) { - imb_freerectImBuf(ibuf); - } - if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) { - imb_freerectfloatImBuf(ibuf); - } - if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) { - IMB_freezbufImBuf(ibuf); - } - if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) { - IMB_freezbuffloatImBuf(ibuf); - } - } - if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { - ima->source = IMA_SRC_FILE; - ima->type = IMA_TYPE_IMAGE; - } - - /* only image path, never ibuf */ - if (opts->relative) { - const char *relbase = ID_BLEND_PATH(opts->bmain, &ima->id); - BLI_path_rel(ima->filepath, relbase); /* only after saving */ - } - - ColorManagedColorspaceSettings old_colorspace_settings; - BKE_color_managed_colorspace_settings_copy(&old_colorspace_settings, &ima->colorspace_settings); - IMB_colormanagement_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); - if (!BKE_color_managed_colorspace_settings_equals(&old_colorspace_settings, - &ima->colorspace_settings)) { - *r_colorspace_changed = true; - } -} - -static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) -{ - if (colormanaged_ibuf != ibuf) { - /* This guys might be modified by image buffer write functions, - * need to copy them back from color managed image buffer to an - * original one, so file type of image is being properly updated. - */ - ibuf->ftype = colormanaged_ibuf->ftype; - ibuf->foptions = colormanaged_ibuf->foptions; - ibuf->planes = colormanaged_ibuf->planes; - - IMB_freeImBuf(colormanaged_ibuf); - } -} - -/** - * \return success. - * \note `ima->filepath` and `ibuf->name` should end up the same. - * \note for multi-view the first `ibuf` is important to get the settings. - */ -static bool image_save_single(ReportList *reports, - Image *ima, - ImageUser *iuser, - ImageSaveOptions *opts, - bool *r_colorspace_changed) -{ - void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); - RenderResult *rr = NULL; - bool ok = false; - - if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { - BKE_image_release_ibuf(ima, ibuf, lock); - goto cleanup; - } - - ImBuf *colormanaged_ibuf = NULL; - const bool save_copy = opts->save_copy; - const bool save_as_render = opts->save_as_render; - ImageFormatData *imf = &opts->im_format; - - if (ima->type == IMA_TYPE_R_RESULT) { - /* enforce user setting for RGB or RGBA, but skip BW */ - if (opts->im_format.planes == R_IMF_PLANES_RGBA) { - ibuf->planes = R_IMF_PLANES_RGBA; - } - else if (opts->im_format.planes == R_IMF_PLANES_RGB) { - ibuf->planes = R_IMF_PLANES_RGB; - } - } - else { - /* TODO: better solution, if a 24bit image is painted onto it may contain alpha. */ - if ((opts->im_format.planes == R_IMF_PLANES_RGBA) && - /* it has been painted onto */ - (ibuf->userflags & IB_BITMAPDIRTY)) { - /* checks each pixel, not ideal */ - ibuf->planes = BKE_imbuf_alpha_test(ibuf) ? R_IMF_PLANES_RGBA : R_IMF_PLANES_RGB; - } - } - - /* we need renderresult for exr and rendered multiview */ - rr = BKE_image_acquire_renderresult(opts->scene, ima); - bool is_mono = rr ? BLI_listbase_count_at_most(&rr->views, 2) < 2 : - BLI_listbase_count_at_most(&ima->views, 2) < 2; - bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && - RE_HasFloatPixels(rr); - bool is_multilayer = is_exr_rr && (imf->imtype == R_IMF_IMTYPE_MULTILAYER); - int layer = (iuser && !is_multilayer) ? iuser->layer : -1; - - /* error handling */ - if (!rr) { - if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { - BKE_report(reports, RPT_ERROR, "Did not write, no Multilayer Image"); - BKE_image_release_ibuf(ima, ibuf, lock); - goto cleanup; - } - } - else { - if (imf->views_format == R_IMF_VIEWS_STEREO_3D) { - if (!BKE_image_is_stereo(ima)) { - BKE_reportf(reports, - RPT_ERROR, - "Did not write, the image doesn't have a \"%s\" and \"%s\" views", - STEREO_LEFT_NAME, - STEREO_RIGHT_NAME); - BKE_image_release_ibuf(ima, ibuf, lock); - goto cleanup; - } - - /* It shouldn't ever happen. */ - if ((BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)) == NULL) || - (BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)) == NULL)) { - BKE_reportf(reports, - RPT_ERROR, - "Did not write, the image doesn't have a \"%s\" and \"%s\" views", - STEREO_LEFT_NAME, - STEREO_RIGHT_NAME); - BKE_image_release_ibuf(ima, ibuf, lock); - goto cleanup; - } - } - BKE_imbuf_stamp_info(rr, ibuf); - } - - /* fancy multiview OpenEXR */ - if (imf->views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) { - /* save render result */ - ok = RE_WriteRenderResult(reports, rr, opts->filepath, imf, NULL, layer); - image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed); - BKE_image_release_ibuf(ima, ibuf, lock); - } - /* regular mono pipeline */ - else if (is_mono) { - if (is_exr_rr) { - ok = RE_WriteRenderResult(reports, rr, opts->filepath, imf, NULL, layer); - } - else { - colormanaged_ibuf = IMB_colormanagement_imbuf_for_write( - ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); - ok = BKE_imbuf_write_as(colormanaged_ibuf, opts->filepath, imf, save_copy); - imbuf_save_post(ibuf, colormanaged_ibuf); - } - image_save_post(reports, - ima, - ibuf, - ok, - opts, - (is_exr_rr ? true : save_copy), - opts->filepath, - r_colorspace_changed); - BKE_image_release_ibuf(ima, ibuf, lock); - } - /* individual multiview images */ - else if (imf->views_format == R_IMF_VIEWS_INDIVIDUAL) { - unsigned char planes = ibuf->planes; - const int totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views)); - - if (!is_exr_rr) { - BKE_image_release_ibuf(ima, ibuf, lock); - } - - for (int i = 0; i < totviews; i++) { - char filepath[FILE_MAX]; - bool ok_view = false; - const char *view = rr ? ((RenderView *)BLI_findlink(&rr->views, i))->name : - ((ImageView *)BLI_findlink(&ima->views, i))->name; - - if (is_exr_rr) { - BKE_scene_multiview_view_filepath_get(&opts->scene->r, opts->filepath, view, filepath); - ok_view = RE_WriteRenderResult(reports, rr, filepath, imf, view, layer); - image_save_post(reports, ima, ibuf, ok_view, opts, true, filepath, r_colorspace_changed); - } - else { - /* copy iuser to get the correct ibuf for this view */ - ImageUser view_iuser; - - if (iuser) { - /* copy iuser to get the correct ibuf for this view */ - view_iuser = *iuser; - } - else { - BKE_imageuser_default(&view_iuser); - } - - view_iuser.view = i; - view_iuser.flag &= ~IMA_SHOW_STEREO; - - if (rr) { - BKE_image_multilayer_index(rr, &view_iuser); - } - else { - BKE_image_multiview_index(ima, &view_iuser); - } - - ibuf = BKE_image_acquire_ibuf(ima, &view_iuser, &lock); - ibuf->planes = planes; - - BKE_scene_multiview_view_filepath_get(&opts->scene->r, opts->filepath, view, filepath); - - colormanaged_ibuf = IMB_colormanagement_imbuf_for_write( - ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); - ok_view = BKE_imbuf_write_as(colormanaged_ibuf, filepath, &opts->im_format, save_copy); - imbuf_save_post(ibuf, colormanaged_ibuf); - image_save_post(reports, ima, ibuf, ok_view, opts, true, filepath, r_colorspace_changed); - BKE_image_release_ibuf(ima, ibuf, lock); - } - ok &= ok_view; - } - - if (is_exr_rr) { - BKE_image_release_ibuf(ima, ibuf, lock); - } - } - /* stereo (multiview) images */ - else if (opts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { - if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { - ok = RE_WriteRenderResult(reports, rr, opts->filepath, imf, NULL, layer); - image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed); - BKE_image_release_ibuf(ima, ibuf, lock); - } - else { - ImBuf *ibuf_stereo[2] = {NULL}; - - unsigned char planes = ibuf->planes; - const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - - /* we need to get the specific per-view buffers */ - BKE_image_release_ibuf(ima, ibuf, lock); - - for (int i = 0; i < 2; i++) { - ImageUser view_iuser; - - if (iuser) { - view_iuser = *iuser; - } - else { - BKE_imageuser_default(&view_iuser); - } - - view_iuser.flag &= ~IMA_SHOW_STEREO; - - if (rr) { - int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); - view_iuser.view = id; - BKE_image_multilayer_index(rr, &view_iuser); - } - else { - view_iuser.view = i; - BKE_image_multiview_index(ima, &view_iuser); - } - - ibuf = BKE_image_acquire_ibuf(ima, &view_iuser, &lock); - - if (ibuf == NULL) { - BKE_report( - reports, RPT_ERROR, "Did not write, unexpected error when saving stereo image"); - goto cleanup; - } - - ibuf->planes = planes; - - /* color manage the ImBuf leaving it ready for saving */ - colormanaged_ibuf = IMB_colormanagement_imbuf_for_write( - ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); - - BKE_imbuf_write_prepare(colormanaged_ibuf, imf); - IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); - - /* duplicate buffer to prevent locker issue when using render result */ - ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf); - - imbuf_save_post(ibuf, colormanaged_ibuf); - BKE_image_release_ibuf(ima, ibuf, lock); - } - - ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]); - - /* save via traditional path */ - ok = BKE_imbuf_write_as(ibuf, opts->filepath, imf, save_copy); - - IMB_freeImBuf(ibuf); - - for (int i = 0; i < 2; i++) { - IMB_freeImBuf(ibuf_stereo[i]); - } - } - } - -cleanup: - if (rr) { - BKE_image_release_renderresult(opts->scene, ima); - } - - return ok; -} - -bool BKE_image_save( - ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts) -{ - ImageUser save_iuser; - BKE_imageuser_default(&save_iuser); - - bool colorspace_changed = false; - - eUDIM_TILE_FORMAT tile_format; - char *udim_pattern = NULL; - - if (ima->source == IMA_SRC_TILED) { - /* Verify filepath for tiled images contains a valid UDIM marker. */ - udim_pattern = BKE_image_get_tile_strformat(opts->filepath, &tile_format); - if (tile_format == UDIM_TILE_FORMAT_NONE) { - BKE_reportf(reports, - RPT_ERROR, - "When saving a tiled image, the path '%s' must contain a valid UDIM marker", - opts->filepath); - return false; - } - - /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ - if (iuser == NULL) { - iuser = &save_iuser; - } - } - - /* Save images */ - bool ok = false; - if (ima->source != IMA_SRC_TILED) { - ok = image_save_single(reports, ima, iuser, opts, &colorspace_changed); - } - else { - char filepath[FILE_MAX]; - BLI_strncpy(filepath, opts->filepath, sizeof(filepath)); - - /* Save all the tiles. */ - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - BKE_image_set_filepath_from_tile_number( - opts->filepath, udim_pattern, tile_format, tile->tile_number); - - iuser->tile = tile->tile_number; - ok = image_save_single(reports, ima, iuser, opts, &colorspace_changed); - if (!ok) { - break; - } - } - BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath)); - BLI_strncpy(opts->filepath, filepath, sizeof(opts->filepath)); - MEM_freeN(udim_pattern); - } - - if (colorspace_changed) { - BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_COLORMANAGE); - } - - return ok; -} diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc new file mode 100644 index 00000000000..0d7d238f3b2 --- /dev/null +++ b/source/blender/blenkernel/intern/image_save.cc @@ -0,0 +1,822 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include <cerrno> +#include <cstring> + +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_vector.hh" + +#include "DNA_image_types.h" + +#include "MEM_guardedalloc.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" +#include "IMB_openexr.h" + +#include "BKE_colortools.h" +#include "BKE_image.h" +#include "BKE_image_format.h" +#include "BKE_image_save.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "RE_pipeline.h" + +using blender::Vector; + +void BKE_image_save_options_init(ImageSaveOptions *opts, Main *bmain, Scene *scene) +{ + memset(opts, 0, sizeof(*opts)); + + opts->bmain = bmain; + opts->scene = scene; + + BKE_image_format_init(&opts->im_format, false); +} + +void BKE_image_save_options_free(ImageSaveOptions *opts) +{ + BKE_image_format_free(&opts->im_format); +} + +static void image_save_post(ReportList *reports, + Image *ima, + ImBuf *ibuf, + int ok, + ImageSaveOptions *opts, + int save_copy, + const char *filepath, + bool *r_colorspace_changed) +{ + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Could not write image: %s", strerror(errno)); + return; + } + + if (save_copy) { + return; + } + + if (opts->do_newpath) { + BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); + BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath)); + } + + ibuf->userflags &= ~IB_BITMAPDIRTY; + + /* change type? */ + if (ima->type == IMA_TYPE_R_RESULT) { + ima->type = IMA_TYPE_IMAGE; + + /* workaround to ensure the render result buffer is no longer used + * by this image, otherwise can crash when a new render result is + * created. */ + if (ibuf->rect && !(ibuf->mall & IB_rect)) { + imb_freerectImBuf(ibuf); + } + if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) { + imb_freerectfloatImBuf(ibuf); + } + if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) { + IMB_freezbufImBuf(ibuf); + } + if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) { + IMB_freezbuffloatImBuf(ibuf); + } + } + if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + + /* only image path, never ibuf */ + if (opts->relative) { + const char *relbase = ID_BLEND_PATH(opts->bmain, &ima->id); + BLI_path_rel(ima->filepath, relbase); /* only after saving */ + } + + ColorManagedColorspaceSettings old_colorspace_settings; + BKE_color_managed_colorspace_settings_copy(&old_colorspace_settings, &ima->colorspace_settings); + IMB_colormanagement_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + if (!BKE_color_managed_colorspace_settings_equals(&old_colorspace_settings, + &ima->colorspace_settings)) { + *r_colorspace_changed = true; + } +} + +static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) +{ + if (colormanaged_ibuf != ibuf) { + /* This guys might be modified by image buffer write functions, + * need to copy them back from color managed image buffer to an + * original one, so file type of image is being properly updated. + */ + ibuf->ftype = colormanaged_ibuf->ftype; + ibuf->foptions = colormanaged_ibuf->foptions; + ibuf->planes = colormanaged_ibuf->planes; + + IMB_freeImBuf(colormanaged_ibuf); + } +} + +/** + * \return success. + * \note `ima->filepath` and `ibuf->name` should end up the same. + * \note for multi-view the first `ibuf` is important to get the settings. + */ +static bool image_save_single(ReportList *reports, + Image *ima, + ImageUser *iuser, + ImageSaveOptions *opts, + bool *r_colorspace_changed) +{ + void *lock; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); + RenderResult *rr = nullptr; + bool ok = false; + + if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { + BKE_image_release_ibuf(ima, ibuf, lock); + return ok; + } + + ImBuf *colormanaged_ibuf = nullptr; + const bool save_copy = opts->save_copy; + const bool save_as_render = opts->save_as_render; + ImageFormatData *imf = &opts->im_format; + + if (ima->type == IMA_TYPE_R_RESULT) { + /* enforce user setting for RGB or RGBA, but skip BW */ + if (opts->im_format.planes == R_IMF_PLANES_RGBA) { + ibuf->planes = R_IMF_PLANES_RGBA; + } + else if (opts->im_format.planes == R_IMF_PLANES_RGB) { + ibuf->planes = R_IMF_PLANES_RGB; + } + } + else { + /* TODO: better solution, if a 24bit image is painted onto it may contain alpha. */ + if ((opts->im_format.planes == R_IMF_PLANES_RGBA) && + /* it has been painted onto */ + (ibuf->userflags & IB_BITMAPDIRTY)) { + /* checks each pixel, not ideal */ + ibuf->planes = BKE_imbuf_alpha_test(ibuf) ? R_IMF_PLANES_RGBA : R_IMF_PLANES_RGB; + } + } + + /* we need renderresult for exr and rendered multiview */ + rr = BKE_image_acquire_renderresult(opts->scene, ima); + bool is_mono = rr ? BLI_listbase_count_at_most(&rr->views, 2) < 2 : + BLI_listbase_count_at_most(&ima->views, 2) < 2; + bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + RE_HasFloatPixels(rr); + bool is_multilayer = is_exr_rr && (imf->imtype == R_IMF_IMTYPE_MULTILAYER); + int layer = (iuser && !is_multilayer) ? iuser->layer : -1; + + /* error handling */ + if (rr == nullptr) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { + BKE_report(reports, RPT_ERROR, "Did not write, no Multilayer Image"); + BKE_image_release_ibuf(ima, ibuf, lock); + return ok; + } + } + else { + if (imf->views_format == R_IMF_VIEWS_STEREO_3D) { + if (!BKE_image_is_stereo(ima)) { + BKE_reportf(reports, + RPT_ERROR, + R"(Did not write, the image doesn't have a "%s" and "%s" views)", + STEREO_LEFT_NAME, + STEREO_RIGHT_NAME); + BKE_image_release_ibuf(ima, ibuf, lock); + BKE_image_release_renderresult(opts->scene, ima); + return ok; + } + + /* It shouldn't ever happen. */ + if ((BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)) == nullptr) || + (BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)) == nullptr)) { + BKE_reportf(reports, + RPT_ERROR, + R"(Did not write, the image doesn't have a "%s" and "%s" views)", + STEREO_LEFT_NAME, + STEREO_RIGHT_NAME); + BKE_image_release_ibuf(ima, ibuf, lock); + BKE_image_release_renderresult(opts->scene, ima); + return ok; + } + } + BKE_imbuf_stamp_info(rr, ibuf); + } + + /* fancy multiview OpenEXR */ + if (imf->views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) { + /* save render result */ + ok = BKE_image_render_write_exr( + reports, rr, opts->filepath, imf, save_as_render, nullptr, layer); + image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed); + BKE_image_release_ibuf(ima, ibuf, lock); + } + /* regular mono pipeline */ + else if (is_mono) { + if (is_exr_rr) { + ok = BKE_image_render_write_exr( + reports, rr, opts->filepath, imf, save_as_render, nullptr, layer); + } + else { + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf); + ok = BKE_imbuf_write_as(colormanaged_ibuf, opts->filepath, imf, save_copy); + imbuf_save_post(ibuf, colormanaged_ibuf); + } + image_save_post(reports, + ima, + ibuf, + ok, + opts, + (is_exr_rr ? true : save_copy), + opts->filepath, + r_colorspace_changed); + BKE_image_release_ibuf(ima, ibuf, lock); + } + /* individual multiview images */ + else if (imf->views_format == R_IMF_VIEWS_INDIVIDUAL) { + unsigned char planes = ibuf->planes; + const int totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views)); + + if (!is_exr_rr) { + BKE_image_release_ibuf(ima, ibuf, lock); + } + + for (int i = 0; i < totviews; i++) { + char filepath[FILE_MAX]; + bool ok_view = false; + const char *view = rr ? ((RenderView *)BLI_findlink(&rr->views, i))->name : + ((ImageView *)BLI_findlink(&ima->views, i))->name; + + if (is_exr_rr) { + BKE_scene_multiview_view_filepath_get(&opts->scene->r, opts->filepath, view, filepath); + ok_view = BKE_image_render_write_exr( + reports, rr, filepath, imf, save_as_render, view, layer); + image_save_post(reports, ima, ibuf, ok_view, opts, true, filepath, r_colorspace_changed); + } + else { + /* copy iuser to get the correct ibuf for this view */ + ImageUser view_iuser; + + if (iuser) { + /* copy iuser to get the correct ibuf for this view */ + view_iuser = *iuser; + } + else { + BKE_imageuser_default(&view_iuser); + } + + view_iuser.view = i; + view_iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) { + BKE_image_multilayer_index(rr, &view_iuser); + } + else { + BKE_image_multiview_index(ima, &view_iuser); + } + + ibuf = BKE_image_acquire_ibuf(ima, &view_iuser, &lock); + ibuf->planes = planes; + + BKE_scene_multiview_view_filepath_get(&opts->scene->r, opts->filepath, view, filepath); + + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf); + ok_view = BKE_imbuf_write_as(colormanaged_ibuf, filepath, &opts->im_format, save_copy); + imbuf_save_post(ibuf, colormanaged_ibuf); + image_save_post(reports, ima, ibuf, ok_view, opts, true, filepath, r_colorspace_changed); + BKE_image_release_ibuf(ima, ibuf, lock); + } + ok &= ok_view; + } + + if (is_exr_rr) { + BKE_image_release_ibuf(ima, ibuf, lock); + } + } + /* stereo (multiview) images */ + else if (opts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { + ok = BKE_image_render_write_exr( + reports, rr, opts->filepath, imf, save_as_render, nullptr, layer); + image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed); + BKE_image_release_ibuf(ima, ibuf, lock); + } + else { + ImBuf *ibuf_stereo[2] = {nullptr}; + + unsigned char planes = ibuf->planes; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + /* we need to get the specific per-view buffers */ + BKE_image_release_ibuf(ima, ibuf, lock); + bool stereo_ok = true; + + for (int i = 0; i < 2; i++) { + ImageUser view_iuser; + + if (iuser) { + view_iuser = *iuser; + } + else { + BKE_imageuser_default(&view_iuser); + } + + view_iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) { + int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + view_iuser.view = id; + BKE_image_multilayer_index(rr, &view_iuser); + } + else { + view_iuser.view = i; + BKE_image_multiview_index(ima, &view_iuser); + } + + ibuf = BKE_image_acquire_ibuf(ima, &view_iuser, &lock); + + if (ibuf == nullptr) { + BKE_report( + reports, RPT_ERROR, "Did not write, unexpected error when saving stereo image"); + BKE_image_release_ibuf(ima, ibuf, lock); + stereo_ok = false; + break; + } + + ibuf->planes = planes; + + /* color manage the ImBuf leaving it ready for saving */ + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf); + + BKE_image_format_to_imbuf(colormanaged_ibuf, imf); + IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); + + /* duplicate buffer to prevent locker issue when using render result */ + ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf); + + imbuf_save_post(ibuf, colormanaged_ibuf); + + BKE_image_release_ibuf(ima, ibuf, lock); + } + + if (stereo_ok) { + ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]); + + /* save via traditional path */ + ok = BKE_imbuf_write_as(ibuf, opts->filepath, imf, save_copy); + + IMB_freeImBuf(ibuf); + } + + for (int i = 0; i < 2; i++) { + IMB_freeImBuf(ibuf_stereo[i]); + } + } + } + + if (rr) { + BKE_image_release_renderresult(opts->scene, ima); + } + + return ok; +} + +bool BKE_image_save( + ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts) +{ + ImageUser save_iuser; + BKE_imageuser_default(&save_iuser); + + bool colorspace_changed = false; + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = nullptr; + + if (ima->source == IMA_SRC_TILED) { + /* Verify filepath for tiled images contains a valid UDIM marker. */ + udim_pattern = BKE_image_get_tile_strformat(opts->filepath, &tile_format); + if (tile_format == UDIM_TILE_FORMAT_NONE) { + BKE_reportf(reports, + RPT_ERROR, + "When saving a tiled image, the path '%s' must contain a valid UDIM marker", + opts->filepath); + return false; + } + + /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. + */ + if (iuser == nullptr) { + iuser = &save_iuser; + } + } + + /* Save images */ + bool ok = false; + if (ima->source != IMA_SRC_TILED) { + ok = image_save_single(reports, ima, iuser, opts, &colorspace_changed); + } + else { + char filepath[FILE_MAX]; + BLI_strncpy(filepath, opts->filepath, sizeof(filepath)); + + /* Save all the tiles. */ + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + BKE_image_set_filepath_from_tile_number( + opts->filepath, udim_pattern, tile_format, tile->tile_number); + + iuser->tile = tile->tile_number; + ok = image_save_single(reports, ima, iuser, opts, &colorspace_changed); + if (!ok) { + break; + } + } + BLI_strncpy(ima->filepath, filepath, sizeof(ima->filepath)); + BLI_strncpy(opts->filepath, filepath, sizeof(opts->filepath)); + MEM_freeN(udim_pattern); + } + + if (colorspace_changed) { + BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_COLORMANAGE); + } + + return ok; +} + +/* OpenEXR saving, single and multilayer. */ + +static float *image_exr_from_scene_linear_to_output(float *rect, + const int width, + const int height, + const int channels, + const ImageFormatData *imf, + Vector<float *> &tmp_output_rects) +{ + if (imf == nullptr) { + return rect; + } + + const char *to_colorspace = imf->linear_colorspace_settings.name; + if (to_colorspace[0] == '\0' || IMB_colormanagement_space_name_is_scene_linear(to_colorspace)) { + return rect; + } + + float *output_rect = (float *)MEM_dupallocN(rect); + tmp_output_rects.append(output_rect); + + const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR); + IMB_colormanagement_transform( + output_rect, width, height, channels, from_colorspace, to_colorspace, false); + + return output_rect; +} + +bool BKE_image_render_write_exr(ReportList *reports, + const RenderResult *rr, + const char *filepath, + const ImageFormatData *imf, + const bool save_as_render, + const char *view, + int layer) +{ + void *exrhandle = IMB_exr_get_handle(); + const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16); + const bool multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR); + const bool write_z = !multi_layer && (imf && (imf->flag & R_IMF_FLAG_ZBUF)); + Vector<float *> tmp_output_rects; + + /* Write first layer if not multilayer and no layer was specified. */ + if (!multi_layer && layer == -1) { + layer = 0; + } + + /* First add views since IMB_exr_add_channel checks number of views. */ + const RenderView *first_rview = (const RenderView *)rr->views.first; + if (first_rview && (first_rview->next || first_rview->name[0])) { + LISTBASE_FOREACH (RenderView *, rview, &rr->views) { + if (!view || STREQ(view, rview->name)) { + IMB_exr_add_view(exrhandle, rview->name); + } + } + } + + /* Compositing result. */ + if (rr->have_combined) { + LISTBASE_FOREACH (RenderView *, rview, &rr->views) { + if (!rview->rectf) { + continue; + } + + const char *viewname = rview->name; + if (view) { + if (!STREQ(view, viewname)) { + continue; + } + + viewname = ""; + } + + /* Skip compositing if only a single other layer is requested. */ + if (!multi_layer && layer != 0) { + continue; + } + + float *output_rect = (save_as_render) ? + image_exr_from_scene_linear_to_output( + rview->rectf, rr->rectx, rr->recty, 4, imf, tmp_output_rects) : + rview->rectf; + + for (int a = 0; a < 4; a++) { + char passname[EXR_PASS_MAXNAME]; + char layname[EXR_PASS_MAXNAME]; + const char *chan_id = "RGBA"; + + if (multi_layer) { + RE_render_result_full_channel_name(passname, nullptr, "Combined", nullptr, chan_id, a); + BLI_strncpy(layname, "Composite", sizeof(layname)); + } + else { + passname[0] = chan_id[a]; + passname[1] = '\0'; + layname[0] = '\0'; + } + + IMB_exr_add_channel( + exrhandle, layname, passname, viewname, 4, 4 * rr->rectx, output_rect + a, half_float); + } + + if (write_z && rview->rectz) { + const char *layname = (multi_layer) ? "Composite" : ""; + IMB_exr_add_channel(exrhandle, layname, "Z", viewname, 1, rr->rectx, rview->rectz, false); + } + } + } + + /* Other render layers. */ + int nr = (rr->have_combined) ? 1 : 0; + LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) { + /* Skip other render layers if requested. */ + if (!multi_layer && nr != layer) { + continue; + } + + LISTBASE_FOREACH (RenderPass *, rp, &rl->passes) { + /* Skip non-RGBA and Z passes if not using multi layer. */ + if (!multi_layer && !(STREQ(rp->name, RE_PASSNAME_COMBINED) || STREQ(rp->name, "") || + (STREQ(rp->name, RE_PASSNAME_Z) && write_z))) { + continue; + } + + /* Skip pass if it does not match the requested view(s). */ + const char *viewname = rp->view; + if (view) { + if (!STREQ(view, viewname)) { + continue; + } + + viewname = ""; + } + + /* We only store RGBA passes as half float, for + * others precision loss can be problematic. */ + const bool pass_RGBA = (STR_ELEM(rp->chan_id, "RGB", "RGBA", "R", "G", "B", "A")); + const bool pass_half_float = half_float && pass_RGBA; + + /* Colorspace conversion only happens on RGBA passes. */ + float *output_rect = (save_as_render && pass_RGBA) ? + image_exr_from_scene_linear_to_output( + rp->rect, rr->rectx, rr->recty, 4, imf, tmp_output_rects) : + rp->rect; + + for (int a = 0; a < rp->channels; a++) { + /* Save Combined as RGBA if single layer save. */ + char passname[EXR_PASS_MAXNAME]; + char layname[EXR_PASS_MAXNAME]; + + if (multi_layer) { + RE_render_result_full_channel_name(passname, nullptr, rp->name, nullptr, rp->chan_id, a); + BLI_strncpy(layname, rl->name, sizeof(layname)); + } + else { + passname[0] = rp->chan_id[a]; + passname[1] = '\0'; + layname[0] = '\0'; + } + + IMB_exr_add_channel(exrhandle, + layname, + passname, + viewname, + rp->channels, + rp->channels * rr->rectx, + output_rect + a, + pass_half_float); + } + } + } + + errno = 0; + + BLI_make_existing_file(filepath); + + int compress = (imf ? imf->exr_codec : 0); + bool success = IMB_exr_begin_write( + exrhandle, filepath, rr->rectx, rr->recty, compress, rr->stamp_data); + if (success) { + IMB_exr_write_channels(exrhandle); + } + else { + /* TODO: get the error from openexr's exception. */ + BKE_reportf( + reports, RPT_ERROR, "Error writing render result, %s (see console)", strerror(errno)); + } + + for (float *rect : tmp_output_rects) { + MEM_freeN(rect); + } + + IMB_exr_close(exrhandle); + return success; +} + +/* Render output. */ + +static void image_render_print_save_message(ReportList *reports, const char *name, int ok, int err) +{ + if (ok) { + /* no need to report, just some helpful console info */ + printf("Saved: '%s'\n", name); + } + else { + /* report on error since users will want to know what failed */ + BKE_reportf(reports, RPT_ERROR, "Render error (%s) cannot save: '%s'", strerror(err), name); + } +} + +static int image_render_write_stamp_test(ReportList *reports, + const Scene *scene, + const RenderResult *rr, + ImBuf *ibuf, + const char *name, + const ImageFormatData *imf, + const bool stamp) +{ + int ok; + + if (stamp) { + /* writes the name of the individual cameras */ + ok = BKE_imbuf_write_stamp(scene, rr, ibuf, name, imf); + } + else { + ok = BKE_imbuf_write(ibuf, name, imf); + } + + image_render_print_save_message(reports, name, ok, errno); + + return ok; +} + +bool BKE_image_render_write(ReportList *reports, + RenderResult *rr, + const Scene *scene, + const bool stamp, + const char *filepath_basis) +{ + bool ok = true; + + if (!rr) { + return false; + } + + ImageFormatData image_format; + BKE_image_format_init_for_write(&image_format, scene, nullptr); + + const bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; + const bool is_exr_rr = ELEM( + image_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + RE_HasFloatPixels(rr); + const float dither = scene->r.dither_intensity; + + if (image_format.views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) { + ok = BKE_image_render_write_exr(reports, rr, filepath_basis, &image_format, true, nullptr, -1); + image_render_print_save_message(reports, filepath_basis, ok, errno); + } + + /* mono, legacy code */ + else if (is_mono || (image_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { + int view_id = 0; + for (const RenderView *rv = (const RenderView *)rr->views.first; rv; + rv = rv->next, view_id++) { + char filepath[FILE_MAX]; + if (is_mono) { + STRNCPY(filepath, filepath_basis); + } + else { + BKE_scene_multiview_view_filepath_get(&scene->r, filepath_basis, rv->name, filepath); + } + + if (is_exr_rr) { + ok = BKE_image_render_write_exr(reports, rr, filepath, &image_format, true, rv->name, -1); + image_render_print_save_message(reports, filepath, ok, errno); + + /* optional preview images for exr */ + if (ok && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { + image_format.imtype = R_IMF_IMTYPE_JPEG90; + + if (BLI_path_extension_check(filepath, ".exr")) { + filepath[strlen(filepath) - 4] = 0; + } + BKE_image_path_ensure_ext_from_imformat(filepath, &image_format); + + ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id); + ibuf->planes = 24; + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &image_format); + + ok = image_render_write_stamp_test( + reports, scene, rr, ibuf, filepath, &image_format, stamp); + + IMB_freeImBuf(ibuf); + } + } + else { + ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id); + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &image_format); + + ok = image_render_write_stamp_test( + reports, scene, rr, ibuf, filepath, &image_format, stamp); + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + } + } + } + else { /* R_IMF_VIEWS_STEREO_3D */ + BLI_assert(image_format.views_format == R_IMF_VIEWS_STEREO_3D); + + char filepath[FILE_MAX]; + STRNCPY(filepath, filepath_basis); + + if (image_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + printf("Stereo 3D not supported for MultiLayer image: %s\n", filepath); + } + else { + ImBuf *ibuf_arr[3] = {nullptr}; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id); + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &image_format); + IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); + } + + ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]); + + ok = image_render_write_stamp_test( + reports, scene, rr, ibuf_arr[2], filepath, &image_format, stamp); + + /* optional preview images for exr */ + if (ok && is_exr_rr && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { + image_format.imtype = R_IMF_IMTYPE_JPEG90; + + if (BLI_path_extension_check(filepath, ".exr")) { + filepath[strlen(filepath) - 4] = 0; + } + + BKE_image_path_ensure_ext_from_imformat(filepath, &image_format); + ibuf_arr[2]->planes = 24; + + ok = image_render_write_stamp_test( + reports, scene, rr, ibuf_arr[2], filepath, &image_format, stamp); + } + + /* imbuf knows which rects are not part of ibuf */ + for (i = 0; i < 3; i++) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + } + + BKE_image_format_free(&image_format); + + return ok; +} diff --git a/source/blender/blenkernel/intern/image_test.cc b/source/blender/blenkernel/intern/image_test.cc new file mode 100644 index 00000000000..9c15fc62d21 --- /dev/null +++ b/source/blender/blenkernel/intern/image_test.cc @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_image.h" + +#include "MEM_guardedalloc.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +TEST(udim, image_ensure_tile_token) +{ + auto verify = [](const char *original, const char *expected) { + char result[FILE_MAX]; + + BLI_strncpy(result, original, sizeof(result)); + BKE_image_ensure_tile_token(result); + EXPECT_STREQ(result, expected); + }; + + /* Already present tokens. */ + verify("test.<UDIM>.png", "test.<UDIM>.png"); + verify("test.<UVTILE>.png", "test.<UVTILE>.png"); + + /* UDIM pattern detection. */ + verify("test.1002.png", "test.<UDIM>.png"); + verify("test-1002-ao.png", "test-<UDIM>-ao.png"); + verify("test_1002_ao.png", "test_<UDIM>_ao.png"); + verify("test.1002.ver0023.png", "test.<UDIM>.ver0023.png"); + verify("test.ver0023.1002.png", "test.ver0023.<UDIM>.png"); + verify("1002test.png", "<UDIM>test.png"); + verify("test1002.png", "test<UDIM>.png"); + + /* UVTILE pattern detection. */ + verify("uv-test.u2_v10.png", "uv-test.<UVTILE>.png"); + verify("uv-test-u2_v10-ao.png", "uv-test-<UVTILE>-ao.png"); + verify("uv-test_u2_v10_ao.png", "uv-test_<UVTILE>_ao.png"); + verify("uv-test.u10_v100.png", "uv-test.<UVTILE>.png"); + verify("u_v-test.u2_v10.png", "u_v-test.<UVTILE>.png"); + verify("u2_v10uv-test.png", "<UVTILE>uv-test.png"); + verify("u2_v10u_v-test.png", "<UVTILE>u_v-test.png"); + + /* Incorrect patterns. */ + for (const char *incorrect : {"test.123.png", + "test.12345.png", + "test.uv.png", + "test.u1v.png", + "test.uv1.png", + "test.u_v.png", + "test.u1_v.png", + "test.u_v2.png", + "test.u2v3.png", + "test.u123_v1.png", + "test.u1_v12345.png"}) { + /* These should not result in modifications happening. */ + verify(incorrect, incorrect); + } +} + +TEST(udim, image_get_tile_strformat) +{ + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern; + + /* Parameter validation. */ + udim_pattern = BKE_image_get_tile_strformat(nullptr, &tile_format); + EXPECT_EQ(udim_pattern, nullptr); + + udim_pattern = BKE_image_get_tile_strformat("", nullptr); + EXPECT_EQ(udim_pattern, nullptr); + + /* Typical usage. */ + udim_pattern = BKE_image_get_tile_strformat("", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_NONE); + EXPECT_EQ(udim_pattern, nullptr); + + udim_pattern = BKE_image_get_tile_strformat("test.<UNKNOWN>.png", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_NONE); + EXPECT_EQ(udim_pattern, nullptr); + + udim_pattern = BKE_image_get_tile_strformat("test.<UDIM>.png", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_UDIM); + EXPECT_STREQ(udim_pattern, "test.%d.png"); + MEM_freeN(udim_pattern); + + udim_pattern = BKE_image_get_tile_strformat("test.<UVTILE>.png", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_UVTILE); + EXPECT_STREQ(udim_pattern, "test.u%d_v%d.png"); + MEM_freeN(udim_pattern); +} + +TEST(udim, image_get_tile_number_from_filepath) +{ + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern; + int tile_number; + + udim_pattern = BKE_image_get_tile_strformat("test.<UDIM>.png", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_UDIM); + EXPECT_NE(udim_pattern, nullptr); + + /* Parameter validation. */ + EXPECT_FALSE( + BKE_image_get_tile_number_from_filepath(nullptr, udim_pattern, tile_format, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "test.1004.png", nullptr, tile_format, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "test.1004.png", udim_pattern, UDIM_TILE_FORMAT_NONE, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "test.1004.png", udim_pattern, tile_format, nullptr)); + + /* UDIM tile format tests. */ + EXPECT_TRUE(BKE_image_get_tile_number_from_filepath( + "test.1004.png", udim_pattern, tile_format, &tile_number)); + EXPECT_EQ(tile_number, 1004); + + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "has_no_number.png", udim_pattern, tile_format, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "test.X.png", udim_pattern, tile_format, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "wrong.1004.png", udim_pattern, tile_format, &tile_number)); + + MEM_freeN(udim_pattern); + + /* UVTILE tile format tests. */ + udim_pattern = BKE_image_get_tile_strformat("test.<UVTILE>.png", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_UVTILE); + EXPECT_NE(udim_pattern, nullptr); + + EXPECT_TRUE(BKE_image_get_tile_number_from_filepath( + "test.u2_v2.png", udim_pattern, tile_format, &tile_number)); + EXPECT_EQ(tile_number, 1012); + + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "has_no_number.png", udim_pattern, tile_format, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "test.u1_vX.png", udim_pattern, tile_format, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "test.uX_v1.png", udim_pattern, tile_format, &tile_number)); + EXPECT_FALSE(BKE_image_get_tile_number_from_filepath( + "wrong.u2_v2.png", udim_pattern, tile_format, &tile_number)); + + MEM_freeN(udim_pattern); +} + +TEST(udim, image_set_filepath_from_tile_number) +{ + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern; + + udim_pattern = BKE_image_get_tile_strformat("test.<UDIM>.png", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_UDIM); + EXPECT_NE(udim_pattern, nullptr); + + char filepath[FILE_MAX]; + + /* Parameter validation. */ + BLI_strncpy(filepath, "xxxx", FILE_MAX); + + BKE_image_set_filepath_from_tile_number(nullptr, udim_pattern, tile_format, 1028); + BKE_image_set_filepath_from_tile_number(filepath, nullptr, tile_format, 1028); + EXPECT_STREQ(filepath, "xxxx"); + BKE_image_set_filepath_from_tile_number(filepath, udim_pattern, UDIM_TILE_FORMAT_NONE, 1028); + EXPECT_STREQ(filepath, "xxxx"); + + /* UDIM tile format tests. */ + BKE_image_set_filepath_from_tile_number(filepath, udim_pattern, tile_format, 1028); + EXPECT_STREQ(filepath, "test.1028.png"); + MEM_freeN(udim_pattern); + + /* UVTILE tile format tests. */ + udim_pattern = BKE_image_get_tile_strformat("test.<UVTILE>.png", &tile_format); + EXPECT_EQ(tile_format, UDIM_TILE_FORMAT_UVTILE); + EXPECT_NE(udim_pattern, nullptr); + + BKE_image_set_filepath_from_tile_number(filepath, udim_pattern, tile_format, 1028); + EXPECT_STREQ(filepath, "test.u8_v3.png"); + MEM_freeN(udim_pattern); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 11d6e6ef973..5247e9f358b 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -2171,16 +2171,14 @@ void BKE_keyblock_convert_from_mesh(Mesh *me, Key *key, KeyBlock *kb) BKE_keyblock_update_from_mesh(me, kb); } -void BKE_keyblock_convert_to_mesh(KeyBlock *kb, Mesh *me) +void BKE_keyblock_convert_to_mesh(KeyBlock *kb, struct MVert *mvert, int totvert) { - MVert *mvert; const float(*fp)[3]; int a, tot; - mvert = me->mvert; fp = kb->data; - tot = min_ii(kb->totelem, me->totvert); + tot = min_ii(kb->totelem, totvert); for (a = 0; a < tot; a++, fp++, mvert++) { copy_v3_v3(mvert->co, *fp); @@ -2193,68 +2191,77 @@ void BKE_keyblock_mesh_calc_normals(struct KeyBlock *kb, float (*r_polynors)[3], float (*r_loopnors)[3]) { - /* We use a temp, shallow copy of mesh to work. */ - Mesh me; - bool free_polynors = false; - if (r_vertnors == NULL && r_polynors == NULL && r_loopnors == NULL) { return; } - me = *mesh; - me.mvert = MEM_dupallocN(mesh->mvert); - CustomData_reset(&me.vdata); - CustomData_reset(&me.edata); - CustomData_reset(&me.pdata); - CustomData_reset(&me.ldata); - CustomData_reset(&me.fdata); - - BKE_keyblock_convert_to_mesh(kb, &me); - - if (r_polynors == NULL && r_loopnors != NULL) { - r_polynors = MEM_mallocN(sizeof(float[3]) * me.totpoly, __func__); - free_polynors = true; - } - - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh); - if (r_vertnors) { - memcpy(r_vertnors, vert_normals, sizeof(float[3]) * me.totvert); - } - - const float(*face_normals)[3] = BKE_mesh_poly_normals_ensure(mesh); - memcpy(r_polynors, face_normals, sizeof(float[3]) * me.totpoly); - - if (r_loopnors) { + MVert *mvert = MEM_dupallocN(mesh->mvert); + BKE_keyblock_convert_to_mesh(kb, mesh->mvert, mesh->totvert); + + const bool loop_normals_needed = r_loopnors != NULL; + const bool vert_normals_needed = r_vertnors != NULL || loop_normals_needed; + const bool poly_normals_needed = r_polynors != NULL || vert_normals_needed || + loop_normals_needed; + + float(*vert_normals)[3] = r_vertnors; + float(*poly_normals)[3] = r_polynors; + bool free_vert_normals = false; + bool free_poly_normals = false; + if (vert_normals_needed && r_vertnors == NULL) { + vert_normals = MEM_malloc_arrayN(mesh->totvert, sizeof(float[3]), __func__); + free_vert_normals = true; + } + if (poly_normals_needed && r_polynors == NULL) { + poly_normals = MEM_malloc_arrayN(mesh->totpoly, sizeof(float[3]), __func__); + free_poly_normals = true; + } + + if (poly_normals_needed) { + BKE_mesh_calc_normals_poly(mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + poly_normals); + } + if (vert_normals_needed) { + BKE_mesh_calc_normals_poly_and_vertex(mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + poly_normals, + vert_normals); + } + if (loop_normals_needed) { short(*clnors)[2] = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); /* May be NULL. */ - - BKE_mesh_normals_loop_split(me.mvert, + BKE_mesh_normals_loop_split(mesh->mvert, vert_normals, - me.totvert, - me.medge, - me.totedge, - me.mloop, + mesh->totvert, + mesh->medge, + mesh->totedge, + mesh->mloop, r_loopnors, - me.totloop, - me.mpoly, - face_normals, - me.totpoly, - (me.flag & ME_AUTOSMOOTH) != 0, - me.smoothresh, + mesh->totloop, + mesh->mpoly, + poly_normals, + mesh->totpoly, + (mesh->flag & ME_AUTOSMOOTH) != 0, + mesh->smoothresh, NULL, clnors, NULL); } - CustomData_free(&me.vdata, me.totvert); - CustomData_free(&me.edata, me.totedge); - CustomData_free(&me.pdata, me.totpoly); - CustomData_free(&me.ldata, me.totloop); - CustomData_free(&me.fdata, me.totface); - MEM_freeN(me.mvert); - - if (free_polynors) { - MEM_freeN(r_polynors); + if (free_vert_normals) { + MEM_freeN(vert_normals); + } + if (free_poly_normals) { + MEM_freeN(poly_normals); } + MEM_freeN(mvert); } /************************* raw coords ************************/ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 701788a4e29..64ebb08f5d0 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -417,6 +417,39 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, } BLI_assert(id_hierarchy_root != NULL); + LinkNode *relinked_ids = NULL; + /* Still checking the whole Main, that way we can tag other local IDs as needing to be + * remapped to use newly created overriding IDs, if needed. */ + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + ID *other_id; + /* In case we created new overrides as 'no main', they are not accessible directly in this + * loop, but we can get to them through their reference's `newid` pointer. */ + if (do_no_main && id->lib == id_root_reference->lib && id->newid != NULL) { + other_id = id->newid; + /* Otherwise we cannot properly distinguish between IDs that are actually from the + * linked library (and should not be remapped), and IDs that are overrides re-generated + * from the reference from the linked library, and must therefore be remapped. + * + * This is reset afterwards at the end of this loop. */ + other_id->lib = NULL; + } + else { + other_id = id; + } + + /* If other ID is a linked one, but not from the same library as our reference, then we + * consider we should also relink it, as part of recursive resync. */ + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != id_root_reference->lib) { + BLI_linklist_prepend(&relinked_ids, other_id); + } + if (other_id != id) { + other_id->lib = id_root_reference->lib; + } + } + FOREACH_MAIN_ID_END; + + struct IDRemapper *id_remapper = BKE_id_remapper_create(); for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { reference_id = todo_id_iter->data; ID *local_id = reference_id->newid; @@ -427,55 +460,25 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, local_id->override_library->hierarchy_root = id_hierarchy_root; + BKE_id_remapper_add(id_remapper, reference_id, local_id); + Key *reference_key, *local_key = NULL; if ((reference_key = BKE_key_from_id(reference_id)) != NULL) { local_key = BKE_key_from_id(reference_id->newid); BLI_assert(local_key != NULL); - } - - /* Still checking the whole Main, that way we can tag other local IDs as needing to be - * remapped to use newly created overriding IDs, if needed. */ - ID *id; - FOREACH_MAIN_ID_BEGIN (bmain, id) { - ID *other_id; - /* In case we created new overrides as 'no main', they are not accessible directly in this - * loop, but we can get to them through their reference's `newid` pointer. */ - if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) { - other_id = id->newid; - /* Otherwise we cannot properly distinguish between IDs that are actually from the - * linked library (and should not be remapped), and IDs that are overrides re-generated - * from the reference from the linked library, and must therefore be remapped. - * - * This is reset afterwards at the end of this loop. */ - other_id->lib = NULL; - } - else { - other_id = id; - } - /* If other ID is a linked one, but not from the same library as our reference, then we - * consider we should also remap it, as part of recursive resync. */ - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib && - other_id != local_id) { - BKE_libblock_relink_ex(bmain, - other_id, - reference_id, - local_id, - ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); - if (reference_key != NULL) { - BKE_libblock_relink_ex(bmain, - other_id, - &reference_key->id, - &local_key->id, - ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); - } - } - if (other_id != id) { - other_id->lib = reference_id->lib; - } + BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); } - FOREACH_MAIN_ID_END; } + + BKE_libblock_relink_multiple(bmain, + relinked_ids, + ID_REMAP_TYPE_REMAP, + id_remapper, + ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); + + BKE_id_remapper_free(id_remapper); + BLI_linklist_free(relinked_ids, NULL); } else { /* We need to cleanup potentially already created data. */ @@ -1353,8 +1356,9 @@ static void lib_override_library_remap(Main *bmain, { ID *id; struct IDRemapper *remapper = BKE_id_remapper_create(); - FOREACH_MAIN_ID_BEGIN (bmain, id) { + LinkNode *nomain_ids = NULL; + FOREACH_MAIN_ID_BEGIN (bmain, id) { if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); @@ -1363,26 +1367,28 @@ static void lib_override_library_remap(Main *bmain, } BKE_id_remapper_add(remapper, id_override_old, id_override_new); - /* Remap no-main override IDs we just created too. */ - GHashIterator linkedref_to_old_override_iter; - GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { - ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); - if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) { - continue; - } - - BKE_libblock_relink_ex(bmain, - id_override_old_iter, - id_override_old, - id_override_new, - ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); - } } } FOREACH_MAIN_ID_END; + /* Remap no-main override IDs we just created too. */ + GHashIterator linkedref_to_old_override_iter; + GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { + ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); + if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) { + continue; + } + + BLI_linklist_prepend(&nomain_ids, id_override_old_iter); + } + /* Remap all IDs to use the new override. */ BKE_libblock_remap_multiple(bmain, remapper, 0); + BKE_libblock_relink_multiple(bmain, + nomain_ids, + ID_REMAP_TYPE_REMAP, + remapper, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); BKE_id_remapper_free(remapper); } @@ -1641,6 +1647,8 @@ static bool lib_override_library_resync(Main *bmain, BKE_main_collection_sync(bmain); + LinkNode *id_override_old_list = NULL; + /* We need to apply override rules in a separate loop, after all ID pointers have been properly * remapped, and all new local override IDs have gotten their proper original names, otherwise * override operations based on those ID names would fail. */ @@ -1690,19 +1698,27 @@ static bool lib_override_library_resync(Main *bmain, RNA_OVERRIDE_APPLY_FLAG_NOP); } - /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages - * of the old one. - * This is necessary in case said old ID is not in Main anymore. */ - BKE_libblock_relink_ex(bmain, - id_override_old, - NULL, - NULL, - ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); - id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT; + BLI_linklist_prepend(&id_override_old_list, id_override_old); } } FOREACH_MAIN_ID_END; + /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages + * of the old one. + * This is necessary in case said old ID is not in Main anymore. */ + struct IDRemapper *id_remapper = BKE_id_remapper_create(); + BKE_libblock_relink_multiple(bmain, + id_override_old_list, + ID_REMAP_TYPE_CLEANUP, + id_remapper, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); + for (LinkNode *ln_iter = id_override_old_list; ln_iter != NULL; ln_iter = ln_iter->next) { + ID *id_override_old = ln_iter->link; + id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT; + } + BKE_id_remapper_free(id_remapper); + BLI_linklist_free(id_override_old_list, NULL); + /* Delete old override IDs. * Note that we have to use tagged group deletion here, since ID deletion also uses * LIB_TAG_DOIT. This improves performances anyway, so everything is fine. */ diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 24e7178dd63..1a6fcf5ff43 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -8,6 +8,7 @@ #include "CLG_log.h" +#include "BLI_linklist.h" #include "BLI_utildefines.h" #include "DNA_collection_types.h" @@ -681,6 +682,126 @@ void BKE_libblock_unlink(Main *bmain, * ... sigh */ +typedef struct LibblockRelinkMultipleUserData { + Main *bmain; + LinkNode *ids; +} LibBlockRelinkMultipleUserData; + +static void libblock_relink_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_data) +{ + LibBlockRelinkMultipleUserData *data = user_data; + Main *bmain = data->bmain; + LinkNode *ids = data->ids; + + BLI_assert(old_id != NULL); + BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); + BLI_assert(old_id != new_id); + + bool is_object_update_processed = false; + for (LinkNode *ln_iter = ids; ln_iter != NULL; ln_iter = ln_iter->next) { + ID *id_iter = ln_iter->link; + + /* Some after-process updates. + * This is a bit ugly, but cannot see a way to avoid it. + * Maybe we should do a per-ID callback for this instead? + */ + switch (GS(id_iter->name)) { + case ID_SCE: + case ID_GR: { + /* NOTE: here we know which collection we have affected, so at lest for NULL children + * detection we can only process that one. + * This is also a required fix in case `id` would not be in Main anymore, which can happen + * e.g. when called from `id_delete`. */ + Collection *owner_collection = (GS(id_iter->name) == ID_GR) ? + (Collection *)id_iter : + ((Scene *)id_iter)->master_collection; + switch (GS(old_id->name)) { + case ID_OB: + if (!is_object_update_processed) { + libblock_remap_data_postprocess_object_update( + bmain, (Object *)old_id, (Object *)new_id); + is_object_update_processed = true; + } + break; + case ID_GR: + libblock_remap_data_postprocess_collection_update( + bmain, owner_collection, (Collection *)old_id, (Collection *)new_id); + break; + default: + break; + } + break; + } + case ID_OB: + if (new_id != NULL) { /* Only affects us in case obdata was relinked (changed). */ + libblock_remap_data_postprocess_obdata_relink(bmain, (Object *)id_iter, new_id); + } + break; + default: + break; + } + } +} + +void BKE_libblock_relink_multiple(Main *bmain, + LinkNode *ids, + const eIDRemapType remap_type, + struct IDRemapper *id_remapper, + const short remap_flags) +{ + BLI_assert(remap_type == ID_REMAP_TYPE_REMAP || BKE_id_remapper_is_empty(id_remapper)); + + for (LinkNode *ln_iter = ids; ln_iter != NULL; ln_iter = ln_iter->next) { + ID *id_iter = ln_iter->link; + libblock_remap_data(bmain, id_iter, remap_type, id_remapper, remap_flags); + } + + switch (remap_type) { + case ID_REMAP_TYPE_REMAP: { + LibBlockRelinkMultipleUserData user_data = {0}; + user_data.bmain = bmain; + user_data.ids = ids; + + BKE_id_remapper_iter(id_remapper, libblock_relink_foreach_idpair_cb, &user_data); + break; + } + case ID_REMAP_TYPE_CLEANUP: { + bool is_object_update_processed = false; + for (LinkNode *ln_iter = ids; ln_iter != NULL; ln_iter = ln_iter->next) { + ID *id_iter = ln_iter->link; + + switch (GS(id_iter->name)) { + case ID_SCE: + case ID_GR: { + /* NOTE: here we know which collection we have affected, so at lest for NULL children + * detection we can only process that one. + * This is also a required fix in case `id` would not be in Main anymore, which can + * happen e.g. when called from `id_delete`. */ + Collection *owner_collection = (GS(id_iter->name) == ID_GR) ? + (Collection *)id_iter : + ((Scene *)id_iter)->master_collection; + /* No choice but to check whole objects once, and all children collections. */ + libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL); + if (!is_object_update_processed) { + libblock_remap_data_postprocess_object_update(bmain, NULL, NULL); + is_object_update_processed = true; + } + break; + } + default: + break; + } + } + + break; + } + default: + BLI_assert_unreachable(); + } + + DEG_relations_tag_update(bmain); +} + void BKE_libblock_relink_ex( Main *bmain, void *idv, void *old_idv, void *new_idv, const short remap_flags) { @@ -690,13 +811,15 @@ void BKE_libblock_relink_ex( ID *id = idv; ID *old_id = old_idv; ID *new_id = new_idv; + LinkNode ids = {.next = NULL, .link = idv}; /* No need to lock here, we are only affecting given ID, not bmain database. */ struct IDRemapper *id_remapper = BKE_id_remapper_create(); eIDRemapType remap_type = ID_REMAP_TYPE_REMAP; - BLI_assert(id); - if (old_id) { + BLI_assert(id != NULL); + UNUSED_VARS_NDEBUG(id); + if (old_id != NULL) { BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); BLI_assert(old_id != new_id); BKE_id_remapper_add(id_remapper, old_id, new_id); @@ -706,56 +829,19 @@ void BKE_libblock_relink_ex( remap_type = ID_REMAP_TYPE_CLEANUP; } - libblock_remap_data(bmain, id, remap_type, id_remapper, remap_flags); - BKE_id_remapper_free(id_remapper); - - /* Some after-process updates. - * This is a bit ugly, but cannot see a way to avoid it. - * Maybe we should do a per-ID callback for this instead? - */ - switch (GS(id->name)) { - case ID_SCE: - case ID_GR: { - /* NOTE: here we know which collection we have affected, so at lest for NULL children - * detection we can only process that one. - * This is also a required fix in case `id` would not be in Main anymore, which can happen - * e.g. when called from `id_delete`. */ - Collection *owner_collection = (GS(id->name) == ID_GR) ? (Collection *)id : - ((Scene *)id)->master_collection; - if (old_id) { - switch (GS(old_id->name)) { - case ID_OB: - libblock_remap_data_postprocess_object_update( - bmain, (Object *)old_id, (Object *)new_id); - break; - case ID_GR: - libblock_remap_data_postprocess_collection_update( - bmain, owner_collection, (Collection *)old_id, (Collection *)new_id); - break; - default: - break; - } - } - else { - /* No choice but to check whole objects/collections. */ - libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL); - libblock_remap_data_postprocess_object_update(bmain, NULL, NULL); - } - break; - } - case ID_OB: - if (new_id) { /* Only affects us in case obdata was relinked (changed). */ - libblock_remap_data_postprocess_obdata_relink(bmain, (Object *)id, new_id); - } - break; - default: - break; - } + BKE_libblock_relink_multiple(bmain, &ids, remap_type, id_remapper, remap_flags); - DEG_relations_tag_update(bmain); + BKE_id_remapper_free(id_remapper); } -static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag); +typedef struct RelinkToNewIDData { + LinkNode *ids; + struct IDRemapper *id_remapper; +} RelinkToNewIDData; + +static void libblock_relink_to_newid_prepare_data(Main *bmain, + ID *id, + RelinkToNewIDData *relink_data); static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) { const int cb_flag = cb_data->cb_flag; @@ -764,35 +850,34 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) } Main *bmain = cb_data->bmain; - ID *id_owner = cb_data->id_owner; ID **id_pointer = cb_data->id_pointer; ID *id = *id_pointer; + RelinkToNewIDData *relink_data = (RelinkToNewIDData *)cb_data->user_data; + if (id) { - const int remap_flag = POINTER_AS_INT(cb_data->user_data); /* See: NEW_ID macro */ if (id->newid != NULL) { - const int remap_flag_final = remap_flag | ID_REMAP_SKIP_INDIRECT_USAGE | - ID_REMAP_SKIP_OVERRIDE_LIBRARY; - BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, (short)remap_flag_final); + BKE_id_remapper_add(relink_data->id_remapper, id, id->newid); id = id->newid; } if (id->tag & LIB_TAG_NEW) { - id->tag &= ~LIB_TAG_NEW; - libblock_relink_to_newid(bmain, id, remap_flag); + libblock_relink_to_newid_prepare_data(bmain, id, relink_data); } } return IDWALK_RET_NOP; } -static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) +static void libblock_relink_to_newid_prepare_data(Main *bmain, + ID *id, + RelinkToNewIDData *relink_data) { if (ID_IS_LINKED(id)) { return; } id->tag &= ~LIB_TAG_NEW; - BKE_library_foreach_ID_link( - bmain, id, id_relink_to_newid_looper, POINTER_FROM_INT(remap_flag), 0); + BLI_linklist_prepend(&relink_data->ids, id); + BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper, relink_data, 0); } void BKE_libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) @@ -803,8 +888,15 @@ void BKE_libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) /* We do not want to have those cached relationship data here. */ BLI_assert(bmain->relations == NULL); - BKE_layer_collection_resync_forbid(); - libblock_relink_to_newid(bmain, id, remap_flag); - BKE_layer_collection_resync_allow(); - BKE_main_collection_sync_remap(bmain); + RelinkToNewIDData relink_data = {.ids = NULL, .id_remapper = BKE_id_remapper_create()}; + + libblock_relink_to_newid_prepare_data(bmain, id, &relink_data); + + const short remap_flag_final = remap_flag | ID_REMAP_SKIP_INDIRECT_USAGE | + ID_REMAP_SKIP_OVERRIDE_LIBRARY; + BKE_libblock_relink_multiple( + bmain, relink_data.ids, ID_REMAP_TYPE_REMAP, relink_data.id_remapper, remap_flag_final); + + BKE_id_remapper_free(relink_data.id_remapper); + BLI_linklist_free(relink_data.ids, NULL); } diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 824c1ab1b90..b30d8f92cc6 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -199,7 +199,7 @@ static void mesh_foreach_path(ID *id, BPathForeachPathData *bpath_data) { Mesh *me = (Mesh *)id; if (me->ldata.external) { - BKE_bpath_foreach_path_fixed_process(bpath_data, me->ldata.external->filename); + BKE_bpath_foreach_path_fixed_process(bpath_data, me->ldata.external->filepath); } } @@ -1166,6 +1166,7 @@ BMesh *BKE_mesh_to_bmesh(Mesh *me, { BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = false; + bmesh_from_mesh_params.calc_vert_normal = false; bmesh_from_mesh_params.add_key_index = add_key_index; bmesh_from_mesh_params.use_shapekey = true; bmesh_from_mesh_params.active_shapekey = ob->shapenr; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 1542f7119d1..40c6fbcf67e 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -1308,7 +1308,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, if (build_shapekey_layers && me->key && (kb = (KeyBlock *)BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) { - BKE_keyblock_convert_to_mesh(kb, me); + BKE_keyblock_convert_to_mesh(kb, me->mvert, me->totvert); } Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index da0bd1f021d..6c5a5de31fc 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -695,7 +695,7 @@ static void bm_corners_to_loops_ex(ID *id, if (CustomData_external_test(fdata, CD_MDISPS)) { if (id && fdata->external) { - CustomData_external_add(ldata, id, CD_MDISPS, totloop, fdata->external->filename); + CustomData_external_add(ldata, id, CD_MDISPS, totloop, fdata->external->filepath); } } diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index e9c26c80141..9c4098e2db6 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -561,7 +561,7 @@ void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map, typedef bool (*MeshRemap_CheckIslandBoundary)(const struct MPoly *mpoly, const struct MLoop *mloop, const struct MEdge *medge, - const int nbr_edge_users, + const int edge_user_count, const struct MPoly *mpoly_array, const struct MeshElemMap *edge_poly_map, void *user_data); @@ -764,14 +764,14 @@ static void poly_edge_loop_islands_calc(const MEdge *medge, static bool poly_is_island_boundary_smooth_cb(const MPoly *mp, const MLoop *UNUSED(ml), const MEdge *me, - const int nbr_edge_users, + const int edge_user_count, const MPoly *mpoly_array, const MeshElemMap *edge_poly_map, void *UNUSED(user_data)) { /* Edge is sharp if one of its polys is flat, or edge itself is sharp, * or edge is not used by exactly two polygons. */ - if ((mp->flag & ME_SMOOTH) && !(me->flag & ME_SHARP) && (nbr_edge_users == 2)) { + if ((mp->flag & ME_SMOOTH) && !(me->flag & ME_SHARP) && (edge_user_count == 2)) { /* In that case, edge appears to be smooth, but we need to check its other poly too. */ const MPoly *mp_other = (mp == &mpoly_array[edge_poly_map->indices[0]]) ? &mpoly_array[edge_poly_map->indices[1]] : @@ -935,7 +935,7 @@ typedef struct MeshCheckIslandBoundaryUv { static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp), const MLoop *ml, const MEdge *me, - const int UNUSED(nbr_edge_users), + const int UNUSED(edge_user_count), const MPoly *UNUSED(mpoly_array), const MeshElemMap *UNUSED(edge_poly_map), void *user_data) diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index da23aea43f3..9202690b3c9 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.c +++ b/source/blender/blenkernel/intern/mesh_mirror.c @@ -46,6 +46,7 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mm &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, .cd_mask_extra = {.vmask = CD_MASK_ORIGINDEX, .emask = CD_MASK_ORIGINDEX, .pmask = CD_MASK_ORIGINDEX}, @@ -92,6 +93,7 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain, }, &(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, .cd_mask_extra = { .vmask = CD_MASK_SHAPEKEY, diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 1c2a903d8c3..4448bedb57a 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -306,14 +306,14 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn( } } -static void mesh_calc_normals_poly_and_vertex(const MVert *mvert, - const int mvert_len, - const MLoop *mloop, - const int UNUSED(mloop_len), - const MPoly *mpoly, - const int mpoly_len, - float (*r_poly_normals)[3], - float (*r_vert_normals)[3]) +void BKE_mesh_calc_normals_poly_and_vertex(const MVert *mvert, + const int mvert_len, + const MLoop *mloop, + const int UNUSED(mloop_len), + const MPoly *mpoly, + const int mpoly_len, + float (*r_poly_normals)[3], + float (*r_vert_normals)[3]) { TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); @@ -372,14 +372,14 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] vert_normals = BKE_mesh_vertex_normals_for_write(&mesh_mutable); poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable); - mesh_calc_normals_poly_and_vertex(mesh_mutable.mvert, - mesh_mutable.totvert, - mesh_mutable.mloop, - mesh_mutable.totloop, - mesh_mutable.mpoly, - mesh_mutable.totpoly, - poly_normals, - vert_normals); + BKE_mesh_calc_normals_poly_and_vertex(mesh_mutable.mvert, + mesh_mutable.totvert, + mesh_mutable.mloop, + mesh_mutable.totloop, + mesh_mutable.mpoly, + mesh_mutable.totpoly, + poly_normals, + vert_normals); BKE_mesh_vertex_normals_clear_dirty(&mesh_mutable); BKE_mesh_poly_normals_clear_dirty(&mesh_mutable); @@ -613,19 +613,19 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space, /* Compute ref alpha, average angle of all available edge vectors to lnor. */ if (edge_vectors) { float alpha = 0.0f; - int nbr = 0; + int count = 0; while (!BLI_stack_is_empty(edge_vectors)) { const float *vec = (const float *)BLI_stack_peek(edge_vectors); alpha += saacosf(dot_v3v3(vec, lnor)); BLI_stack_discard(edge_vectors); - nbr++; + count++; } - /* NOTE: In theory, this could be `nbr > 2`, + /* NOTE: In theory, this could be `count > 2`, * but there is one case where we only have two edges for two loops: * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.). */ - BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop. */ - lnor_space->ref_alpha = alpha / (float)nbr; + BLI_assert(count >= 2); /* This piece of code shall only be called for more than one loop. */ + lnor_space->ref_alpha = alpha / (float)count; } else { lnor_space->ref_alpha = (saacosf(dot_v3v3(vec_ref, lnor)) + @@ -1134,7 +1134,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli /* We validate clnors data on the fly - cheapest way to do! */ int clnors_avg[2] = {0, 0}; short(*clnor_ref)[2] = nullptr; - int clnors_nbr = 0; + int clnors_count = 0; bool clnors_invalid = false; /* Temp loop normal stack. */ @@ -1194,7 +1194,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli if (clnors_data) { /* Accumulate all clnors, if they are not all equal we have to fix that! */ short(*clnor)[2] = &clnors_data[mlfan_vert_index]; - if (clnors_nbr) { + if (clnors_count) { clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); } else { @@ -1202,7 +1202,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli } clnors_avg[0] += (*clnor)[0]; clnors_avg[1] += (*clnor)[1]; - clnors_nbr++; + clnors_count++; /* We store here a pointer to all custom lnors processed. */ BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); } @@ -1262,8 +1262,8 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli if (clnors_invalid) { short *clnor; - clnors_avg[0] /= clnors_nbr; - clnors_avg[1] /= clnors_nbr; + clnors_avg[0] /= clnors_count; + clnors_avg[1] /= clnors_count; /* Fix/update all clnors of this fan with computed average value. */ if (G.debug & G_DEBUG) { printf("Invalid clnors in this fan!\n"); @@ -1952,7 +1952,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, BLI_BITMAP_DISABLE(done_loops, i); } else { - int nbr_nors = 0; + int avg_nor_count = 0; float avg_nor[3]; short clnor_data_tmp[2], *clnor_data; @@ -1962,7 +1962,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, const int nidx = use_vertices ? (int)mloops[lidx].v : lidx; float *nor = r_custom_loopnors[nidx]; - nbr_nors++; + avg_nor_count++; add_v3_v3(avg_nor, nor); BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]); @@ -1970,7 +1970,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, BLI_BITMAP_DISABLE(done_loops, lidx); } - mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors); + mul_v3_fl(avg_nor, 1.0f / (float)avg_nor_count); BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], avg_nor, clnor_data_tmp); while ((clnor_data = (short *)BLI_SMALLSTACK_POP(clnors_data))) { @@ -2088,8 +2088,8 @@ void BKE_mesh_normals_loop_to_vertex(const int numVerts, const float (*clnors)[3], float (*r_vert_clnors)[3]) { - int *vert_loops_nbr = (int *)MEM_calloc_arrayN( - (size_t)numVerts, sizeof(*vert_loops_nbr), __func__); + int *vert_loops_count = (int *)MEM_calloc_arrayN( + (size_t)numVerts, sizeof(*vert_loops_count), __func__); copy_vn_fl((float *)r_vert_clnors, 3 * numVerts, 0.0f); @@ -2099,14 +2099,14 @@ void BKE_mesh_normals_loop_to_vertex(const int numVerts, const uint v = ml->v; add_v3_v3(r_vert_clnors[v], clnors[i]); - vert_loops_nbr[v]++; + vert_loops_count[v]++; } for (i = 0; i < numVerts; i++) { - mul_v3_fl(r_vert_clnors[i], 1.0f / (float)vert_loops_nbr[i]); + mul_v3_fl(r_vert_clnors[i], 1.0f / (float)vert_loops_count[i]); } - MEM_freeN(vert_loops_nbr); + MEM_freeN(vert_loops_count); } #undef LNOR_SPACE_TRIGO_THRESHOLD diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index b34a241bca8..3c7992ec3d7 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -401,6 +401,7 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; + bmesh_from_mesh_params.calc_vert_normal = true; BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params); BMVert *v; diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index fb526354305..6af765d7de5 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -577,7 +577,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, else if (mp->loopstart + mp->totloop > totloop) { /* Invalid loop data. */ PRINT_ERR( - "\tPoly %u uses loops out of range (loopstart: %d, loopend: %d, max nbr of loops: %u)", + "\tPoly %u uses loops out of range " + "(loopstart: %d, loopend: %d, max number of loops: %u)", sp->index, mp->loopstart, mp->loopstart + mp->totloop - 1, @@ -1049,41 +1050,40 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata, bool BKE_mesh_validate(Mesh *me, const bool do_verbose, const bool cddata_check_mask) { - bool is_valid = true; bool changed; if (do_verbose) { CLOG_INFO(&LOG, 0, "MESH: %s", me->id.name + 2); } - is_valid &= BKE_mesh_validate_all_customdata(&me->vdata, - me->totvert, - &me->edata, - me->totedge, - &me->ldata, - me->totloop, - &me->pdata, - me->totpoly, - cddata_check_mask, - do_verbose, - true, - &changed); - - is_valid &= BKE_mesh_validate_arrays(me, - me->mvert, - me->totvert, - me->medge, - me->totedge, - me->mface, - me->totface, - me->mloop, - me->totloop, - me->mpoly, - me->totpoly, - me->dvert, - do_verbose, - true, - &changed); + BKE_mesh_validate_all_customdata(&me->vdata, + me->totvert, + &me->edata, + me->totedge, + &me->ldata, + me->totloop, + &me->pdata, + me->totpoly, + cddata_check_mask, + do_verbose, + true, + &changed); + + BKE_mesh_validate_arrays(me, + me->mvert, + me->totvert, + me->medge, + me->totedge, + me->mface, + me->totface, + me->mloop, + me->totloop, + me->mpoly, + me->totpoly, + me->dvert, + do_verbose, + true, + &changed); if (changed) { DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY_ALL_MODES); diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index c23110b4703..3a93b7cde84 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -60,6 +60,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_moviecache.h" +#include "IMB_openexr.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -68,10 +69,6 @@ #include "BLO_read_write.h" -#ifdef WITH_OPENEXR -# include "intern/openexr/openexr_multi.h" -#endif - static void free_buffers(MovieClip *clip); static void movie_clip_init_data(ID *id) diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c index 80926bfc1e2..cad680ecedd 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.c +++ b/source/blender/blenkernel/intern/multires_unsubdivide.c @@ -869,6 +869,7 @@ static BMesh *get_bmesh_from_mesh(Mesh *mesh) mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); return bm; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 9a573919165..e3fe5d77d63 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -55,6 +55,7 @@ #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_idtype.h" +#include "BKE_image_format.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -532,13 +533,6 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } - else if ((ntree->type == NTREE_GEOMETRY) && - (node->type == GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP)) { - BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage); - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; - BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_vec); - BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_rgb); - } else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) { NodeShaderScript *nss = (NodeShaderScript *)node->storage; if (nss->bytecode) { @@ -602,8 +596,13 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) if (node->type == CMP_NODE_OUTPUT_FILE) { /* Inputs have their own storage data. */ + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + BKE_image_format_blend_write(writer, &nimf->format); + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - BLO_write_struct(writer, NodeImageMultiFileSocket, sock->storage); + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; + BLO_write_struct(writer, NodeImageMultiFileSocket, sockdata); + BKE_image_format_blend_write(writer, &sockdata->format); } } if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) { @@ -715,18 +714,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage); break; } - case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: { - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; - BLO_read_data_address(reader, &data->curve_vec); - if (data->curve_vec) { - BKE_curvemapping_blend_read(reader, data->curve_vec); - } - BLO_read_data_address(reader, &data->curve_rgb); - if (data->curve_rgb) { - BKE_curvemapping_blend_read(reader, data->curve_rgb); - } - break; - } case SH_NODE_SCRIPT: { NodeShaderScript *nss = (NodeShaderScript *)node->storage; BLO_read_data_address(reader, &nss->bytecode); @@ -768,6 +755,11 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) iuser->scene = nullptr; break; } + case CMP_NODE_OUTPUT_FILE: { + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + BKE_image_format_blend_read_data(reader, &nimf->format); + break; + } case FN_NODE_INPUT_STRING: { NodeInputString *storage = (NodeInputString *)node->storage; BLO_read_data_address(reader, &storage->string); @@ -790,6 +782,14 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { direct_link_node_socket(reader, sock); } + + /* Socket storage. */ + if (node->type == CMP_NODE_OUTPUT_FILE) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; + BKE_image_format_blend_read_data(reader, &sockdata->format); + } + } } /* interface socket lists */ @@ -3616,15 +3616,13 @@ void nodeSetActive(bNodeTree *ntree, bNode *node) LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) { tnode->flag &= ~NODE_ACTIVE; - if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) || - (node->typeinfo->type == GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE)) { + if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) { tnode->flag &= ~NODE_ACTIVE_TEXTURE; } } node->flag |= NODE_ACTIVE; - if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) || - (node->typeinfo->type == GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE)) { + if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) { node->flag |= NODE_ACTIVE_TEXTURE; } } @@ -4692,45 +4690,10 @@ static void registerGeometryNodes() { register_node_type_geo_group(); - register_node_type_geo_legacy_attribute_proximity(); - register_node_type_geo_legacy_attribute_randomize(); - register_node_type_geo_legacy_attribute_remove(); - register_node_type_geo_legacy_attribute_transfer(); - register_node_type_geo_legacy_curve_endpoints(); - register_node_type_geo_legacy_curve_reverse(); - register_node_type_geo_legacy_curve_set_handles(); - register_node_type_geo_legacy_curve_spline_type(); - register_node_type_geo_legacy_curve_subdivide(); - register_node_type_geo_legacy_curve_to_points(); - register_node_type_geo_legacy_delete_geometry(); - register_node_type_geo_legacy_edge_split(); - register_node_type_geo_legacy_material_assign(); - register_node_type_geo_legacy_mesh_to_curve(); - register_node_type_geo_legacy_points_to_volume(); - register_node_type_geo_legacy_raycast(); - register_node_type_geo_legacy_select_by_handle_type(); - register_node_type_geo_legacy_select_by_material(); - register_node_type_geo_legacy_subdivision_surface(); - register_node_type_geo_legacy_volume_to_mesh(); - register_node_type_geo_accumulate_field(); - register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); - register_node_type_geo_attribute_clamp(); - register_node_type_geo_attribute_color_ramp(); - register_node_type_geo_attribute_combine_xyz(); - register_node_type_geo_attribute_compare(); - register_node_type_geo_attribute_convert(); - register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_domain_size(); - register_node_type_geo_attribute_fill(); - register_node_type_geo_attribute_map_range(); - register_node_type_geo_attribute_math(); - register_node_type_geo_attribute_mix(); - register_node_type_geo_attribute_separate_xyz(); register_node_type_geo_attribute_statistic(); - register_node_type_geo_attribute_vector_math(); - register_node_type_geo_attribute_vector_rotate(); register_node_type_geo_boolean(); register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); @@ -4811,12 +4774,6 @@ static void registerGeometryNodes() register_node_type_geo_mesh_to_curve(); register_node_type_geo_mesh_to_points(); register_node_type_geo_object_info(); - register_node_type_geo_point_distribute(); - register_node_type_geo_point_instance(); - register_node_type_geo_point_rotate(); - register_node_type_geo_point_scale(); - register_node_type_geo_point_separate(); - register_node_type_geo_point_translate(); register_node_type_geo_points_to_vertices(); register_node_type_geo_points_to_volume(); register_node_type_geo_proximity(); @@ -4824,7 +4781,6 @@ static void registerGeometryNodes() register_node_type_geo_realize_instances(); register_node_type_geo_remove_attribute(); register_node_type_geo_rotate_instances(); - register_node_type_geo_sample_texture(); register_node_type_geo_scale_elements(); register_node_type_geo_scale_instances(); register_node_type_geo_separate_components(); @@ -4855,8 +4811,6 @@ static void registerGeometryNodes() static void registerFunctionNodes() { - register_node_type_fn_legacy_random_float(); - register_node_type_fn_align_euler_to_vector(); register_node_type_fn_boolean_math(); register_node_type_fn_compare(); diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index 85061018383..baf3a0c8d22 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -1455,6 +1455,16 @@ class NodeTreeMainUpdater { socket_hash = noise::hash(socket_hash, input_socket_hash); } } + /* The Image Texture node has a special case. The behavior of the color output changes + * depending on whether the Alpha output is linked. */ + if (node.bnode()->type == SH_NODE_TEX_IMAGE && socket.index() == 0) { + BLI_assert(socket.name() == "Color"); + const OutputSocketRef &alpha_socket = node.output(1); + BLI_assert(alpha_socket.name() == "Alpha"); + if (alpha_socket.is_directly_linked()) { + socket_hash = noise::hash(socket_hash); + } + } hash_by_socket_id[socket.id()] = socket_hash; sockets_to_check.pop(); } diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 985c9edac1a..1e3b5d77fa7 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -4630,9 +4630,11 @@ bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb) if (key->refkey) { /* apply new basis key on original data */ switch (ob->type) { - case OB_MESH: - BKE_keyblock_convert_to_mesh(key->refkey, (Mesh *)ob->data); + case OB_MESH: { + Mesh *mesh = (Mesh *)ob->data; + BKE_keyblock_convert_to_mesh(key->refkey, mesh->mvert, mesh->totvert); break; + } case OB_CURVES_LEGACY: case OB_SURF: BKE_keyblock_convert_to_curve( diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 70a11dce92d..4dc0130366e 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -25,6 +25,7 @@ #include "BLI_utildefines.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_ocean.h" #include "ocean_intern.h" diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 62794e3d0ec..e7ed100ed03 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -28,6 +28,7 @@ #include "BLI_utildefines.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" @@ -173,16 +174,16 @@ PackedFile *BKE_packedfile_new_from_memory(void *mem, int memlen) return pf; } -PackedFile *BKE_packedfile_new(ReportList *reports, const char *filename, const char *basepath) +PackedFile *BKE_packedfile_new(ReportList *reports, const char *filepath, const char *basepath) { PackedFile *pf = NULL; int file, filelen; char name[FILE_MAX]; void *data; - /* render result has no filename and can be ignored + /* render result has no filepath and can be ignored * any other files with no name can be ignored too */ - if (filename[0] == '\0') { + if (filepath[0] == '\0') { return pf; } @@ -190,7 +191,7 @@ PackedFile *BKE_packedfile_new(ReportList *reports, const char *filename, const /* convert relative filenames to absolute filenames */ - BLI_strncpy(name, filename, sizeof(name)); + BLI_strncpy(name, filepath, sizeof(name)); BLI_path_abs(name, basepath); /* open the file @@ -284,7 +285,7 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose) int BKE_packedfile_write_to_file(ReportList *reports, const char *ref_file_name, - const char *filename, + const char *filepath, PackedFile *pf, const bool guimode) { @@ -298,7 +299,7 @@ int BKE_packedfile_write_to_file(ReportList *reports, if (guimode) { } // XXX waitcursor(1); - BLI_strncpy(name, filename, sizeof(name)); + BLI_strncpy(name, filepath, sizeof(name)); BLI_path_abs(name, ref_file_name); if (BLI_exists(name)) { @@ -357,7 +358,7 @@ int BKE_packedfile_write_to_file(ReportList *reports, } enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name, - const char *filename, + const char *filepath, PackedFile *pf) { BLI_stat_t st; @@ -365,7 +366,7 @@ enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name, char buf[4096]; char name[FILE_MAX]; - BLI_strncpy(name, filename, sizeof(name)); + BLI_strncpy(name, filepath, sizeof(name)); BLI_path_abs(name, ref_file_name); if (BLI_stat(name, &st) == -1) { @@ -503,7 +504,7 @@ static void unpack_generate_paths(const char *name, const PackedFile *pf = imapf->packedfile; enum eImbFileType ftype = IMB_ispic_type_from_memory((const uchar *)pf->data, pf->size); if (ftype != IMB_FTYPE_NONE) { - const int imtype = BKE_image_ftype_to_imtype(ftype, NULL); + const int imtype = BKE_ftype_to_imtype(ftype, NULL); BKE_image_path_ensure_ext_from_imtype(tempname, imtype); } } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 238cf1ad74e..1c58173f570 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1099,6 +1099,7 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) } else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) { CurvesSculpt *data = MEM_callocN(sizeof(*data), __func__); + data->curve_length = 0.3f; paint = &data->paint; } else if (*r_paint == &ts->imapaint.paint) { diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index c47b22dcd34..9ea1336a95a 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -2661,8 +2661,8 @@ void psys_find_parents(ParticleSimulationData *sim, const bool use_render_params int from = PART_FROM_FACE; totparent = (int)(totchild * part->parents * 0.3f); - if (use_render_params && part->child_nbr && part->ren_child_nbr) { - totparent *= (float)part->child_nbr / (float)part->ren_child_nbr; + if (use_render_params && part->child_percent && part->child_render_percent) { + totparent *= (float)part->child_percent / (float)part->child_render_percent; } /* hard limit, workaround for it being ignored above */ @@ -2736,8 +2736,8 @@ static bool psys_thread_context_init_path(ParticleThreadContext *ctx, if (totchild && part->childtype == PART_CHILD_FACES) { totparent = (int)(totchild * part->parents * 0.3f); - if (use_render_params && part->child_nbr && part->ren_child_nbr) { - totparent *= (float)part->child_nbr / (float)part->ren_child_nbr; + if (use_render_params && part->child_percent && part->child_render_percent) { + totparent *= (float)part->child_percent / (float)part->child_render_percent; } /* part->parents could still be 0 so we can't test with totparent */ diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c index 8106ae8b302..5dba4d3f003 100644 --- a/source/blender/blenkernel/intern/particle_child.c +++ b/source/blender/blenkernel/intern/particle_child.c @@ -142,9 +142,6 @@ static void do_kink_spiral(ParticleThreadContext *ctx, float kink_freq = part->kink_freq; float kink_shape = part->kink_shape; float kink_axis_random = part->kink_axis_random; - float rough1 = part->rough1; - float rough2 = part->rough2; - float rough_end = part->rough_end; ParticlePathIterator iter; ParticleCacheKey *key; @@ -164,9 +161,6 @@ static void do_kink_spiral(ParticleThreadContext *ctx, if (ptex) { kink_amp *= ptex->kink_amp; kink_freq *= ptex->kink_freq; - rough1 *= ptex->rough1; - rough2 *= ptex->rough2; - rough_end *= ptex->roughe; } cut_time = (totkeys - 1) * ptex->length; diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 83fb52ce1ef..d2c3776d4ea 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -64,14 +64,14 @@ static void distribute_simple_children(Scene *scene, { ChildParticle *cpa = NULL; int i, p; - int child_nbr = psys_get_child_number(scene, psys, use_render_params); - int totpart = psys_get_tot_child(scene, psys, use_render_params); + const int child_num = psys_get_child_number(scene, psys, use_render_params); + const int totpart = psys_get_tot_child(scene, psys, use_render_params); RNG *rng = BLI_rng_new_srandom(31415926 + psys->seed + psys->child_seed); alloc_child_particles(psys, totpart); cpa = psys->child; - for (i = 0; i < child_nbr; i++) { + for (i = 0; i < child_num; i++) { for (p = 0; p < psys->totpart; p++, cpa++) { float length = 2.0; cpa->parent = p; diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 3a1aefec2d3..7fdc60a265b 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -98,9 +98,9 @@ float psys_get_current_display_percentage(ParticleSystem *psys, const bool use_r ParticleSettings *part = psys->part; if ((use_render_params && - !particles_are_dynamic(psys)) || /* non-dynamic particles can be rendered fully */ - (part->child_nbr && part->childtype) || /* display percentage applies to children */ - (psys->pointcache->flag & PTCACHE_BAKING)) /* baking is always done with full amount */ + !particles_are_dynamic(psys)) || /* non-dynamic particles can be rendered fully */ + (part->child_percent && part->childtype) || /* display percentage applies to children */ + (psys->pointcache->flag & PTCACHE_BAKING)) /* baking is always done with full amount */ { return 1.0f; } @@ -280,20 +280,20 @@ static void realloc_particles(ParticleSimulationData *sim, int new_totpart) int psys_get_child_number(Scene *scene, ParticleSystem *psys, const bool use_render_params) { - int nbr; + int child_num; if (!psys->part->childtype) { return 0; } if (use_render_params) { - nbr = psys->part->ren_child_nbr; + child_num = psys->part->child_render_percent; } else { - nbr = psys->part->child_nbr; + child_num = psys->part->child_percent; } - return get_render_child_particle_number(&scene->r, nbr, use_render_params); + return get_render_child_particle_number(&scene->r, child_num, use_render_params); } int psys_get_tot_child(Scene *scene, ParticleSystem *psys, const bool use_render_params) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index baf2f0bac8a..b98c82e365e 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -70,6 +70,7 @@ #include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" @@ -201,15 +202,8 @@ static void scene_init_data(ID *id) colorspace_name, sizeof(scene->sequencer_colorspace_settings.name)); - /* Those next two sets (render and baking settings) are not currently in use, - * but are exposed to RNA API and hence must have valid data. */ - BKE_color_managed_display_settings_init(&scene->r.im_format.display_settings); - BKE_color_managed_view_settings_init_render( - &scene->r.im_format.view_settings, &scene->r.im_format.display_settings, "Filmic"); - - BKE_color_managed_display_settings_init(&scene->r.bake.im_format.display_settings); - BKE_color_managed_view_settings_init_render( - &scene->r.bake.im_format.view_settings, &scene->r.bake.im_format.display_settings, "Filmic"); + BKE_image_format_init(&scene->r.im_format, true); + BKE_image_format_init(&scene->r.bake.im_format, true); /* Curve Profile */ scene->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE); @@ -295,15 +289,8 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int BKE_color_managed_colorspace_settings_copy(&scene_dst->sequencer_colorspace_settings, &scene_src->sequencer_colorspace_settings); - BKE_color_managed_display_settings_copy(&scene_dst->r.im_format.display_settings, - &scene_src->r.im_format.display_settings); - BKE_color_managed_view_settings_copy(&scene_dst->r.im_format.view_settings, - &scene_src->r.im_format.view_settings); - - BKE_color_managed_display_settings_copy(&scene_dst->r.bake.im_format.display_settings, - &scene_src->r.bake.im_format.display_settings); - BKE_color_managed_view_settings_copy(&scene_dst->r.bake.im_format.view_settings, - &scene_src->r.bake.im_format.view_settings); + BKE_image_format_copy(&scene_dst->r.im_format, &scene_src->r.im_format); + BKE_image_format_copy(&scene_dst->r.bake.im_format, &scene_src->r.bake.im_format); BKE_curvemapping_copy_data(&scene_dst->r.mblur_shutter_curve, &scene_src->r.mblur_shutter_curve); @@ -402,6 +389,8 @@ static void scene_free_data(ID *id) BKE_sound_destroy_scene(scene); BKE_color_managed_view_settings_free(&scene->view_settings); + BKE_image_format_free(&scene->r.im_format); + BKE_image_format_free(&scene->r.bake.im_format); BKE_previewimg_free(&scene->preview); BKE_curvemapping_free_data(&scene->r.mblur_shutter_curve); @@ -1044,6 +1033,8 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres } BKE_color_managed_view_settings_blend_write(writer, &sce->view_settings); + BKE_image_format_blend_write(writer, &sce->r.im_format); + BKE_image_format_blend_write(writer, &sce->r.bake.im_format); /* writing RigidBodyWorld data to the blend file */ if (sce->rigidbody_world) { @@ -1276,6 +1267,8 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) } BKE_color_managed_view_settings_blend_read_data(reader, &sce->view_settings); + BKE_image_format_blend_read_data(reader, &sce->r.im_format); + BKE_image_format_blend_read_data(reader, &sce->r.bake.im_format); BLO_read_data_address(reader, &sce->rigidbody_world); RigidBodyWorld *rbw = sce->rigidbody_world; @@ -1855,15 +1848,8 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) BKE_color_managed_colorspace_settings_copy(&sce_copy->sequencer_colorspace_settings, &sce->sequencer_colorspace_settings); - BKE_color_managed_display_settings_copy(&sce_copy->r.im_format.display_settings, - &sce->r.im_format.display_settings); - BKE_color_managed_view_settings_copy(&sce_copy->r.im_format.view_settings, - &sce->r.im_format.view_settings); - - BKE_color_managed_display_settings_copy(&sce_copy->r.bake.im_format.display_settings, - &sce->r.bake.im_format.display_settings); - BKE_color_managed_view_settings_copy(&sce_copy->r.bake.im_format.view_settings, - &sce->r.bake.im_format.view_settings); + BKE_image_format_copy(&sce_copy->r.im_format, &sce->r.im_format); + BKE_image_format_copy(&sce_copy->r.bake.im_format, &sce->r.bake.im_format); BKE_curvemapping_copy_data(&sce_copy->r.mblur_shutter_curve, &sce->r.mblur_shutter_curve); @@ -2765,17 +2751,17 @@ int get_render_subsurf_level(const RenderData *r, int lvl, bool for_render) return lvl; } -int get_render_child_particle_number(const RenderData *r, int num, bool for_render) +int get_render_child_particle_number(const RenderData *r, int child_num, bool for_render) { if (r->mode & R_SIMPLIFY) { if (for_render) { - return (int)(r->simplify_particles_render * num); + return (int)(r->simplify_particles_render * child_num); } - return (int)(r->simplify_particles * num); + return (int)(r->simplify_particles * child_num); } - return num; + return child_num; } Base *_setlooper_base_step(Scene **sce_iter, ViewLayer *view_layer, Base *base) @@ -3137,7 +3123,9 @@ int BKE_scene_multiview_view_id_get(const RenderData *rd, const char *viewname) return 0; } -void BKE_scene_multiview_filepath_get(SceneRenderView *srv, const char *filepath, char *r_filepath) +void BKE_scene_multiview_filepath_get(const SceneRenderView *srv, + const char *filepath, + char *r_filepath) { BLI_strncpy(r_filepath, filepath, FILE_MAX); BLI_path_suffix(r_filepath, FILE_MAX, srv->suffix, ""); diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index dc5b1d28539..eecb374cd63 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_array.hh" +#include "BLI_generic_virtual_array.hh" #include "BLI_span.hh" #include "BLI_task.hh" #include "BLI_timeit.hh" @@ -9,19 +10,17 @@ #include "BKE_attribute_math.hh" #include "BKE_spline.hh" -#include "FN_generic_virtual_array.hh" - using blender::Array; using blender::float3; +using blender::GMutableSpan; +using blender::GSpan; +using blender::GVArray; using blender::IndexRange; using blender::MutableSpan; using blender::Span; using blender::VArray; using blender::attribute_math::convert_to_static_type; using blender::bke::AttributeIDRef; -using blender::fn::GMutableSpan; -using blender::fn::GSpan; -using blender::fn::GVArray; CurveType Spline::type() const { @@ -100,7 +99,7 @@ void Spline::reverse() this->attributes.foreach_attribute( [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - std::optional<blender::fn::GMutableSpan> attribute = this->attributes.get_for_write(id); + std::optional<blender::GMutableSpan> attribute = this->attributes.get_for_write(id); if (!attribute) { BLI_assert_unreachable(); return false; diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index e9ae51b16f8..8e207f93bf5 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -8,11 +8,11 @@ using blender::Array; using blender::float3; +using blender::GVArray; using blender::IndexRange; using blender::MutableSpan; using blender::Span; using blender::VArray; -using blender::fn::GVArray; void BezierSpline::copy_settings(Spline &dst) const { diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index a8a17b7aee6..9d1d5a53a43 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -9,11 +9,11 @@ using blender::Array; using blender::float3; +using blender::GVArray; using blender::IndexRange; using blender::MutableSpan; using blender::Span; using blender::VArray; -using blender::fn::GVArray; void NURBSpline::copy_settings(Spline &dst) const { diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index a5d3fd47ede..122f7f6c059 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -6,9 +6,9 @@ #include "BKE_spline.hh" using blender::float3; +using blender::GVArray; using blender::MutableSpan; using blender::Span; -using blender::fn::GVArray; void PolySpline::copy_settings(Spline &UNUSED(dst)) const { diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index 811b6bfe182..f17450ac3f4 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -25,13 +25,12 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_openexr.h" #include "GPU_texture.h" #include "MEM_guardedalloc.h" -#include "intern/openexr/openexr_multi.h" - /* Statics */ static ListBase studiolights; static int last_studiolight_id = 0; @@ -1167,18 +1166,18 @@ static void studiolight_add_files_from_datafolder(const int folder_id, const char *subfolder, int flag) { - struct direntry *dir; + struct direntry *dirs; const char *folder = BKE_appdir_folder_id(folder_id, subfolder); if (folder) { - uint totfile = BLI_filelist_dir_contents(folder, &dir); + const uint dirs_num = BLI_filelist_dir_contents(folder, &dirs); int i; - for (i = 0; i < totfile; i++) { - if (dir[i].type & S_IFREG) { - studiolight_add_file(dir[i].path, flag); + for (i = 0; i < dirs_num; i++) { + if (dirs[i].type & S_IFREG) { + studiolight_add_file(dirs[i].path, flag); } } - BLI_filelist_free(dir, totfile); - dir = NULL; + BLI_filelist_free(dirs, dirs_num); + dirs = NULL; } } diff --git a/source/blender/blenkernel/intern/subdiv.c b/source/blender/blenkernel/intern/subdiv.c index 7b1ebd5df1f..ee1976d5946 100644 --- a/source/blender/blenkernel/intern/subdiv.c +++ b/source/blender/blenkernel/intern/subdiv.c @@ -106,7 +106,7 @@ Subdiv *BKE_subdiv_new_from_converter(const SubdivSettings *settings, * The thing here is: OpenSubdiv can only deal with faces, but our * side of subdiv also deals with loose vertices and edges. */ } - Subdiv *subdiv = MEM_callocN(sizeof(Subdiv), "subdiv from converetr"); + Subdiv *subdiv = MEM_callocN(sizeof(Subdiv), "subdiv from converter"); subdiv->settings = *settings; subdiv->topology_refiner = osd_topology_refiner; subdiv->evaluator = NULL; diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 34dfdaf7595..83772f153d9 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -77,6 +77,10 @@ static bool is_subdivision_evaluation_possible_on_gpu(void) return false; } + if (GPU_max_shader_storage_buffer_bindings() < MAX_GPU_SUBDIV_SSBOS) { + return false; + } + const int available_evaluators = openSubdiv_getAvailableEvaluators(); if ((available_evaluators & OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) == 0) { return false; diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 991fd9e3aff..8759d7a0e5f 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -143,7 +143,7 @@ static void text_copy_data(Main *UNUSED(bmain), /** Free (or release) any data used by this text (does not free the text itself). */ static void text_free_data(ID *id) { - /* No animdata here. */ + /* No animation-data here. */ Text *text = (Text *)id; BKE_text_free_lines(text); @@ -1501,7 +1501,8 @@ char *txt_sel_to_buf(Text *text, size_t *r_buf_strlen) if (linef == linel) { length = charl - charf; buf = MEM_mallocN(length + 1, "sel buffer"); - memcpy(buf, linef->line + charf, length + 1); + memcpy(buf, linef->line + charf, length); + buf[length] = '\0'; } else { /* Add 1 for the '\n' */ diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c index 21a56c44c9b..6145e51920f 100644 --- a/source/blender/blenkernel/intern/tracking_auto.c +++ b/source/blender/blenkernel/intern/tracking_auto.c @@ -24,6 +24,9 @@ #include "BKE_movieclip.h" #include "BKE_tracking.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "libmv-capi.h" #include "tracking_private.h" @@ -99,6 +102,12 @@ typedef struct AutoTrackContext { /* Accessor for images of clip. Used by the autotrack context. */ TrackingImageAccessor *image_accessor; + /* Image buffers acquired for markers which are using keyframe pattern matching. + * These image buffers are user-referenced and flagged as persistent so that they don't get + * removed from the movie cache during tracking. */ + int num_referenced_image_buffers; + ImBuf **referenced_image_buffers; + /* -------------------------------------------------------------------- * Variant part. * Denotes tracing state and tracking result. @@ -554,6 +563,59 @@ AutoTrackContext *BKE_autotrack_context_new(MovieClip *clip, /** \} */ /* -------------------------------------------------------------------- */ +/** \name Context tracking start. + * + * Called from possible job once before performing tracking steps. + * \{ */ + +static void reference_keyframed_image_buffers(AutoTrackContext *context) +{ + /* NOTE: This is potentially over-allocating, but it simplifies memory manipulation. + * In practice this is unlikely to be noticed in the profiler as the memory footprint of this + * data is way less of what the tracking process will use. */ + context->referenced_image_buffers = MEM_calloc_arrayN( + context->num_autotrack_markers, sizeof(ImBuf *), __func__); + + context->num_referenced_image_buffers = 0; + + for (int i = 0; i < context->num_autotrack_markers; ++i) { + const AutoTrackMarker *autotrack_marker = &context->autotrack_markers[i]; + const int clip_index = autotrack_marker->libmv_marker.clip; + const int track_index = autotrack_marker->libmv_marker.track; + + const AutoTrackClip *autotrack_clip = &context->autotrack_clips[clip_index]; + const AutoTrackTrack *autotrack_track = &context->all_autotrack_tracks[track_index]; + const MovieTrackingTrack *track = autotrack_track->track; + + if (track->pattern_match != TRACK_MATCH_KEYFRAME) { + continue; + } + + const int scene_frame = BKE_movieclip_remap_clip_to_scene_frame( + autotrack_clip->clip, autotrack_marker->libmv_marker.reference_frame); + + MovieClipUser user_at_keyframe; + BKE_movieclip_user_set_frame(&user_at_keyframe, scene_frame); + user_at_keyframe.render_size = MCLIP_PROXY_RENDER_SIZE_FULL; + user_at_keyframe.render_flag = 0; + + /* Keep reference to the image buffer so that we can manipulate its flags later on. + * Also request the movie cache to not remove the image buffer from the cache. */ + ImBuf *ibuf = BKE_movieclip_get_ibuf(autotrack_clip->clip, &user_at_keyframe); + ibuf->userflags |= IB_PERSISTENT; + + context->referenced_image_buffers[context->num_referenced_image_buffers++] = ibuf; + } +} + +void BKE_autotrack_context_start(AutoTrackContext *context) +{ + reference_keyframed_image_buffers(context); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Threaded context step (tracking process). * \{ */ @@ -799,6 +861,20 @@ void BKE_autotrack_context_finish(AutoTrackContext *context) } } +static void release_keyframed_image_buffers(AutoTrackContext *context) +{ + for (int i = 0; i < context->num_referenced_image_buffers; ++i) { + ImBuf *ibuf = context->referenced_image_buffers[i]; + + /* Restore flag. It is not expected that anyone else is setting this flag on image buffers from + * movie clip, so can simply clear the flag. */ + ibuf->userflags &= ~IB_PERSISTENT; + IMB_freeImBuf(ibuf); + } + + MEM_freeN(context->referenced_image_buffers); +} + void BKE_autotrack_context_free(AutoTrackContext *context) { if (context->autotrack != NULL) { @@ -809,6 +885,8 @@ void BKE_autotrack_context_free(AutoTrackContext *context) tracking_image_accessor_destroy(context->image_accessor); } + release_keyframed_image_buffers(context); + MEM_SAFE_FREE(context->all_autotrack_tracks); MEM_SAFE_FREE(context->autotrack_markers); diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc index 2e8b5b3433b..d10979eeee9 100644 --- a/source/blender/blenkernel/intern/type_conversions.cc +++ b/source/blender/blenkernel/intern/type_conversions.cc @@ -288,8 +288,7 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type, functions->convert_single_to_uninitialized(from_value, to_value); } -void DataTypeConversions::convert_to_initialized_n(fn::GSpan from_span, - fn::GMutableSpan to_span) const +void DataTypeConversions::convert_to_initialized_n(GSpan from_span, GMutableSpan to_span) const { const CPPType &from_type = from_span.type(); const CPPType &to_type = to_span.type(); @@ -305,19 +304,17 @@ void DataTypeConversions::convert_to_initialized_n(fn::GSpan from_span, fn->call_auto(IndexRange(from_span.size()), params, context); } -class GVArray_For_ConvertedGVArray : public fn::GVArrayImpl { +class GVArray_For_ConvertedGVArray : public GVArrayImpl { private: - fn::GVArray varray_; + GVArray varray_; const CPPType &from_type_; ConversionFunctions old_to_new_conversions_; public: - GVArray_For_ConvertedGVArray(fn::GVArray varray, + GVArray_For_ConvertedGVArray(GVArray varray, const CPPType &to_type, const DataTypeConversions &conversions) - : fn::GVArrayImpl(to_type, varray.size()), - varray_(std::move(varray)), - from_type_(varray_.type()) + : GVArrayImpl(to_type, varray.size()), varray_(std::move(varray)), from_type_(varray_.type()) { old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type); } @@ -340,18 +337,18 @@ class GVArray_For_ConvertedGVArray : public fn::GVArrayImpl { } }; -class GVMutableArray_For_ConvertedGVMutableArray : public fn::GVMutableArrayImpl { +class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArrayImpl { private: - fn::GVMutableArray varray_; + GVMutableArray varray_; const CPPType &from_type_; ConversionFunctions old_to_new_conversions_; ConversionFunctions new_to_old_conversions_; public: - GVMutableArray_For_ConvertedGVMutableArray(fn::GVMutableArray varray, + GVMutableArray_For_ConvertedGVMutableArray(GVMutableArray varray, const CPPType &to_type, const DataTypeConversions &conversions) - : fn::GVMutableArrayImpl(to_type, varray.size()), + : GVMutableArrayImpl(to_type, varray.size()), varray_(std::move(varray)), from_type_(varray_.type()) { @@ -384,7 +381,7 @@ class GVMutableArray_For_ConvertedGVMutableArray : public fn::GVMutableArrayImpl } }; -fn::GVArray DataTypeConversions::try_convert(fn::GVArray varray, const CPPType &to_type) const +GVArray DataTypeConversions::try_convert(GVArray varray, const CPPType &to_type) const { const CPPType &from_type = varray.type(); if (from_type == to_type) { @@ -393,11 +390,11 @@ fn::GVArray DataTypeConversions::try_convert(fn::GVArray varray, const CPPType & if (!this->is_convertible(from_type, to_type)) { return {}; } - return fn::GVArray::For<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this); + return GVArray::For<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this); } -fn::GVMutableArray DataTypeConversions::try_convert(fn::GVMutableArray varray, - const CPPType &to_type) const +GVMutableArray DataTypeConversions::try_convert(GVMutableArray varray, + const CPPType &to_type) const { const CPPType &from_type = varray.type(); if (from_type == to_type) { @@ -406,7 +403,7 @@ fn::GVMutableArray DataTypeConversions::try_convert(fn::GVMutableArray varray, if (!this->is_convertible(from_type, to_type)) { return {}; } - return fn::GVMutableArray::For<GVMutableArray_For_ConvertedGVMutableArray>( + return GVMutableArray::For<GVMutableArray_For_ConvertedGVMutableArray>( std::move(varray), to_type, *this); } diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/blenlib/BLI_cpp_type.hh index 420e2a8d9a9..881408f460b 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/blenlib/BLI_cpp_type.hh @@ -3,28 +3,47 @@ #pragma once /** \file - * \ingroup fn + * \ingroup bli * - * The `CPPType` class is the core of a runtime-type-system. It allows working with arbitrary C++ - * types in a generic way. An instance of `CPPType` wraps exactly one type like `int` or - * `std::string`. + * The `CPPType` class allows working with arbitrary C++ types in a generic way. An instance of + * #CPPType wraps exactly one type like `int` or `std::string`. + * + * With #CPPType one can write generic data structures and algorithms. That is similar to what C++ + * templates allow. The difference is that when using templates, the types have to be known at + * compile time and the code has to be instantiated multiple times. On the other hand, when using + * #CPPType, the data type only has to be known at run-time, and the code only has to be compiled + * once. Whether #CPPType or classic c++ templates should be used depends on the context: + * - If the data type is not known at run-time, #CPPType should be used. + * - If the data type is known to be one of a few, it depends on how performance sensitive the code + * is. + * - If it it's a small hot loop, a template can be used to optimize for every type (at the + * cost of longer compile time, a larger binary and the complexity that comes from using + * templates). + * - If the code is not performance sensitive, it usually makes sense to use #CPPType instead. + * - Sometimes a combination can make sense. Optimized code can be be generated at compile-time for + * some types, while there is a fallback code path using #CPPType for all other types. + * #CPPType::to_static_type allows dispatching between both versions based on the type. + * + * Under some circumstances, #CPPType serves a similar role as #std::type_info. However, #CPPType + * has much more utility because it contains methods for actually working with instances of the + * type. * * Every type has a size and an alignment. Every function dealing with C++ types in a generic way, - * has to make sure that alignment rules are followed. The methods provided by a CPPType instance + * has to make sure that alignment rules are followed. The methods provided by a #CPPType instance * will check for correct alignment as well. * * Every type has a name that is for debugging purposes only. It should not be used as identifier. * - * To check if two instances of CPPType represent the same type, only their pointers have to be - * compared. Any C++ type has at most one corresponding CPPType instance. + * To check if two instances of #CPPType represent the same type, only their pointers have to be + * compared. Any C++ type has at most one corresponding #CPPType instance. * - * A CPPType instance comes with many methods that allow dealing with types in a generic way. Most - * methods come in three variants. Using the construct-default methods as example: - * - default_construct(void *ptr): + * A #CPPType instance comes with many methods that allow dealing with types in a generic way. Most + * methods come in three variants. Using the default-construct methods as an example: + * - `default_construct(void *ptr)`: * Constructs a single instance of that type at the given pointer. - * - default_construct_n(void *ptr, int64_t n): + * - `default_construct_n(void *ptr, int64_t n)`: * Constructs n instances of that type in an array that starts at the given pointer. - * - default_construct_indices(void *ptr, IndexMask mask): + * - `default_construct_indices(void *ptr, IndexMask mask)`: * Constructs multiple instances of that type in an array that starts at the given pointer. * Only the indices referenced by `mask` will by constructed. * @@ -39,8 +58,9 @@ * Concepts like inheritance are currently not captured by this system. This is not because it is * not possible, but because it was not necessary to add this complexity yet. * - * One could also implement CPPType itself using virtual inheritance. However, I found the approach - * used now with explicit function pointers to work better. Here are some reasons: + * One could also implement CPPType itself using virtual methods and a child class for every + * wrapped type. However, the approach used now with explicit function pointers to works better. + * Here are some reasons: * - If CPPType would be inherited once for every used C++ type, we would get a lot of classes * that would only be instanced once each. * - Methods like `default_construct` that operate on a single instance have to be fast. Even this @@ -52,6 +72,7 @@ #include "BLI_hash.hh" #include "BLI_index_mask.hh" +#include "BLI_map.hh" #include "BLI_math_base.h" #include "BLI_string_ref.hh" #include "BLI_utility_mixins.hh" @@ -71,7 +92,7 @@ enum class CPPTypeFlags { }; ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable) -namespace blender::fn { +namespace blender { /** Utility class to pass template parameters to constructor of `CPPType`. */ template<typename T, CPPTypeFlags Flags> struct CPPTypeParam { @@ -142,7 +163,7 @@ class CPPType : NonCopyable, NonMovable { /** * Get the `CPPType` that corresponds to a specific static type. * This only works for types that actually implement the template specialization using - * `MAKE_CPP_TYPE`. + * `BLI_CPP_TYPE_MAKE`. */ template<typename T> static const CPPType &get() { @@ -624,9 +645,80 @@ class CPPType : NonCopyable, NonMovable { { return this == &CPPType::get<std::decay_t<T>>(); } + + /** + * Convert a #CPPType that is only known at run-time, to a static type that is known at + * compile-time. This allows the compiler to optimize a function for specific types, while all + * other types can still use a generic fallback function. + * + * \param Types The types that code should be generated for. + * \param fn The function object to call. This is expected to have a templated `operator()` and a + * non-templated `operator()`. The templated version will be called if the current #CPPType + * matches any of the given types. Otherwise, the non-templated function is called. + */ + template<typename... Types, typename Fn> void to_static_type(const Fn &fn) const + { + using Callback = void (*)(const Fn &fn); + + /* Build a lookup table to avoid having to compare the current #CPPType with every type in + * #Types one after another. */ + static const Map<const CPPType *, Callback> callback_map = []() { + Map<const CPPType *, Callback> callback_map; + /* This adds an entry in the map for every type in #Types. */ + (callback_map.add_new(&CPPType::get<Types>(), + [](const Fn &fn) { + /* Call the templated `operator()` of the given function object. */ + fn.template operator()<Types>(); + }), + ...); + return callback_map; + }(); + + const Callback callback = callback_map.lookup_default(this, nullptr); + if (callback != nullptr) { + callback(fn); + } + else { + /* Call the non-templated `operator()` of the given function object. */ + fn(); + } + } + + template<typename T> struct type_tag { + using type = T; + }; + + private: + template<typename Fn> struct TypeTagExecutor { + const Fn &fn; + + template<typename T> void operator()() const + { + fn(type_tag<T>{}); + } + + void operator()() const + { + fn(type_tag<void>{}); + } + }; + + public: + /** + * Similar to #to_static_type but is easier to use with a lambda function. The function is + * expected to take a single `auto type_tag` parameter. To extract the static type, use: + * `using T = typename decltype(type_tag)::type;` + * + * If the current #CPPType is not in #Types, the type tag is `void`. + */ + template<typename... Types, typename Fn> void to_static_type_tag(const Fn &fn) const + { + TypeTagExecutor<Fn> executor{fn}; + this->to_static_type<Types...>(executor); + } }; -} // namespace blender::fn +} // namespace blender /* Utility for allocating an uninitialized buffer for a single value of the given #CPPType. */ #define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name) \ diff --git a/source/blender/functions/FN_cpp_type_make.hh b/source/blender/blenlib/BLI_cpp_type_make.hh index ac498e40fe3..9100b2b9a0f 100644 --- a/source/blender/functions/FN_cpp_type_make.hh +++ b/source/blender/blenlib/BLI_cpp_type_make.hh @@ -3,13 +3,13 @@ #pragma once /** \file - * \ingroup fn + * \ingroup bli */ +#include "BLI_cpp_type.hh" #include "BLI_utildefines.h" -#include "FN_cpp_type.hh" -namespace blender::fn::cpp_type_util { +namespace blender::cpp_type_util { template<typename T> void default_construct_cb(void *ptr) { @@ -169,9 +169,9 @@ template<typename T> uint64_t hash_cb(const void *value) return get_default_hash(value_); } -} // namespace blender::fn::cpp_type_util +} // namespace blender::cpp_type_util -namespace blender::fn { +namespace blender { template<typename T, CPPTypeFlags Flags> CPPType::CPPType(CPPTypeParam<T, Flags> /* unused */, StringRef debug_name) @@ -240,12 +240,11 @@ CPPType::CPPType(CPPTypeParam<T, Flags> /* unused */, StringRef debug_name) move_construct_ && move_assign_ && destruct_); } -} // namespace blender::fn +} // namespace blender -#define MAKE_CPP_TYPE(IDENTIFIER, TYPE_NAME, FLAGS) \ - template<> const blender::fn::CPPType &blender::fn::CPPType::get_impl<TYPE_NAME>() \ +#define BLI_CPP_TYPE_MAKE(IDENTIFIER, TYPE_NAME, FLAGS) \ + template<> const blender::CPPType &blender::CPPType::get_impl<TYPE_NAME>() \ { \ - static CPPType cpp_type{blender::fn::CPPTypeParam<TYPE_NAME, FLAGS>(), \ - STRINGIFY(IDENTIFIER)}; \ + static CPPType cpp_type{blender::CPPTypeParam<TYPE_NAME, FLAGS>(), STRINGIFY(IDENTIFIER)}; \ return cpp_type; \ } diff --git a/source/blender/functions/FN_generic_array.hh b/source/blender/blenlib/BLI_generic_array.hh index 4bd0c38aa42..e1b6b29874a 100644 --- a/source/blender/functions/FN_generic_array.hh +++ b/source/blender/blenlib/BLI_generic_array.hh @@ -3,7 +3,7 @@ #pragma once /** \file - * \ingroup fn + * \ingroup bli * * This is a generic counterpart to #blender::Array, used when the type is not known at runtime. * @@ -14,11 +14,10 @@ */ #include "BLI_allocator.hh" +#include "BLI_cpp_type.hh" +#include "BLI_generic_span.hh" -#include "FN_cpp_type.hh" -#include "FN_generic_span.hh" - -namespace blender::fn { +namespace blender { template< /** @@ -253,4 +252,4 @@ class GArray { } }; -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/FN_generic_pointer.hh b/source/blender/blenlib/BLI_generic_pointer.hh index a1fb16b5ab2..226f76c3d33 100644 --- a/source/blender/functions/FN_generic_pointer.hh +++ b/source/blender/blenlib/BLI_generic_pointer.hh @@ -2,9 +2,9 @@ #pragma once -#include "FN_cpp_type.hh" +#include "BLI_cpp_type.hh" -namespace blender::fn { +namespace blender { /** * A generic non-const pointer whose type is only known at runtime. @@ -120,4 +120,4 @@ class GPointer { } }; -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/blenlib/BLI_generic_span.hh index 2d3bfa55787..f4f93735e06 100644 --- a/source/blender/functions/FN_generic_span.hh +++ b/source/blender/blenlib/BLI_generic_span.hh @@ -3,14 +3,13 @@ #pragma once /** \file - * \ingroup fn + * \ingroup bli */ +#include "BLI_cpp_type.hh" #include "BLI_span.hh" -#include "FN_cpp_type.hh" - -namespace blender::fn { +namespace blender { /** * A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time. @@ -167,4 +166,4 @@ class GMutableSpan { } }; -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/FN_generic_value_map.hh b/source/blender/blenlib/BLI_generic_value_map.hh index 3807ada1c3c..bd8408526b8 100644 --- a/source/blender/functions/FN_generic_value_map.hh +++ b/source/blender/blenlib/BLI_generic_value_map.hh @@ -2,12 +2,11 @@ #pragma once +#include "BLI_generic_pointer.hh" #include "BLI_linear_allocator.hh" #include "BLI_map.hh" -#include "FN_generic_pointer.hh" - -namespace blender::fn { +namespace blender { /** * This is a map that stores key-value-pairs. What makes it special is that the type of values does @@ -109,4 +108,4 @@ template<typename Key> class GValueMap { } }; -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/blenlib/BLI_generic_vector_array.hh index d7416a0f5b9..c98817df4e3 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/blenlib/BLI_generic_vector_array.hh @@ -3,7 +3,7 @@ #pragma once /** \file - * \ingroup fn + * \ingroup bli * * A`GVectorArray` is a container for a fixed amount of dynamically growing vectors with a generic * data type. Its main use case is to store many small vectors with few separate allocations. Using @@ -11,11 +11,10 @@ */ #include "BLI_array.hh" +#include "BLI_generic_virtual_vector_array.hh" #include "BLI_linear_allocator.hh" -#include "FN_generic_virtual_vector_array.hh" - -namespace blender::fn { +namespace blender { /* An array of vectors containing elements of a generic type. */ class GVectorArray : NonCopyable, NonMovable { @@ -145,4 +144,4 @@ class GVVectorArray_For_GVectorArray : public GVVectorArray { } }; -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh index ced0c2b9546..f4c9e745cf9 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/blenlib/BLI_generic_virtual_array.hh @@ -3,19 +3,18 @@ #pragma once /** \file - * \ingroup fn + * \ingroup bli * - * A generic virtual array is the same as a virtual array from blenlib, except for the fact that - * the data type is only known at runtime. + * A generic virtual array is the same as a virtual array, except for the fact that the data type + * is only known at runtime. */ +#include "BLI_generic_array.hh" +#include "BLI_generic_span.hh" #include "BLI_timeit.hh" #include "BLI_virtual_array.hh" -#include "FN_generic_array.hh" -#include "FN_generic_span.hh" - -namespace blender::fn { +namespace blender { /* -------------------------------------------------------------------- */ /** \name #GVArrayImpl and #GVMutableArrayImpl. @@ -875,4 +874,4 @@ template<typename T> inline VMutableArray<T> GVMutableArray::typed() const /** \} */ -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/FN_generic_virtual_vector_array.hh b/source/blender/blenlib/BLI_generic_virtual_vector_array.hh index 1f40366da04..364b1ab33c7 100644 --- a/source/blender/functions/FN_generic_virtual_vector_array.hh +++ b/source/blender/blenlib/BLI_generic_virtual_vector_array.hh @@ -3,17 +3,16 @@ #pragma once /** \file - * \ingroup fn + * \ingroup bli * - * A generic virtual vector array is essentially the same as a virtual vector array from blenlib, - * but its data type is only known at runtime. + * A generic virtual vector array is essentially the same as a virtual vector array, but its data + * type is only known at runtime. */ -#include "FN_generic_virtual_array.hh" - +#include "BLI_generic_virtual_array.hh" #include "BLI_virtual_vector_array.hh" -namespace blender::fn { +namespace blender { /* A generically typed version of `VVectorArray`. */ class GVVectorArray { @@ -171,4 +170,4 @@ template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray< } }; -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index 5e56eec2ec6..38f3e1ee290 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -324,3 +324,32 @@ extern const float bvhtree_kdop_axes[13][3]; #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +# include "BLI_function_ref.hh" +# include "BLI_math_vector.hh" + +namespace blender { + +using BVHTree_RangeQuery_CPP = FunctionRef<void(int index, const float3 &co, float dist_sq)>; + +inline void BLI_bvhtree_range_query_cpp(BVHTree &tree, + const float3 co, + float radius, + BVHTree_RangeQuery_CPP fn) +{ + BLI_bvhtree_range_query( + &tree, + co, + radius, + [](void *userdata, const int index, const float co[3], const float dist_sq) { + BVHTree_RangeQuery_CPP fn = *static_cast<BVHTree_RangeQuery_CPP *>(userdata); + fn(index, co, dist_sq); + }, + &fn); +} + +} // namespace blender + +#endif diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 4bba84f2e29..c31e3045c97 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -202,7 +202,7 @@ float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3]); * \return the lowest squared distance to either of the planes. * where `(return < 0.0)` is outside. * - * <pre> + * \code{.unparsed} * v1 * + * / @@ -211,7 +211,7 @@ float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3]); * +----+ * v2 v3 * x - also outside - * </pre> + * \endcode */ float dist_signed_squared_to_corner_v3v3v3(const float p[3], const float v1[3], @@ -322,18 +322,22 @@ double closest_to_line_v2_db(double r_close[2], float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3]); /** * Point closest to v1 on line v2-v3 in 2D. + * + * \return A value in [0, 1] that corresponds to the position of #r_close on the line segment. */ -void closest_to_line_segment_v2(float r_close[2], - const float p[2], - const float l1[2], - const float l2[2]); +float closest_to_line_segment_v2(float r_close[2], + const float p[2], + const float l1[2], + const float l2[2]); /** * Point closest to v1 on line v2-v3 in 3D. + * + * \return A value in [0, 1] that corresponds to the position of #r_close on the line segment. */ -void closest_to_line_segment_v3(float r_close[3], - const float p[3], - const float l1[3], - const float l2[3]); +float closest_to_line_segment_v3(float r_close[3], + const float p[3], + const float l1[3], + const float l2[3]); void closest_to_plane_normalized_v3(float r_close[3], const float plane[4], const float pt[3]); /** * Find the closest point on a plane. diff --git a/source/blender/blenlib/BLI_math_statistics.h b/source/blender/blenlib/BLI_math_statistics.h index d33d4be360a..501a250e1a0 100644 --- a/source/blender/blenlib/BLI_math_statistics.h +++ b/source/blender/blenlib/BLI_math_statistics.h @@ -28,7 +28,7 @@ extern "C" { * * \param n: the dimension of the vectors (and hence, of the covariance matrix to compute). * \param cos_vn: the nD points to compute covariance from. - * \param nbr_cos_vn: the number of nD coordinates in cos_vn. + * \param cos_vn_num: the number of nD coordinates in cos_vn. * \param center: the center (or mean point) of cos_vn. If NULL, * it is assumed cos_vn is already centered. * \param use_sample_correction: whether to apply sample correction @@ -37,7 +37,7 @@ extern "C" { */ void BLI_covariance_m_vn_ex(int n, const float *cos_vn, - int nbr_cos_vn, + int cos_vn_num, const float *center, bool use_sample_correction, float *r_covmat); @@ -45,12 +45,12 @@ void BLI_covariance_m_vn_ex(int n, * \brief Compute the covariance matrix of given set of 3D coordinates. * * \param cos_v3: the 3D points to compute covariance from. - * \param nbr_cos_v3: the number of 3D coordinates in cos_v3. + * \param cos_v3_num: the number of 3D coordinates in cos_v3. * \return r_covmat the computed covariance matrix. * \return r_center the computed center (mean) of 3D points (may be NULL). */ void BLI_covariance_m3_v3n(const float (*cos_v3)[3], - int nbr_cos_v3, + int cos_v3_num, bool use_sample_correction, float r_covmat[3][3], float r_center[3]); diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 4689072bcce..f3283371a3c 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -389,7 +389,7 @@ void mid_v3_v3v3v3(float v[3], const float v1[3], const float v2[3], const float void mid_v2_v2v2v2(float v[2], const float v1[2], const float v2[2], const float v3[2]); void mid_v3_v3v3v3v3( float v[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3]); -void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], unsigned int nbr); +void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], unsigned int vec_arr_num); /** * Specialized function for calculating normals. @@ -660,7 +660,10 @@ void minmax_v4v4_v4(float min[4], float max[4], const float vec[4]); void minmax_v3v3_v3(float min[3], float max[3], const float vec[3]); void minmax_v2v2_v2(float min[2], float max[2], const float vec[2]); -void minmax_v3v3_v3_array(float r_min[3], float r_max[3], const float (*vec_arr)[3], int nbr); +void minmax_v3v3_v3_array(float r_min[3], + float r_max[3], + const float (*vec_arr)[3], + int var_arr_num); /** ensure \a v1 is \a dist from \a v2 */ void dist_ensure_v3_v3fl(float v1[3], const float v2[3], float dist); diff --git a/source/blender/blenlib/BLI_rand.hh b/source/blender/blenlib/BLI_rand.hh index 069a81bb84b..2c4484bd63f 100644 --- a/source/blender/blenlib/BLI_rand.hh +++ b/source/blender/blenlib/BLI_rand.hh @@ -103,6 +103,12 @@ class RandomNumberGenerator { return float3(rand1, rand2, 1.0f - rand1 - rand2); } + /** + * Round value to the next integer randomly. + * 4.9f is more likely to round to 5 than 4.6f. + */ + int round_probabilistic(float x); + float2 get_unit_float2(); float3 get_unit_float3(); /** diff --git a/source/blender/blenlib/BLI_sort.hh b/source/blender/blenlib/BLI_sort.hh new file mode 100644 index 00000000000..411e6a0c4df --- /dev/null +++ b/source/blender/blenlib/BLI_sort.hh @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef WITH_TBB +# include <tbb/parallel_sort.h> +#else +# include <algorithm> +#endif + +namespace blender { + +#ifdef WITH_TBB +using tbb::parallel_sort; +#else +template<typename RandomAccessIterator> +void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end) +{ + std::sort<RandomAccessIterator>(begin, end); +} +template<typename RandomAccessIterator, typename Compare> +void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end, const Compare &comp) +{ + std::sort<RandomAccessIterator, Compare>(begin, end, comp); +} +#endif + +} // namespace blender diff --git a/source/blender/blenlib/BLI_timeit.hh b/source/blender/blenlib/BLI_timeit.hh index 31e1b5d2a03..8da0a020d99 100644 --- a/source/blender/blenlib/BLI_timeit.hh +++ b/source/blender/blenlib/BLI_timeit.hh @@ -38,6 +38,41 @@ class ScopedTimer { } }; +class ScopedTimerAveraged { + private: + std::string name_; + TimePoint start_; + + int64_t &total_count_; + Nanoseconds &total_time_; + Nanoseconds &min_time_; + + public: + ScopedTimerAveraged(std::string name, + int64_t &total_count, + Nanoseconds &total_time, + Nanoseconds &min_time) + : name_(std::move(name)), + total_count_(total_count), + total_time_(total_time), + min_time_(min_time) + { + start_ = Clock::now(); + } + + ~ScopedTimerAveraged(); +}; + } // namespace blender::timeit #define SCOPED_TIMER(name) blender::timeit::ScopedTimer scoped_timer(name) + +/** + * Print the average and minimum runtime of the timer's scope. + * \warning This uses static variables, so it is not thread-safe. + */ +#define SCOPED_TIMER_AVERAGED(name) \ + static int64_t total_count_; \ + static blender::timeit::Nanoseconds total_time_; \ + static blender::timeit::Nanoseconds min_time_ = blender::timeit::Nanoseconds::max(); \ + blender::timeit::ScopedTimerAveraged scoped_timer(name, total_count_, total_time_, min_time_) diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 16fd706c99d..3aa25bf6819 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -31,10 +31,8 @@ namespace blender { /** Forward declarations for generic virtual arrays. */ -namespace fn { class GVArray; class GVMutableArray; -}; // namespace fn /** * Implements the specifics of how the elements of a virtual array are accessed. It contains a @@ -154,7 +152,7 @@ template<typename T> class VArrayImpl { * arrays in all cases. * Return true when the virtual array was assigned and false when nothing was done. */ - virtual bool try_assign_GVArray(fn::GVArray &UNUSED(varray)) const + virtual bool try_assign_GVArray(GVArray &UNUSED(varray)) const { return false; } @@ -211,7 +209,7 @@ template<typename T> class VMutableArrayImpl : public VArrayImpl<T> { /** * Similar to #VArrayImpl::try_assign_GVArray but for mutable virtual arrays. */ - virtual bool try_assign_GVMutableArray(fn::GVMutableArray &UNUSED(varray)) const + virtual bool try_assign_GVMutableArray(GVMutableArray &UNUSED(varray)) const { return false; } @@ -743,7 +741,7 @@ template<typename T> class VArrayCommon { } /** See #GVArrayImpl::try_assign_GVArray. */ - bool try_assign_GVArray(fn::GVArray &varray) const + bool try_assign_GVArray(GVArray &varray) const { return impl_->try_assign_GVArray(varray); } @@ -960,7 +958,7 @@ template<typename T> class VMutableArray : public VArrayCommon<T> { } /** See #GVMutableArrayImpl::try_assign_GVMutableArray. */ - bool try_assign_GVMutableArray(fn::GVMutableArray &varray) const + bool try_assign_GVMutableArray(GVMutableArray &varray) const { return this->get_impl()->try_assign_GVMutableArray(varray); } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 6e3e84f6495..647726722b1 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -50,6 +50,7 @@ set(SRC intern/boxpack_2d.c intern/buffer.c intern/convexhull_2d.c + intern/cpp_type.cc intern/delaunay_2d.cc intern/dot_export.cc intern/dynlib.c @@ -64,6 +65,9 @@ set(SRC intern/filereader_memory.c intern/filereader_zstd.c intern/fnmatch.c + intern/generic_vector_array.cc + intern/generic_virtual_array.cc + intern/generic_virtual_vector_array.cc intern/gsqueue.c intern/hash_md5.c intern/hash_mm2a.c @@ -161,8 +165,8 @@ set(SRC BLI_bitmap.h BLI_bitmap_draw_2d.h BLI_blenlib.h - BLI_boxpack_2d.h BLI_bounds.hh + BLI_boxpack_2d.h BLI_buffer.h BLI_color.hh BLI_compiler_attrs.h @@ -170,6 +174,8 @@ set(SRC BLI_compiler_typecheck.h BLI_console.h BLI_convexhull_2d.h + BLI_cpp_type.hh + BLI_cpp_type_make.hh BLI_delaunay_2d.h BLI_dial_2d.h BLI_disjoint_set.hh @@ -192,6 +198,13 @@ set(SRC BLI_float4x4.hh BLI_fnmatch.h BLI_function_ref.hh + BLI_generic_array.hh + BLI_generic_pointer.hh + BLI_generic_span.hh + BLI_generic_value_map.hh + BLI_generic_vector_array.hh + BLI_generic_virtual_array.hh + BLI_generic_virtual_vector_array.hh BLI_ghash.h BLI_gsqueue.h BLI_hash.h @@ -222,8 +235,8 @@ set(SRC BLI_map.hh BLI_map_slots.hh BLI_math.h - BLI_math_base.hh BLI_math_base.h + BLI_math_base.hh BLI_math_base_safe.h BLI_math_bits.h BLI_math_boolean.hh @@ -272,6 +285,7 @@ set(SRC BLI_simd.h BLI_smallhash.h BLI_sort.h + BLI_sort.hh BLI_sort_utils.h BLI_span.hh BLI_stack.h @@ -403,12 +417,16 @@ if(WITH_GTESTS) tests/BLI_array_utils_test.cc tests/BLI_bounds_test.cc tests/BLI_color_test.cc + tests/BLI_cpp_type_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc tests/BLI_edgehash_test.cc tests/BLI_expr_pylike_eval_test.cc tests/BLI_fileops_test.cc tests/BLI_function_ref_test.cc + tests/BLI_generic_array_test.cc + tests/BLI_generic_span_test.cc + tests/BLI_generic_vector_array_test.cc tests/BLI_ghash_test.cc tests/BLI_hash_mm2a_test.cc tests/BLI_heap_simple_test.cc diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c index 9f1f8fe0448..76fc5b6342a 100644 --- a/source/blender/blenlib/intern/BLI_filelist.c +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -97,8 +97,8 @@ static int bli_compare(struct direntry *entry1, struct direntry *entry2) } struct BuildDirCtx { - struct direntry *files; /* array[nrfiles] */ - int nrfiles; + struct direntry *files; /* array[files_num] */ + int files_num; }; /** @@ -154,7 +154,7 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname) if (newnum) { if (dir_ctx->files) { void *const tmp = MEM_reallocN(dir_ctx->files, - (dir_ctx->nrfiles + newnum) * sizeof(struct direntry)); + (dir_ctx->files_num + newnum) * sizeof(struct direntry)); if (tmp) { dir_ctx->files = (struct direntry *)tmp; } @@ -171,7 +171,7 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname) if (dir_ctx->files) { struct dirlink *dlink = (struct dirlink *)dirbase.first; - struct direntry *file = &dir_ctx->files[dir_ctx->nrfiles]; + struct direntry *file = &dir_ctx->files[dir_ctx->files_num]; while (dlink) { char fullname[PATH_MAX]; memset(file, 0, sizeof(struct direntry)); @@ -186,7 +186,7 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname) * does not support stat on '\\SERVER\foo\..', sigh... */ file->type |= S_IFDIR; } - dir_ctx->nrfiles++; + dir_ctx->files_num++; file++; dlink = dlink->next; } @@ -199,7 +199,7 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname) BLI_freelist(&dirbase); if (dir_ctx->files) { qsort(dir_ctx->files, - dir_ctx->nrfiles, + dir_ctx->files_num, sizeof(struct direntry), (int (*)(const void *, const void *))bli_compare); } @@ -219,7 +219,7 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_ { struct BuildDirCtx dir_ctx; - dir_ctx.nrfiles = 0; + dir_ctx.files_num = 0; dir_ctx.files = NULL; bli_builddir(&dir_ctx, dirname); @@ -233,7 +233,7 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_ *r_filelist = MEM_mallocN(sizeof(**r_filelist), __func__); } - return dir_ctx.nrfiles; + return dir_ctx.files_num; } void BLI_filelist_entry_size_to_string(const struct stat *st, diff --git a/source/blender/blenlib/intern/cpp_type.cc b/source/blender/blenlib/intern/cpp_type.cc new file mode 100644 index 00000000000..d6a087cf175 --- /dev/null +++ b/source/blender/blenlib/intern/cpp_type.cc @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_color.hh" +#include "BLI_cpp_type_make.hh" +#include "BLI_float4x4.hh" +#include "BLI_math_vec_types.hh" + +BLI_CPP_TYPE_MAKE(bool, bool, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(float, float, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(float2, blender::float2, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(float3, blender::float3, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(float4x4, blender::float4x4, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(int8, int8_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int16, int16_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int32, int32_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int64, int64_t, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(uint8, uint8_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint16, uint16_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint32, uint32_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint64, uint64_t, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(ColorGeometry4f, blender::ColorGeometry4f, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(ColorGeometry4b, blender::ColorGeometry4b, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(string, std::string, CPPTypeFlags::BasicType) diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index b7dbd7d679c..e6164c98402 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -423,7 +423,7 @@ template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state< os << " merge to " << vertname(cdt_state.cdt.verts[v->merge_to_index]) << "\n"; } const SymEdge<T> *se = v->symedge; - int cnt = 0; + int count = 0; constexpr int print_count_limit = 25; if (se) { os << " edges out:\n"; @@ -440,8 +440,8 @@ template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state< os << " " << vertname(vother) << "(e=" << trunc_ptr(se->edge) << ", se=" << trunc_ptr(se) << ")\n"; se = se->rot; - cnt++; - } while (se != v->symedge && cnt < print_count_limit); + count++; + } while (se != v->symedge && count < print_count_limit); os << "\n"; } } @@ -2202,7 +2202,6 @@ void add_face_constraints(CDT_state<T> *cdt_state, { int nv = input.vert.size(); int nf = input.face.size(); - int fstart = 0; SymEdge<T> *face_symedge0 = nullptr; CDTArrangement<T> *cdt = &cdt_state->cdt; int maxflen = 0; @@ -2221,7 +2220,6 @@ void add_face_constraints(CDT_state<T> *cdt_state, int flen = input.face[f].size(); if (flen <= 2) { /* Ignore faces with fewer than 3 vertices. */ - fstart += flen; continue; } int fedge_start = (f + 1) * cdt_state->face_edge_offset; @@ -2266,7 +2264,6 @@ void add_face_constraints(CDT_state<T> *cdt_state, add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end); } } - fstart += flen; } } diff --git a/source/blender/blenlib/intern/expr_pylike_eval.c b/source/blender/blenlib/intern/expr_pylike_eval.c index db677cd304c..1c32336fee0 100644 --- a/source/blender/blenlib/intern/expr_pylike_eval.c +++ b/source/blender/blenlib/intern/expr_pylike_eval.c @@ -829,18 +829,18 @@ static bool parse_unary(ExprParseState *state) /* Specially supported functions. */ if (STREQ(state->tokenbuf, "min")) { - int cnt = parse_function_args(state); - CHECK_ERROR(cnt > 0); + int count = parse_function_args(state); + CHECK_ERROR(count > 0); - parse_add_op(state, OPCODE_MIN, 1 - cnt)->arg.ival = cnt; + parse_add_op(state, OPCODE_MIN, 1 - count)->arg.ival = count; return true; } if (STREQ(state->tokenbuf, "max")) { - int cnt = parse_function_args(state); - CHECK_ERROR(cnt > 0); + int count = parse_function_args(state); + CHECK_ERROR(count > 0); - parse_add_op(state, OPCODE_MAX, 1 - cnt)->arg.ival = cnt; + parse_add_op(state, OPCODE_MAX, 1 - count)->arg.ival = count; return true; } diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 27f8f1d4bc8..26a0479f445 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -382,9 +382,9 @@ static bool delete_recursive(const char *dir) { struct direntry *filelist, *fl; bool err = false; - uint nbr, i; + uint filelist_num, i; - i = nbr = BLI_filelist_dir_contents(dir, &filelist); + i = filelist_num = BLI_filelist_dir_contents(dir, &filelist); fl = filelist; while (i--) { const char *file = BLI_path_basename(fl->path); @@ -415,7 +415,7 @@ static bool delete_recursive(const char *dir) err = true; } - BLI_filelist_free(filelist, nbr); + BLI_filelist_free(filelist, filelist_num); return err; } diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/blenlib/intern/generic_vector_array.cc index b188b31b087..b32236bfada 100644 --- a/source/blender/functions/intern/generic_vector_array.cc +++ b/source/blender/blenlib/intern/generic_vector_array.cc @@ -1,10 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "FN_generic_vector_array.hh" -#include "FN_multi_function_params.hh" -#include "FN_multi_function_signature.hh" +#include "BLI_generic_vector_array.hh" -namespace blender::fn { +namespace blender { GVectorArray::GVectorArray(const CPPType &type, const int64_t array_size) : type_(type), element_size_(type.size()), items_(array_size) @@ -95,4 +93,4 @@ void GVectorArray::realloc_to_at_least(Item &item, int64_t min_capacity) item.capacity = new_capacity; } -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/blenlib/intern/generic_virtual_array.cc index 4644323bd1f..8a6ef8e792f 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/blenlib/intern/generic_virtual_array.cc @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "FN_generic_virtual_array.hh" +#include "BLI_generic_virtual_array.hh" -namespace blender::fn { +namespace blender { /* -------------------------------------------------------------------- */ /** \name #GVArrayImpl @@ -391,10 +391,7 @@ void GVMutableArray_GSpan::save() if (data_ != owned_data_) { return; } - const int64_t element_size = type_->size(); - for (int64_t i : IndexRange(size_)) { - varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i)); - } + varray_.set_all(owned_data_); } void GVMutableArray_GSpan::disable_not_applied_warning() @@ -721,4 +718,4 @@ GMutableSpan GVMutableArray::get_internal_span() const /** \} */ -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/functions/intern/generic_virtual_vector_array.cc b/source/blender/blenlib/intern/generic_virtual_vector_array.cc index 7dc728a4460..8fd1fb50b72 100644 --- a/source/blender/functions/intern/generic_virtual_vector_array.cc +++ b/source/blender/blenlib/intern/generic_virtual_vector_array.cc @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "FN_generic_virtual_vector_array.hh" +#include "BLI_generic_virtual_vector_array.hh" -namespace blender::fn { +namespace blender { void GVArray_For_GVVectorArrayIndex::get(const int64_t index_in_vector, void *r_value) const { @@ -50,4 +50,4 @@ bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const return true; } -} // namespace blender::fn +} // namespace blender diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index bc3ed099fd5..e1ec22063e0 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -294,46 +294,48 @@ float dist_to_line_segment_v2(const float p[2], const float l1[2], const float l return sqrtf(dist_squared_to_line_segment_v2(p, l1, l2)); } -void closest_to_line_segment_v2(float r_close[2], - const float p[2], - const float l1[2], - const float l2[2]) +float closest_to_line_segment_v2(float r_close[2], + const float p[2], + const float l1[2], + const float l2[2]) { float lambda, cp[2]; lambda = closest_to_line_v2(cp, p, l1, l2); /* flip checks for !finite case (when segment is a point) */ - if (!(lambda > 0.0f)) { + if (lambda <= 0.0f) { copy_v2_v2(r_close, l1); + return 0.0f; } - else if (!(lambda < 1.0f)) { + if (lambda >= 1.0f) { copy_v2_v2(r_close, l2); + return 1.0f; } - else { - copy_v2_v2(r_close, cp); - } + copy_v2_v2(r_close, cp); + return lambda; } -void closest_to_line_segment_v3(float r_close[3], - const float p[3], - const float l1[3], - const float l2[3]) +float closest_to_line_segment_v3(float r_close[3], + const float p[3], + const float l1[3], + const float l2[3]) { float lambda, cp[3]; lambda = closest_to_line_v3(cp, p, l1, l2); /* flip checks for !finite case (when segment is a point) */ - if (!(lambda > 0.0f)) { + if (lambda <= 0.0f) { copy_v3_v3(r_close, l1); + return 0.0f; } - else if (!(lambda < 1.0f)) { + if (lambda >= 1.0f) { copy_v3_v3(r_close, l2); + return 1.0f; } - else { - copy_v3_v3(r_close, cp); - } + copy_v3_v3(r_close, cp); + return lambda; } void closest_to_plane_v3(float r_close[3], const float plane[4], const float pt[3]) diff --git a/source/blender/blenlib/intern/math_statistics.c b/source/blender/blenlib/intern/math_statistics.c index 14baca891c0..53fbc16f3fc 100644 --- a/source/blender/blenlib/intern/math_statistics.c +++ b/source/blender/blenlib/intern/math_statistics.c @@ -21,7 +21,7 @@ typedef struct CovarianceData { float *r_covmat; float covfac; int n; - int nbr_cos_vn; + int cos_vn_num; } CovarianceData; static void covariance_m_vn_ex_task_cb(void *__restrict userdata, @@ -33,7 +33,7 @@ static void covariance_m_vn_ex_task_cb(void *__restrict userdata, const float *center = data->center; float *r_covmat = data->r_covmat; const int n = data->n; - const int nbr_cos_vn = data->nbr_cos_vn; + const int cos_vn_num = data->cos_vn_num; int k; @@ -55,12 +55,12 @@ static void covariance_m_vn_ex_task_cb(void *__restrict userdata, } if (center) { - for (k = 0; k < nbr_cos_vn; k++) { + for (k = 0; k < cos_vn_num; k++) { r_covmat[a] += (cos_vn[k * n + i] - center[i]) * (cos_vn[k * n + j] - center[j]); } } else { - for (k = 0; k < nbr_cos_vn; k++) { + for (k = 0; k < cos_vn_num; k++) { r_covmat[a] += cos_vn[k * n + i] * cos_vn[k * n + j]; } } @@ -73,7 +73,7 @@ static void covariance_m_vn_ex_task_cb(void *__restrict userdata, void BLI_covariance_m_vn_ex(const int n, const float *cos_vn, - const int nbr_cos_vn, + const int cos_vn_num, const float *center, const bool use_sample_correction, float *r_covmat) @@ -81,7 +81,7 @@ void BLI_covariance_m_vn_ex(const int n, /* Note about that division: see https://en.wikipedia.org/wiki/Bessel%27s_correction. * In a nutshell, it must be 1 / (n - 1) for 'sample data', and 1 / n for 'population data'... */ - const float covfac = 1.0f / (float)(use_sample_correction ? nbr_cos_vn - 1 : nbr_cos_vn); + const float covfac = 1.0f / (float)(use_sample_correction ? cos_vn_num - 1 : cos_vn_num); memset(r_covmat, 0, sizeof(*r_covmat) * (size_t)(n * n)); @@ -91,27 +91,27 @@ void BLI_covariance_m_vn_ex(const int n, .r_covmat = r_covmat, .covfac = covfac, .n = n, - .nbr_cos_vn = nbr_cos_vn, + .cos_vn_num = cos_vn_num, }; TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((nbr_cos_vn * n * n) >= 10000); + settings.use_threading = ((cos_vn_num * n * n) >= 10000); BLI_task_parallel_range(0, n * n, &data, covariance_m_vn_ex_task_cb, &settings); } void BLI_covariance_m3_v3n(const float (*cos_v3)[3], - const int nbr_cos_v3, + const int cos_v3_num, const bool use_sample_correction, float r_covmat[3][3], float r_center[3]) { float center[3]; - const float mean_fac = 1.0f / (float)nbr_cos_v3; + const float mean_fac = 1.0f / (float)cos_v3_num; int i; zero_v3(center); - for (i = 0; i < nbr_cos_v3; i++) { + for (i = 0; i < cos_v3_num; i++) { /* Applying mean_fac here rather than once at the end reduce compute errors... */ madd_v3_v3fl(center, cos_v3[i], mean_fac); } @@ -121,5 +121,5 @@ void BLI_covariance_m3_v3n(const float (*cos_v3)[3], } BLI_covariance_m_vn_ex( - 3, (const float *)cos_v3, nbr_cos_v3, center, use_sample_correction, (float *)r_covmat); + 3, (const float *)cos_v3, cos_v3_num, center, use_sample_correction, (float *)r_covmat); } diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index da6a6dff16f..5dcdabaf760 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -268,12 +268,12 @@ void mid_v3_v3v3v3v3( v[2] = (v1[2] + v2[2] + v3[2] + v4[2]) / 4.0f; } -void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], const uint nbr) +void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], const uint vec_arr_num) { - const float factor = 1.0f / (float)nbr; + const float factor = 1.0f / (float)vec_arr_num; zero_v3(r); - for (uint i = 0; i < nbr; i++) { + for (uint i = 0; i < vec_arr_num; i++) { madd_v3_v3fl(r, vec_arr[i], factor); } } @@ -904,9 +904,12 @@ void minmax_v2v2_v2(float min[2], float max[2], const float vec[2]) } } -void minmax_v3v3_v3_array(float r_min[3], float r_max[3], const float (*vec_arr)[3], int nbr) +void minmax_v3v3_v3_array(float r_min[3], + float r_max[3], + const float (*vec_arr)[3], + int var_arr_num) { - while (nbr--) { + while (var_arr_num--) { minmax_v3v3_v3(r_min, r_max, *vec_arr++); } } diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 8f2c86556aa..96ae0750899 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -26,6 +26,7 @@ # include "BLI_math_vector.h" # include "BLI_polyfill_2d.h" # include "BLI_set.hh" +# include "BLI_sort.hh" # include "BLI_span.hh" # include "BLI_task.h" # include "BLI_task.hh" @@ -37,10 +38,6 @@ # include "BLI_mesh_intersect.hh" -# ifdef WITH_TBB -# include <tbb/parallel_sort.h> -# endif - // # define PERFDEBUG namespace blender::meshintersect { @@ -672,11 +669,7 @@ void IMesh::populate_vert(int max_verts) * TODO: when all debugged, set fix_order = false. */ const bool fix_order = true; if (fix_order) { -# ifdef WITH_TBB - tbb::parallel_sort(vert_.begin(), vert_.end(), [](const Vert *a, const Vert *b) { -# else - std::sort(vert_.begin(), vert_.end(), [](const Vert *a, const Vert *b) { -# endif + blender::parallel_sort(vert_.begin(), vert_.end(), [](const Vert *a, const Vert *b) { if (a->orig != NO_INDEX && b->orig != NO_INDEX) { return a->orig < b->orig; } diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index f94d49cf1dd..6c576627fa0 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -313,8 +313,8 @@ bool BLI_filename_make_safe(char *fname) bool BLI_path_make_safe(char *path) { - /* Simply apply BLI_filename_make_safe() over each component of the path. - * Luckily enough, same 'safe' rules applies to filenames and dirnames. */ + /* Simply apply #BLI_filename_make_safe() over each component of the path. + * Luckily enough, same 'safe' rules applies to file & directory names. */ char *curr_slash, *curr_path = path; bool changed = false; bool skip_first = false; diff --git a/source/blender/blenlib/intern/rand.cc b/source/blender/blenlib/intern/rand.cc index 27774cad31b..17bf5585f3f 100644 --- a/source/blender/blenlib/intern/rand.cc +++ b/source/blender/blenlib/intern/rand.cc @@ -374,6 +374,15 @@ void RandomNumberGenerator::seed_random(uint32_t seed) this->seed(seed + hash[seed & 255]); } +int RandomNumberGenerator::round_probabilistic(float x) +{ + /* Support for negative values can be added when necessary. */ + BLI_assert(x >= 0.0f); + const float round_up_probability = fractf(x); + const bool round_up = round_up_probability > this->get_float(); + return (int)x + (int)round_up; +} + float2 RandomNumberGenerator::get_unit_float2() { float a = (float)(M_PI * 2.0) * this->get_float(); diff --git a/source/blender/blenlib/intern/timeit.cc b/source/blender/blenlib/intern/timeit.cc index 2dcfe2e6ab1..f11f9c4ad94 100644 --- a/source/blender/blenlib/intern/timeit.cc +++ b/source/blender/blenlib/intern/timeit.cc @@ -2,6 +2,8 @@ #include "BLI_timeit.hh" +#include <algorithm> + namespace blender::timeit { void print_duration(Nanoseconds duration) @@ -17,4 +19,20 @@ void print_duration(Nanoseconds duration) } } +ScopedTimerAveraged::~ScopedTimerAveraged() +{ + const TimePoint end = Clock::now(); + const Nanoseconds duration = end - start_; + + total_count_++; + total_time_ += duration; + min_time_ = std::min(duration, min_time_); + + std::cout << "Timer '" << name_ << "': (Average: "; + print_duration(total_time_ / total_count_); + std::cout << ", Min: "; + print_duration(min_time_); + std::cout << ")\n"; +} + } // namespace blender::timeit diff --git a/source/blender/functions/tests/FN_cpp_type_test.cc b/source/blender/blenlib/tests/BLI_cpp_type_test.cc index 6d9310874f5..94456e1ee28 100644 --- a/source/blender/functions/tests/FN_cpp_type_test.cc +++ b/source/blender/blenlib/tests/BLI_cpp_type_test.cc @@ -2,10 +2,10 @@ #include "testing/testing.h" -#include "FN_cpp_type.hh" -#include "FN_cpp_type_make.hh" +#include "BLI_cpp_type.hh" +#include "BLI_cpp_type_make.hh" -namespace blender::fn::tests { +namespace blender::tests { static const int default_constructed_value = 1; static const int copy_constructed_value = 2; @@ -74,11 +74,11 @@ struct TestType { } }; -} // namespace blender::fn::tests +} // namespace blender::tests -MAKE_CPP_TYPE(TestType, blender::fn::tests::TestType, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(TestType, blender::tests::TestType, CPPTypeFlags::BasicType) -namespace blender::fn::tests { +namespace blender::tests { static const CPPType &CPPType_TestType = CPPType::get<TestType>(); @@ -323,4 +323,28 @@ TEST(cpp_type, DebugPrint) EXPECT_EQ(text, "42"); } -} // namespace blender::fn::tests +TEST(cpp_type, ToStaticType) +{ + Vector<const CPPType *> types; + bool found_unsupported_type = false; + auto fn = [&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (!std::is_same_v<T, void>) { + types.append(&CPPType::get<T>()); + } + else { + found_unsupported_type = true; + } + }; + CPPType::get<std::string>().to_static_type_tag<int, float, std::string>(fn); + CPPType::get<float>().to_static_type_tag<int, float, std::string>(fn); + EXPECT_FALSE(found_unsupported_type); + CPPType::get<int64_t>().to_static_type_tag<int, float, std::string>(fn); + EXPECT_TRUE(found_unsupported_type); + + EXPECT_EQ(types.size(), 2); + EXPECT_EQ(types[0], &CPPType::get<std::string>()); + EXPECT_EQ(types[1], &CPPType::get<float>()); +} + +} // namespace blender::tests diff --git a/source/blender/functions/tests/FN_generic_array_test.cc b/source/blender/blenlib/tests/BLI_generic_array_test.cc index 5420a809ffc..52bc7728a6a 100644 --- a/source/blender/functions/tests/FN_generic_array_test.cc +++ b/source/blender/blenlib/tests/BLI_generic_array_test.cc @@ -5,10 +5,9 @@ #include "MEM_guardedalloc.h" #include "BLI_array.hh" +#include "BLI_generic_array.hh" -#include "FN_generic_array.hh" - -namespace blender::fn::tests { +namespace blender::tests { TEST(generic_array, TypeConstructor) { @@ -115,4 +114,4 @@ TEST(generic_array, InContainer) } } -} // namespace blender::fn::tests +} // namespace blender::tests diff --git a/source/blender/functions/tests/FN_generic_span_test.cc b/source/blender/blenlib/tests/BLI_generic_span_test.cc index 19b3ceaf44e..fe07a67d63b 100644 --- a/source/blender/functions/tests/FN_generic_span_test.cc +++ b/source/blender/blenlib/tests/BLI_generic_span_test.cc @@ -2,9 +2,9 @@ #include "testing/testing.h" -#include "FN_generic_span.hh" +#include "BLI_generic_span.hh" -namespace blender::fn::tests { +namespace blender::tests { TEST(generic_span, TypeConstructor) { @@ -50,4 +50,4 @@ TEST(generic_mutable_span, BufferAndSizeConstructor) EXPECT_EQ(values[2], 20); } -} // namespace blender::fn::tests +} // namespace blender::tests diff --git a/source/blender/functions/tests/FN_generic_vector_array_test.cc b/source/blender/blenlib/tests/BLI_generic_vector_array_test.cc index 6a83e6094b4..105f3603914 100644 --- a/source/blender/functions/tests/FN_generic_vector_array_test.cc +++ b/source/blender/blenlib/tests/BLI_generic_vector_array_test.cc @@ -2,9 +2,9 @@ #include "testing/testing.h" -#include "FN_generic_vector_array.hh" +#include "BLI_generic_vector_array.hh" -namespace blender::fn::tests { +namespace blender::tests { TEST(generic_vector_array, Construct) { @@ -40,4 +40,4 @@ TEST(generic_vector_array, Extend) EXPECT_EQ(vector_array[2].size(), 0); } -} // namespace blender::fn::tests +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_vector_test.cc b/source/blender/blenlib/tests/BLI_math_vector_test.cc index 35a111f04db..8c310645d6d 100644 --- a/source/blender/blenlib/tests/BLI_math_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_math_vector_test.cc @@ -85,4 +85,4 @@ TEST(math_vector, Clamp) EXPECT_EQ(result_2.z, -50); } -} // namespace blender::tests
\ No newline at end of file +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc index 95cf0f17d2e..fabc2412828 100644 --- a/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc +++ b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc @@ -39,11 +39,13 @@ class IMeshBuilder { return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN); } - /* + /** * Spec should have form: - * #verts #faces - * mpq_class mpq_class mpq_clas [#verts lines] - * int int int ... [#faces lines; indices into verts for given face] + * <pre> + * #verts #faces + * mpq_class mpq_class mpq_clas [#verts lines] + * int int int ... [#faces lines; indices into verts for given face] + * </pre> */ IMeshBuilder(const char *spec) { diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc index 4ddf656ff2e..d6a7a338d13 100644 --- a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc +++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc @@ -42,11 +42,13 @@ class IMeshBuilder { return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN); } - /* + /** * Spec should have form: - * #verts #faces - * mpq_class mpq_class mpq_clas [#verts lines] - * int int int ... [#faces lines; indices into verts for given face] + * <pre> + * #verts #faces + * mpq_class mpq_class mpq_clas [#verts lines] + * int int int ... [#faces lines; indices into verts for given face] + * </pre> */ IMeshBuilder(const char *spec) { diff --git a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc index eae90f130cd..0ff488202c2 100644 --- a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc +++ b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc @@ -177,17 +177,17 @@ TEST(ghash, TextMurmur2a) /* Int: uniform 100M first integers. */ -static void int_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +static void int_ghash_tests(GHash *ghash, const char *id, const unsigned int count) { printf("\n========== STARTING %s ==========\n", id); { - unsigned int i = nbr; + unsigned int i = count; TIMEIT_START(int_insert); #ifdef GHASH_RESERVE - BLI_ghash_reserve(ghash, nbr); + BLI_ghash_reserve(ghash, count); #endif while (i--) { @@ -200,7 +200,7 @@ static void int_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr PRINTF_GHASH_STATS(ghash); { - unsigned int i = nbr; + unsigned int i = count; TIMEIT_START(int_lookup); @@ -266,17 +266,17 @@ TEST(ghash, IntMurmur2a100000000) /* Int: random 50M integers. */ -static void randint_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +static void randint_ghash_tests(GHash *ghash, const char *id, const unsigned int count) { printf("\n========== STARTING %s ==========\n", id); - unsigned int *data = (unsigned int *)MEM_mallocN(sizeof(*data) * (size_t)nbr, __func__); + unsigned int *data = (unsigned int *)MEM_mallocN(sizeof(*data) * (size_t)count, __func__); unsigned int *dt; unsigned int i; { RNG *rng = BLI_rng_new(1); - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { *dt = BLI_rng_get_uint(rng); } BLI_rng_free(rng); @@ -286,10 +286,10 @@ static void randint_ghash_tests(GHash *ghash, const char *id, const unsigned int TIMEIT_START(int_insert); #ifdef GHASH_RESERVE - BLI_ghash_reserve(ghash, nbr); + BLI_ghash_reserve(ghash, count); #endif - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { BLI_ghash_insert(ghash, POINTER_FROM_UINT(*dt), POINTER_FROM_UINT(*dt)); } @@ -301,7 +301,7 @@ static void randint_ghash_tests(GHash *ghash, const char *id, const unsigned int { TIMEIT_START(int_lookup); - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { void *v = BLI_ghash_lookup(ghash, POINTER_FROM_UINT(*dt)); EXPECT_EQ(POINTER_AS_UINT(v), *dt); } @@ -375,18 +375,18 @@ TEST(ghash, Int4NoHash50000000) /* Int_v4: 20M of randomly-generated integer vectors. */ -static void int4_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +static void int4_ghash_tests(GHash *ghash, const char *id, const unsigned int count) { printf("\n========== STARTING %s ==========\n", id); - void *data_v = MEM_mallocN(sizeof(unsigned int[4]) * (size_t)nbr, __func__); + void *data_v = MEM_mallocN(sizeof(unsigned int[4]) * (size_t)count, __func__); unsigned int(*data)[4] = (unsigned int(*)[4])data_v; unsigned int(*dt)[4]; unsigned int i, j; { RNG *rng = BLI_rng_new(1); - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { for (j = 4; j--;) { (*dt)[j] = BLI_rng_get_uint(rng); } @@ -398,10 +398,10 @@ static void int4_ghash_tests(GHash *ghash, const char *id, const unsigned int nb TIMEIT_START(int_v4_insert); #ifdef GHASH_RESERVE - BLI_ghash_reserve(ghash, nbr); + BLI_ghash_reserve(ghash, count); #endif - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { BLI_ghash_insert(ghash, *dt, POINTER_FROM_UINT(i)); } @@ -413,7 +413,7 @@ static void int4_ghash_tests(GHash *ghash, const char *id, const unsigned int nb { TIMEIT_START(int_v4_lookup); - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { void *v = BLI_ghash_lookup(ghash, (void *)(*dt)); EXPECT_EQ(POINTER_AS_UINT(v), i); } @@ -483,25 +483,25 @@ TEST(ghash, Int2NoHash50000000) /* MultiSmall: create and manipulate a lot of very small ghash's * (90% < 10 items, 9% < 100 items, 1% < 1000 items). */ -static void multi_small_ghash_tests_one(GHash *ghash, RNG *rng, const unsigned int nbr) +static void multi_small_ghash_tests_one(GHash *ghash, RNG *rng, const unsigned int count) { - unsigned int *data = (unsigned int *)MEM_mallocN(sizeof(*data) * (size_t)nbr, __func__); + unsigned int *data = (unsigned int *)MEM_mallocN(sizeof(*data) * (size_t)count, __func__); unsigned int *dt; unsigned int i; - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { *dt = BLI_rng_get_uint(rng); } #ifdef GHASH_RESERVE - BLI_ghash_reserve(ghash, nbr); + BLI_ghash_reserve(ghash, count); #endif - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { BLI_ghash_insert(ghash, POINTER_FROM_UINT(*dt), POINTER_FROM_UINT(*dt)); } - for (i = nbr, dt = data; i--; dt++) { + for (i = count, dt = data; i--; dt++) { void *v = BLI_ghash_lookup(ghash, POINTER_FROM_UINT(*dt)); EXPECT_EQ(POINTER_AS_UINT(v), *dt); } @@ -510,7 +510,7 @@ static void multi_small_ghash_tests_one(GHash *ghash, RNG *rng, const unsigned i MEM_freeN(data); } -static void multi_small_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +static void multi_small_ghash_tests(GHash *ghash, const char *id, const unsigned int count) { printf("\n========== STARTING %s ==========\n", id); @@ -518,22 +518,22 @@ static void multi_small_ghash_tests(GHash *ghash, const char *id, const unsigned TIMEIT_START(multi_small_ghash); - unsigned int i = nbr; + unsigned int i = count; while (i--) { - const int nbr = 1 + (BLI_rng_get_int(rng) % TESTCASE_SIZE_SMALL) * - (!(i % 100) ? 100 : (!(i % 10) ? 10 : 1)); - multi_small_ghash_tests_one(ghash, rng, nbr); + const int count = 1 + (BLI_rng_get_int(rng) % TESTCASE_SIZE_SMALL) * + (!(i % 100) ? 100 : (!(i % 10) ? 10 : 1)); + multi_small_ghash_tests_one(ghash, rng, count); } TIMEIT_END(multi_small_ghash); TIMEIT_START(multi_small2_ghash); - unsigned int i = nbr; + unsigned int i = count; while (i--) { - const int nbr = 1 + (BLI_rng_get_int(rng) % TESTCASE_SIZE_SMALL) / 2 * - (!(i % 100) ? 100 : (!(i % 10) ? 10 : 1)); - multi_small_ghash_tests_one(ghash, rng, nbr); + const int count = 1 + (BLI_rng_get_int(rng) % TESTCASE_SIZE_SMALL) / 2 * + (!(i % 100) ? 100 : (!(i % 10) ? 10 : 1)); + multi_small_ghash_tests_one(ghash, rng, count); } TIMEIT_END(multi_small2_ghash); diff --git a/source/blender/blenlib/tests/performance/BLI_task_performance_test.cc b/source/blender/blenlib/tests/performance/BLI_task_performance_test.cc index 33a196569e9..98fafe55d69 100644 --- a/source/blender/blenlib/tests/performance/BLI_task_performance_test.cc +++ b/source/blender/blenlib/tests/performance/BLI_task_performance_test.cc @@ -135,17 +135,17 @@ static void task_listbase_test_do(ListBase *list, NUM_RUN_AVERAGED); } -static void task_listbase_test(const char *id, const int nbr, const bool use_threads) +static void task_listbase_test(const char *id, const int count, const bool use_threads) { printf("\n========== STARTING %s ==========\n", id); ListBase list = {nullptr, nullptr}; - LinkData *items_buffer = (LinkData *)MEM_calloc_arrayN(nbr, sizeof(*items_buffer), __func__); + LinkData *items_buffer = (LinkData *)MEM_calloc_arrayN(count, sizeof(*items_buffer), __func__); BLI_threadapi_init(); int num_items = 0; - for (int i = 0; i < nbr; i++) { + for (int i = 0; i < count; i++) { BLI_addtail(&list, &items_buffer[i]); num_items++; } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 3ae26dea767..2f6f0d5c9fa 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1510,27 +1510,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 292, 9)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_GEOMETRY) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_MATH && node->storage == NULL) { - const int old_use_attibute_a = (1 << 0); - const int old_use_attibute_b = (1 << 1); - NodeAttributeMath *data = MEM_callocN(sizeof(NodeAttributeMath), "NodeAttributeMath"); - data->operation = NODE_MATH_ADD; - data->input_type_a = (node->custom2 & old_use_attibute_a) ? - GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE : - GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - data->input_type_b = (node->custom2 & old_use_attibute_b) ? - GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE : - GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node->storage = data; - } - } - } - } - FOREACH_NODETREE_END; - /* Default properties editors to auto outliner sync. */ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { @@ -1668,39 +1647,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - if (!MAIN_VERSION_ATLEAST(bmain, 293, 3)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type != NTREE_GEOMETRY) { - continue; - } - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_LEGACY_POINT_INSTANCE && node->storage == NULL) { - NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( - sizeof(NodeGeometryPointInstance), __func__); - data->instance_type = node->custom1; - data->flag = (node->custom2 ? 0 : GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION); - node->storage = data; - } - } - } - FOREACH_NODETREE_END; - } - - if (!MAIN_VERSION_ATLEAST(bmain, 293, 4)) { - /* Add support for all operations to the "Attribute Math" node. */ - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_GEOMETRY) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_MATH) { - NodeAttributeMath *data = (NodeAttributeMath *)node->storage; - data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - } - } - } - } - FOREACH_NODETREE_END; - } - if (!MAIN_VERSION_ATLEAST(bmain, 293, 5)) { /* Change Nishita sky model Altitude unit. */ FOREACH_NODETREE_BEGIN (bmain, ntree, id) { @@ -1744,24 +1690,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_END; } - if (!MAIN_VERSION_ATLEAST(bmain, 293, 8)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type != NTREE_GEOMETRY) { - continue; - } - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE && node->storage == NULL) { - NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( - sizeof(NodeAttributeRandomize), __func__); - data->data_type = node->custom1; - data->operation = GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE; - node->storage = data; - } - } - } - FOREACH_NODETREE_END; - } - if (!MAIN_VERSION_ATLEAST(bmain, 293, 9)) { if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "bokeh_overblur")) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { @@ -1783,24 +1711,9 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Result", "Distance"); - } - } - FOREACH_NODETREE_END; } if (!MAIN_VERSION_ATLEAST(bmain, 293, 10)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name( - ntree, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Location", "Position"); - } - } - FOREACH_NODETREE_END; - LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { /* Fix old scene with too many samples that were not being used. * Now they are properly used and might produce a huge slowdown. @@ -1886,16 +1799,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) light->volume_fac = 1.0f; } } - - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type == NTREE_GEOMETRY) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_FILL) { - node->custom2 = ATTR_DOMAIN_AUTO; - } - } - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 293, 15)) { @@ -1940,13 +1843,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Grid", "Density"); - } - } - FOREACH_NODETREE_END; - if (!DNA_struct_elem_find(fd->filesdna, "bArmature", "float", "axes_position")) { /* Convert the axes draw position to its old default (tip of bone). */ LISTBASE_FOREACH (struct bArmature *, arm, &bmain->armatures) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index eead735b305..51b5cab1f7c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -527,7 +527,6 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree) GEO_NODE_TRIM_CURVE, GEO_NODE_REPLACE_MATERIAL, GEO_NODE_SUBDIVIDE_MESH, - GEO_NODE_LEGACY_ATTRIBUTE_REMOVE, GEO_NODE_TRIANGULATE)) { bNodeSocket *geometry_socket = node->inputs.first; add_realize_instances_before_socket(ntree, node, geometry_socket); @@ -600,30 +599,6 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } if (!MAIN_VERSION_ATLEAST(bmain, 300, 3)) { - /* Use new texture socket in Attribute Sample Texture node. */ - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type != NTREE_GEOMETRY) { - continue; - } - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type != GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE) { - continue; - } - if (node->id == NULL) { - continue; - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (socket->type == SOCK_TEXTURE) { - bNodeSocketValueTexture *socket_value = (bNodeSocketValueTexture *) - socket->default_value; - socket_value->value = (Tex *)node->id; - break; - } - } - node->id = NULL; - } - } - sort_linked_ids(bmain); assert_sorted_ids(bmain); } @@ -951,141 +926,6 @@ static bNodeSocket *do_version_replace_float_size_with_vector(bNodeTree *ntree, return new_socket; } -static bool geometry_node_is_293_legacy(const short node_type) -{ - switch (node_type) { - /* Not legacy: No attribute inputs or outputs. */ - case GEO_NODE_TRIANGULATE: - case GEO_NODE_TRANSFORM: - case GEO_NODE_MESH_BOOLEAN: - case GEO_NODE_IS_VIEWPORT: - case GEO_NODE_SUBDIVIDE_MESH: - case GEO_NODE_MESH_PRIMITIVE_CUBE: - case GEO_NODE_MESH_PRIMITIVE_CIRCLE: - case GEO_NODE_MESH_PRIMITIVE_UV_SPHERE: - case GEO_NODE_MESH_PRIMITIVE_CYLINDER: - case GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE: - case GEO_NODE_MESH_PRIMITIVE_CONE: - case GEO_NODE_MESH_PRIMITIVE_LINE: - case GEO_NODE_MESH_PRIMITIVE_GRID: - case GEO_NODE_BOUNDING_BOX: - case GEO_NODE_RESAMPLE_CURVE: - case GEO_NODE_INPUT_MATERIAL: - case GEO_NODE_REPLACE_MATERIAL: - case GEO_NODE_CURVE_LENGTH: - case GEO_NODE_CONVEX_HULL: - case GEO_NODE_SEPARATE_COMPONENTS: - case GEO_NODE_CURVE_PRIMITIVE_STAR: - case GEO_NODE_CURVE_PRIMITIVE_SPIRAL: - case GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER: - case GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT: - case GEO_NODE_CURVE_PRIMITIVE_CIRCLE: - case GEO_NODE_VIEWER: - case GEO_NODE_CURVE_PRIMITIVE_LINE: - case GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL: - case GEO_NODE_FILL_CURVE: - case GEO_NODE_TRIM_CURVE: - case GEO_NODE_CURVE_TO_MESH: - return false; - - /* Not legacy: Newly added with fields patch. */ - case GEO_NODE_INPUT_POSITION: - case GEO_NODE_SET_POSITION: - case GEO_NODE_INPUT_INDEX: - case GEO_NODE_INPUT_NORMAL: - case GEO_NODE_CAPTURE_ATTRIBUTE: - return false; - - /* Maybe legacy: Might need special attribute handling, depending on design. */ - case GEO_NODE_SWITCH: - case GEO_NODE_JOIN_GEOMETRY: - case GEO_NODE_LEGACY_ATTRIBUTE_REMOVE: - case GEO_NODE_OBJECT_INFO: - case GEO_NODE_COLLECTION_INFO: - return false; - - /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to - * generate a mesh for all grids in the volume. */ - case GEO_NODE_LEGACY_VOLUME_TO_MESH: - return false; - - /* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ - case GEO_NODE_LEGACY_CURVE_ENDPOINTS: - case GEO_NODE_LEGACY_CURVE_TO_POINTS: - return true; - - /* Legacy: Attribute operation completely replaced by field nodes. */ - case GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE: - case GEO_NODE_LEGACY_ATTRIBUTE_MATH: - case GEO_NODE_LEGACY_ATTRIBUTE_FILL: - case GEO_NODE_LEGACY_ATTRIBUTE_MIX: - case GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP: - case GEO_NODE_LEGACY_ATTRIBUTE_COMPARE: - case GEO_NODE_LEGACY_POINT_ROTATE: - case GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR: - case GEO_NODE_LEGACY_POINT_SCALE: - case GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE: - case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE: - case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: - case GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE: - case GEO_NODE_LEGACY_ATTRIBUTE_CLAMP: - case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH: - case GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ: - case GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ: - return true; - - /* Legacy: Replaced by field node depending on another geometry. */ - case GEO_NODE_LEGACY_RAYCAST: - case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER: - case GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY: - return true; - - /* Legacy: Simple selection attribute input. */ - case GEO_NODE_LEGACY_MESH_TO_CURVE: - case GEO_NODE_LEGACY_POINT_SEPARATE: - case GEO_NODE_LEGACY_CURVE_SELECT_HANDLES: - case GEO_NODE_LEGACY_CURVE_SPLINE_TYPE: - case GEO_NODE_LEGACY_CURVE_REVERSE: - case GEO_NODE_LEGACY_MATERIAL_ASSIGN: - case GEO_NODE_LEGACY_CURVE_SET_HANDLES: - return true; - - /* Legacy: More complex attribute inputs or outputs. */ - case GEO_NODE_LEGACY_SUBDIVISION_SURFACE: /* Used "crease" attribute. */ - case GEO_NODE_LEGACY_EDGE_SPLIT: /* Needs selection input version. */ - case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */ - case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ - case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ - case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ - case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */ - case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */ - case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */ - case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */ - return true; - } - return false; -} - -static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree) -{ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (geometry_node_is_293_legacy(node->type)) { - if (strstr(node->idname, "Legacy")) { - /* Make sure we haven't changed this idname already, better safe than sorry. */ - continue; - } - - char temp_idname[sizeof(node->idname)]; - BLI_strncpy(temp_idname, node->idname, sizeof(node->idname)); - - BLI_snprintf(node->idname, - sizeof(node->idname), - "GeometryNodeLegacy%s", - temp_idname + strlen("GeometryNode")); - } - } -} - static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) { StripTransform *transform = seq->strip->transform; @@ -1873,24 +1713,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 300, 19)) { - /* Add node storage for subdivision surface node. */ - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_GEOMETRY) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_LEGACY_SUBDIVISION_SURFACE) { - if (node->storage == NULL) { - NodeGeometrySubdivisionSurface *data = MEM_callocN( - sizeof(NodeGeometrySubdivisionSurface), __func__); - data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; - data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; - node->storage = data; - } - } - } - } - } - FOREACH_NODETREE_END; - /* Disable Fade Inactive Overlay by default as it is redundant after introducing flash on * mode transfer. */ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { @@ -2091,30 +1913,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - /* Deprecate the random float node in favor of the random value node. */ - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type != NTREE_GEOMETRY) { - continue; - } - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type != FN_NODE_LEGACY_RANDOM_FLOAT) { - continue; - } - if (strstr(node->idname, "Legacy")) { - /* Make sure we haven't changed this idname already. */ - continue; - } - - char temp_idname[sizeof(node->idname)]; - BLI_strncpy(temp_idname, node->idname, sizeof(node->idname)); - - BLI_snprintf(node->idname, - sizeof(node->idname), - "FunctionNodeLegacy%s", - temp_idname + strlen("FunctionNode")); - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 300, 29)) { @@ -2141,12 +1939,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type == NTREE_GEOMETRY) { - version_geometry_nodes_change_legacy_names(ntree); - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 300, 31)) { @@ -2304,7 +2096,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); version_geometry_nodes_set_position_node_offset(ntree); - version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh"); } /* Add storage to viewer node. */ @@ -2638,12 +2429,32 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* Keep this block, even when empty. */ - /* Deprecate the attribute remove node. It was hidden and is replaced by a version without a - * multi-input socket. */ - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type == NTREE_GEOMETRY) { - version_node_id( - ntree, GEO_NODE_LEGACY_ATTRIBUTE_REMOVE, "GeometryNodeLegacyAttributeRemove"); + /* Initialize brush curves sculpt settings. */ + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->ob_mode != OB_MODE_SCULPT_CURVES) { + continue; + } + if (brush->curves_sculpt_settings != NULL) { + continue; + } + brush->curves_sculpt_settings = MEM_callocN(sizeof(BrushCurvesSculptSettings), __func__); + brush->curves_sculpt_settings->add_amount = 1; + } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->toolsettings && scene->toolsettings->curves_sculpt && + scene->toolsettings->curves_sculpt->curve_length == 0.0f) { + scene->toolsettings->curves_sculpt->curve_length = 0.3f; + } + } + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_OUTLINER) { + SpaceOutliner *space_outliner = (SpaceOutliner *)sl; + space_outliner->filter &= ~SO_FILTER_CLEARED_1; + } + } } } } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 5b026c1cca0..16297149cc3 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -2064,8 +2064,8 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) } for (part = bmain->particles.first; part; part = part->id.next) { - if (part->ren_child_nbr == 0) { - part->ren_child_nbr = part->child_nbr; + if (part->child_render_percent == 0) { + part->child_render_percent = part->child_percent; } } diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 0d16cc9dffd..a5994b52bc2 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -17,7 +17,7 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); struct BMeshCreateParams { - uint use_toolflags : 1; + bool use_toolflags : true; }; /** diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index fd14a3416e2..118e7751cbc 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -229,7 +229,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar * work, but also accessing normals on an incomplete mesh, for example when restoring undo steps * in edit mode. */ const float(*vert_normals)[3] = nullptr; - if (!BKE_mesh_vertex_normals_are_dirty(me)) { + if (params->calc_vert_normal) { vert_normals = BKE_mesh_vertex_normals_ensure(me); } diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index 0bd70749cb1..cc1ad71549d 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -18,11 +18,12 @@ void BM_mesh_cd_flag_apply(BMesh *bm, char cd_flag); char BM_mesh_cd_flag_from_bmesh(BMesh *bm); struct BMeshFromMeshParams { - uint calc_face_normal : 1; + bool calc_face_normal : true; + bool calc_vert_normal : true; /* add a vertex CD_SHAPE_KEYINDEX layer */ - uint add_key_index : 1; + bool add_key_index : true; /* set vertex coordinates from the shapekey */ - uint use_shapekey : 1; + bool use_shapekey : true; /* define the active shape key (index + 1) */ int active_shapekey; struct CustomData_MeshMasks cd_mask_extra; @@ -42,7 +43,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const struct Mesh *me, const struct BMeshFrom struct BMeshToMeshParams { /** Update object hook indices & vertex parents. */ - uint calc_object_remap : 1; + bool calc_object_remap : true; /** * This re-assigns shape-key indices. Only do if the BMesh will have continued use * to update the mesh & shape key in the future. @@ -52,12 +53,12 @@ struct BMeshToMeshParams { * so a second flush or edit-mode exit doesn't run with indices * that have become invalid from updating the shape-key, see T71865. */ - uint update_shapekey_indices : 1; + bool update_shapekey_indices : true; /** * Instead of copying the basis shape-key into the #MVert array, * copy the #BMVert.co directly to #MVert.co (used for reading undo data). */ - uint active_shapekey_to_mvert : 1; + bool active_shapekey_to_mvert : true; struct CustomData_MeshMasks cd_mask_extra; }; /** diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index d61b2c5bb43..cd89a550279 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -580,7 +580,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, /* We validate clnors data on the fly - cheapest way to do! */ int clnors_avg[2] = {0, 0}; const short(*clnor_ref)[2] = NULL; - int clnors_nbr = 0; + int clnors_count = 0; bool clnors_invalid = false; const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; @@ -649,7 +649,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] : (const void *)BM_ELEM_CD_GET_VOID_P( lfan_pivot, cd_loop_clnors_offset); - if (clnors_nbr) { + if (clnors_count) { clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); } else { @@ -657,7 +657,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, } clnors_avg[0] += (*clnor)[0]; clnors_avg[1] += (*clnor)[1]; - clnors_nbr++; + clnors_count++; /* We store here a pointer to all custom lnors processed. */ BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); } @@ -706,8 +706,8 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, if (clnors_invalid) { short *clnor; - clnors_avg[0] /= clnors_nbr; - clnors_avg[1] /= clnors_nbr; + clnors_avg[0] /= clnors_count; + clnors_avg[1] /= clnors_count; /* Fix/update all clnors of this fan with computed average value. */ /* Prints continuously when merge custom normals, so commenting. */ @@ -1517,7 +1517,7 @@ static void bm_mesh_loops_assign_normal_data(BMesh *bm, BLI_BITMAP_ENABLE(done_loops, i); } else { - int nbr_nors = 0; + int avg_nor_count = 0; float avg_nor[3]; short clnor_data_tmp[2], *clnor_data; @@ -1530,7 +1530,7 @@ static void bm_mesh_loops_assign_normal_data(BMesh *bm, short *clnor = r_clnors_data ? &r_clnors_data[lidx] : BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset); - nbr_nors++; + avg_nor_count++; add_v3_v3(avg_nor, nor); BLI_SMALLSTACK_PUSH(clnors_data, clnor); @@ -1538,7 +1538,7 @@ static void bm_mesh_loops_assign_normal_data(BMesh *bm, BLI_BITMAP_ENABLE(done_loops, lidx); } - mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors); + mul_v3_fl(avg_nor, 1.0f / (float)avg_nor_count); BKE_lnor_space_custom_normal_to_data( lnors_spacearr->lspacearr[i], avg_nor, clnor_data_tmp); diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index 1bc5b70f874..8bc16324971 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -1869,7 +1869,7 @@ bool BM_face_exists_overlap_subset(BMVert **varr, const int len) for (int i = 0; i < len; i++) { BM_ITER_ELEM (f, &viter, varr[i], BM_FACES_OF_VERT) { if ((f->len <= len) && (BM_ELEM_API_FLAG_TEST(f, _FLAG_OVERLAP) == 0)) { - /* Check if all vers in this face are flagged. */ + /* Check if all verts in this face are flagged. */ BMLoop *l_iter, *l_first; if (is_init == false) { diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 5790d76936f..be5521d45ab 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -571,7 +571,7 @@ static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e) /* Return the count of edges between e1 and e2 when going around bv CCW. */ static int count_ccw_edges_between(EdgeHalf *e1, EdgeHalf *e2) { - int cnt = 0; + int count = 0; EdgeHalf *e = e1; do { @@ -579,9 +579,9 @@ static int count_ccw_edges_between(EdgeHalf *e1, EdgeHalf *e2) break; } e = e->next; - cnt++; + count++; } while (e != e1); - return cnt; + return count; } /* Assume bme1 and bme2 both share some vert. Do they share a face? diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 07d4172d037..81a392d38a5 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -517,7 +517,6 @@ static bool bm_decim_triangulate_begin(BMesh *bm, int *r_edges_tri_tot) { BMIter iter; BMFace *f; - bool has_quad = false; bool has_ngon = false; bool has_cut = false; @@ -533,7 +532,6 @@ static bool bm_decim_triangulate_begin(BMesh *bm, int *r_edges_tri_tot) BM_elem_index_set(l_iter, -1); /* set_dirty */ } while ((l_iter = l_iter->next) != l_first); - has_quad |= (f->len > 3); has_ngon |= (f->len > 4); } diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index eafa6614d31..0fdd7647f8d 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -318,12 +318,6 @@ extern "C" { * - output nodes can have different priorities in the WorkScheduler. * This is implemented in the COM_execute function. * - * \param view_settings: - * reference to view settings used for color management - * - * \param display_settings: - * reference to display settings used for color management - * * OCIO_TODO: this options only used in rare cases, namely in output file node, * so probably this settings could be passed in a nicer way. * should be checked further, probably it'll be also needed for preview @@ -335,8 +329,6 @@ void COM_execute(RenderData *render_data, Scene *scene, bNodeTree *node_tree, int rendering, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name); /** diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index f17a24c6b9d..2c55af1779a 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -12,8 +12,6 @@ CompositorContext::CompositorContext() quality_ = eCompositorQuality::High; hasActiveOpenCLDevices_ = false; fast_calculation_ = false; - view_settings_ = nullptr; - display_settings_ = nullptr; bnodetree_ = nullptr; } diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index 430bab48352..480f70f4193 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -64,10 +64,6 @@ class CompositorContext { */ bool fast_calculation_; - /* \brief color management settings */ - const ColorManagedViewSettings *view_settings_; - const ColorManagedDisplaySettings *display_settings_; - /** * \brief active rendering view name */ @@ -153,38 +149,6 @@ class CompositorContext { } /** - * \brief set view settings of color management - */ - void set_view_settings(const ColorManagedViewSettings *view_settings) - { - view_settings_ = view_settings; - } - - /** - * \brief get view settings of color management - */ - const ColorManagedViewSettings *get_view_settings() const - { - return view_settings_; - } - - /** - * \brief set display settings of color management - */ - void set_display_settings(const ColorManagedDisplaySettings *display_settings) - { - display_settings_ = display_settings; - } - - /** - * \brief get display settings of color management - */ - const ColorManagedDisplaySettings *get_display_settings() const - { - return display_settings_; - } - - /** * \brief set the quality */ void set_quality(eCompositorQuality quality) diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 0d9d3b111b0..66dbabe71b5 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -468,8 +468,8 @@ void DebugInfo::delete_operation_exports() const std::string dir = get_operations_export_dir(); if (BLI_exists(dir.c_str())) { struct direntry *file_list; - int num_files = BLI_filelist_dir_contents(dir.c_str(), &file_list); - for (int i = 0; i < num_files; i++) { + int file_list_num = BLI_filelist_dir_contents(dir.c_str(), &file_list); + for (int i = 0; i < file_list_num; i++) { direntry *file = &file_list[i]; const eFileAttributes file_attrs = BLI_file_attributes(file->path); if (file_attrs & FILE_ATTR_ANY_LINK) { @@ -480,7 +480,7 @@ void DebugInfo::delete_operation_exports() BLI_delete(file->path, false, false); } } - BLI_filelist_free(file_list, num_files); + BLI_filelist_free(file_list, file_list_num); } } diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index 036e2bc8a91..c850585148a 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -23,8 +23,6 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name) { num_work_threads_ = WorkScheduler::get_num_cpu_threads(); @@ -45,8 +43,6 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, (editingtree->flag & NTREE_COM_OPENCL)); context_.set_render_data(rd); - context_.set_view_settings(view_settings); - context_.set_display_settings(display_settings); BLI_mutex_init(&work_mutex_); BLI_condition_init(&work_finished_cond_); diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index 9dd20d18144..2cd9e2d9001 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -160,8 +160,6 @@ class ExecutionSystem { bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name); /** diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 2f27699ce9c..788686f3036 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -221,8 +221,9 @@ PreviewOperation *NodeOperationBuilder::make_preview_operation() const bNodeInstanceHash *previews = context_->get_preview_hash(); if (previews) { - PreviewOperation *operation = new PreviewOperation(context_->get_view_settings(), - context_->get_display_settings(), + Scene *scene = context_->get_scene(); + PreviewOperation *operation = new PreviewOperation(&scene->view_settings, + &scene->display_settings, current_node_->get_bnode()->preview_xsize, current_node_->get_bnode()->preview_ysize); operation->set_bnodetree(context_->get_bnodetree()); diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc index 791cc327bfb..519ad93bcaf 100644 --- a/source/blender/compositor/intern/COM_compositor.cc +++ b/source/blender/compositor/intern/COM_compositor.cc @@ -49,8 +49,6 @@ void COM_execute(RenderData *render_data, Scene *scene, bNodeTree *node_tree, int rendering, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name) { /* Initialize mutex, TODO: this mutex init is actually not thread safe and @@ -80,14 +78,8 @@ void COM_execute(RenderData *render_data, /* Execute. */ const bool twopass = (node_tree->flag & NTREE_TWO_PASS) && !rendering; if (twopass) { - blender::compositor::ExecutionSystem fast_pass(render_data, - scene, - node_tree, - rendering, - true, - view_settings, - display_settings, - view_name); + blender::compositor::ExecutionSystem fast_pass( + render_data, scene, node_tree, rendering, true, view_name); fast_pass.execute(); if (node_tree->test_break(node_tree->tbh)) { @@ -97,7 +89,7 @@ void COM_execute(RenderData *render_data, } blender::compositor::ExecutionSystem system( - render_data, scene, node_tree, rendering, false, view_settings, display_settings, view_name); + render_data, scene, node_tree, rendering, false, view_name); system.execute(); BLI_mutex_unlock(&g_compositor.mutex); diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index b0568dc308e..f69511d88e6 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -35,12 +35,22 @@ void OutputFileNode::map_input_sockets(NodeConverter &converter, } } +void OutputFileNode::add_preview_to_first_linked_input(NodeConverter &converter) const +{ + NodeInput *first_socket = this->get_input_socket(0); + if (first_socket->is_linked()) { + converter.add_node_input_preview(first_socket); + } +} + void OutputFileNode::convert_to_operations(NodeConverter &converter, const CompositorContext &context) const { NodeImageMultiFile *storage = (NodeImageMultiFile *)this->get_bnode()->storage; const bool is_multiview = (context.get_render_data()->scemode & R_MULTIVIEW) != 0; + add_preview_to_first_linked_input(converter); + if (!context.is_rendering()) { /* only output files when rendering a sequence - * otherwise, it overwrites the output files just @@ -81,7 +91,6 @@ void OutputFileNode::convert_to_operations(NodeConverter &converter, map_input_sockets(converter, *output_operation); } else { /* single layer format */ - bool preview_added = false; for (NodeInput *input : inputs_) { if (input->is_linked()) { NodeImageMultiFileSocket *sockdata = @@ -97,47 +106,39 @@ void OutputFileNode::convert_to_operations(NodeConverter &converter, if (is_multiview && format->views_format == R_IMF_VIEWS_MULTIVIEW) { output_operation = new OutputOpenExrSingleLayerMultiViewOperation( + context.get_scene(), context.get_render_data(), context.get_bnodetree(), input->get_data_type(), format, path, - context.get_view_settings(), - context.get_display_settings(), context.get_view_name(), sockdata->save_as_render); } else if ((!is_multiview) || (format->views_format == R_IMF_VIEWS_INDIVIDUAL)) { - output_operation = new OutputSingleLayerOperation(context.get_render_data(), + output_operation = new OutputSingleLayerOperation(context.get_scene(), + context.get_render_data(), context.get_bnodetree(), input->get_data_type(), format, path, - context.get_view_settings(), - context.get_display_settings(), context.get_view_name(), sockdata->save_as_render); } else { /* R_IMF_VIEWS_STEREO_3D */ - output_operation = new OutputStereoOperation(context.get_render_data(), + output_operation = new OutputStereoOperation(context.get_scene(), + context.get_render_data(), context.get_bnodetree(), input->get_data_type(), format, path, sockdata->layer, - context.get_view_settings(), - context.get_display_settings(), context.get_view_name(), sockdata->save_as_render); } converter.add_operation(output_operation); converter.map_input_socket(input, output_operation->get_input_socket(0)); - - if (!preview_added) { - converter.add_node_input_preview(input); - preview_added = true; - } } } } diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.h b/source/blender/compositor/nodes/COM_OutputFileNode.h index be2464c1888..61d8700270e 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.h +++ b/source/blender/compositor/nodes/COM_OutputFileNode.h @@ -22,6 +22,7 @@ class OutputFileNode : public Node { const CompositorContext &context) const override; private: + void add_preview_to_first_linked_input(NodeConverter &converter) const; void add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const; void map_input_sockets(NodeConverter &converter, OutputOpenExrMultiLayerOperation &operation) const; diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc index 0e7db05980a..d02bc6e773d 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc @@ -24,6 +24,7 @@ void SplitViewerNode::convert_to_operations(NodeConverter &converter, NodeInput *image2Socket = this->get_input_socket(1); Image *image = (Image *)this->get_bnode()->id; ImageUser *image_user = (ImageUser *)this->get_bnode()->storage; + Scene *scene = context.get_scene(); SplitOperation *split_viewer_operation = new SplitOperation(); split_viewer_operation->set_split_percentage(this->get_bnode()->custom1); @@ -36,8 +37,8 @@ void SplitViewerNode::convert_to_operations(NodeConverter &converter, ViewerOperation *viewer_operation = new ViewerOperation(); viewer_operation->set_image(image); viewer_operation->set_image_user(image_user); - viewer_operation->set_view_settings(context.get_view_settings()); - viewer_operation->set_display_settings(context.get_display_settings()); + viewer_operation->set_view_settings(&scene->view_settings); + viewer_operation->set_display_settings(&scene->display_settings); viewer_operation->set_render_data(context.get_render_data()); viewer_operation->set_view_name(context.get_view_name()); diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc index 216df59cc47..be5f7b90e11 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.cc +++ b/source/blender/compositor/nodes/COM_TextureNode.cc @@ -17,8 +17,7 @@ void TextureNode::convert_to_operations(NodeConverter &converter, bNode *editor_node = this->get_bnode(); Tex *texture = (Tex *)editor_node->id; TextureOperation *operation = new TextureOperation(); - const ColorManagedDisplaySettings *display_settings = context.get_display_settings(); - bool scene_color_manage = !STREQ(display_settings->display_device, "None"); + bool scene_color_manage = !STREQ(context.get_scene()->display_settings.display_device, "None"); operation->set_texture(texture); operation->set_render_data(context.get_render_data()); operation->set_scene_color_manage(scene_color_manage); diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc index 1b335abc4d7..ebef331c62f 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cc +++ b/source/blender/compositor/nodes/COM_ViewerNode.cc @@ -37,8 +37,9 @@ void ViewerNode::convert_to_operations(NodeConverter &converter, viewer_operation->set_render_data(context.get_render_data()); viewer_operation->set_view_name(context.get_view_name()); - viewer_operation->set_view_settings(context.get_view_settings()); - viewer_operation->set_display_settings(context.get_display_settings()); + Scene *scene = context.get_scene(); + viewer_operation->set_view_settings(&scene->view_settings); + viewer_operation->set_display_settings(&scene->display_settings); viewer_operation->set_canvas_input_index(0); if (!image_socket->is_linked()) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 23520364bf0..e8b61ff229e 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -114,7 +114,7 @@ void CompositorOperation::deinit_execution() void CompositorOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - float color[8]; // 7 is enough + float color[8]; /* 7 is enough. */ float *buffer = output_buffer_; float *zbuffer = depth_buffer_; diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index db29a84f7da..aeaf6b659e3 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -4,6 +4,7 @@ #include "COM_OutputFileMultiViewOperation.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_main.h" #include "BKE_scene.h" @@ -16,24 +17,16 @@ namespace blender::compositor { /************************************ OpenEXR Singlelayer Multiview ******************************/ OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOperation( + const Scene *scene, const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name, const bool save_as_render) - : OutputSingleLayerOperation(rd, - tree, - datatype, - format, - path, - view_settings, - display_settings, - view_name, - save_as_render) + : OutputSingleLayerOperation( + scene, rd, tree, datatype, format, path, view_name, save_as_render) { } @@ -67,7 +60,7 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam /* prepare the file with all the channels */ - if (!IMB_exr_begin_write(exrhandle, filename, width, height, format_->exr_codec, nullptr)) { + if (!IMB_exr_begin_write(exrhandle, filename, width, height, format_.exr_codec, nullptr)) { printf("Error Writing Singlelayer Multiview Openexr\n"); IMB_exr_close(exrhandle); } @@ -103,7 +96,7 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinit_execution() datatype_, view_name_, width, - format_->depth == R_IMF_CHAN_DEPTH_16, + format_.depth == R_IMF_CHAN_DEPTH_16, output_buffer_); /* memory can only be freed after we write all views to the file */ @@ -246,25 +239,17 @@ void OutputOpenExrMultiLayerMultiViewOperation::deinit_execution() /******************************** Stereo3D ******************************/ -OutputStereoOperation::OutputStereoOperation(const RenderData *rd, +OutputStereoOperation::OutputStereoOperation(const Scene *scene, + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, const char *name, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name, const bool save_as_render) - : OutputSingleLayerOperation(rd, - tree, - datatype, - format, - path, - view_settings, - display_settings, - view_name, - save_as_render) + : OutputSingleLayerOperation( + scene, rd, tree, datatype, format, path, view_name, save_as_render) { BLI_strncpy(name_, name, sizeof(name_)); channels_ = get_datatype_size(datatype); @@ -316,7 +301,7 @@ void OutputStereoOperation::deinit_execution() 1, channels_ * width * height, buf, - format_->depth == R_IMF_CHAN_DEPTH_16); + format_.depth == R_IMF_CHAN_DEPTH_16); image_input_ = nullptr; output_buffer_ = nullptr; @@ -331,7 +316,7 @@ void OutputStereoOperation::deinit_execution() /* get rectf from EXR */ for (i = 0; i < 2; i++) { float *rectf = IMB_exr_channel_rect(exrhandle, nullptr, name_, names[i]); - ibuf[i] = IMB_allocImBuf(width, height, format_->planes, 0); + ibuf[i] = IMB_allocImBuf(width, height, format_.planes, 0); ibuf[i]->channels = channels_; ibuf[i]->rect_float = rectf; @@ -339,24 +324,23 @@ void OutputStereoOperation::deinit_execution() ibuf[i]->dither = rd_->dither_intensity; /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ - IMB_colormanagement_imbuf_for_write( - ibuf[i], true, false, view_settings_, display_settings_, format_); + IMB_colormanagement_imbuf_for_write(ibuf[i], true, false, &format_); IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); } /* create stereo buffer */ - ibuf[2] = IMB_stereo3d_ImBuf(format_, ibuf[0], ibuf[1]); + ibuf[2] = IMB_stereo3d_ImBuf(&format_, ibuf[0], ibuf[1]); BKE_image_path_from_imformat(filename, path_, BKE_main_blendfile_path_from_global(), rd_->cfra, - format_, + &format_, (rd_->scemode & R_EXTENSION) != 0, true, nullptr); - BKE_imbuf_write(ibuf[2], filename, format_); + BKE_imbuf_write(ibuf[2], filename, &format_); /* imbuf knows which rects are not part of ibuf */ for (i = 0; i < 3; i++) { diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index dac7a208e7e..69f4011d340 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -11,20 +11,19 @@ #include "DNA_color_types.h" -#include "intern/openexr/openexr_multi.h" +#include "IMB_openexr.h" namespace blender::compositor { class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOperation { private: public: - OutputOpenExrSingleLayerMultiViewOperation(const RenderData *rd, + OutputOpenExrSingleLayerMultiViewOperation(const Scene *scene, + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name, bool save_as_render); @@ -54,14 +53,13 @@ class OutputStereoOperation : public OutputSingleLayerOperation { size_t channels_; public: - OutputStereoOperation(const RenderData *rd, + OutputStereoOperation(const Scene *scene, + const RenderData *rd, const bNodeTree *tree, DataType datatype, struct ImageFormatData *format, const char *path, const char *name, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name, bool save_as_render); void *get_handle(const char *filename); diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 56fbfc570a8..cde1496546e 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -8,6 +8,7 @@ #include "BLI_string.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_main.h" #include "BKE_scene.h" @@ -199,16 +200,14 @@ static void write_buffer_rect(rcti *rect, } } -OutputSingleLayerOperation::OutputSingleLayerOperation( - const RenderData *rd, - const bNodeTree *tree, - DataType datatype, - ImageFormatData *format, - const char *path, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, - const char *view_name, - const bool save_as_render) +OutputSingleLayerOperation::OutputSingleLayerOperation(const Scene *scene, + const RenderData *rd, + const bNodeTree *tree, + DataType datatype, + ImageFormatData *format, + const char *path, + const char *view_name, + const bool save_as_render) { rd_ = rd; tree_ = tree; @@ -219,11 +218,9 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( datatype_ = datatype; image_input_ = nullptr; - format_ = format; + BKE_image_format_init_for_write(&format_, scene, format); BLI_strncpy(path_, path, sizeof(path_)); - view_settings_ = view_settings; - display_settings_ = display_settings; view_name_ = view_name; save_as_render_ = save_as_render; } @@ -244,7 +241,7 @@ void OutputSingleLayerOperation::deinit_execution() if (this->get_width() * this->get_height() != 0) { int size = get_datatype_size(datatype_); - ImBuf *ibuf = IMB_allocImBuf(this->get_width(), this->get_height(), format_->planes, 0); + ImBuf *ibuf = IMB_allocImBuf(this->get_width(), this->get_height(), format_.planes, 0); char filename[FILE_MAX]; const char *suffix; @@ -253,8 +250,7 @@ void OutputSingleLayerOperation::deinit_execution() ibuf->mall |= IB_rectfloat; ibuf->dither = rd_->dither_intensity; - IMB_colormanagement_imbuf_for_write( - ibuf, save_as_render_, false, view_settings_, display_settings_, format_); + IMB_colormanagement_imbuf_for_write(ibuf, save_as_render_, false, &format_); suffix = BKE_scene_multiview_view_suffix_get(rd_, view_name_); @@ -262,12 +258,12 @@ void OutputSingleLayerOperation::deinit_execution() path_, BKE_main_blendfile_path_from_global(), rd_->cfra, - format_, + &format_, (rd_->scemode & R_EXTENSION) != 0, true, suffix); - if (0 == BKE_imbuf_write(ibuf, filename, format_)) { + if (0 == BKE_imbuf_write(ibuf, filename, &format_)) { printf("Cannot save Node File Output to %s\n", filename); } else { diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 12fc9ddf5f6..98b7e77cc21 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -10,7 +10,7 @@ #include "DNA_color_types.h" -#include "intern/openexr/openexr_multi.h" +#include "IMB_openexr.h" namespace blender::compositor { @@ -20,27 +20,23 @@ class OutputSingleLayerOperation : public MultiThreadedOperation { const RenderData *rd_; const bNodeTree *tree_; - ImageFormatData *format_; + ImageFormatData format_; char path_[FILE_MAX]; float *output_buffer_; DataType datatype_; SocketReader *image_input_; - const ColorManagedViewSettings *view_settings_; - const ColorManagedDisplaySettings *display_settings_; - const char *view_name_; bool save_as_render_; public: - OutputSingleLayerOperation(const RenderData *rd, + OutputSingleLayerOperation(const Scene *scene, + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name, bool save_as_render); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 98f75ad6106..38ae9e86585 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -25,10 +25,10 @@ set(INC ../windowmanager ../../../intern/atomic + ../../../intern/clog ../../../intern/glew-mx ../../../intern/guardedalloc ../../../intern/opensubdiv - ../../../intern/clog # dna_type_offsets.h ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern @@ -129,6 +129,7 @@ set(SRC engines/eevee/eevee_subsurface.c engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c + engines/eevee_next/eevee_engine.cc engines/workbench/workbench_data.c engines/workbench/workbench_effect_antialiasing.c engines/workbench/workbench_effect_cavity.c @@ -210,17 +211,19 @@ set(SRC engines/eevee/eevee_lightcache.h engines/eevee/eevee_lut.h engines/eevee/eevee_private.h + engines/eevee_next/eevee_engine.h engines/external/external_engine.h engines/image/image_batches.hh + engines/image/image_buffer_cache.hh engines/image/image_drawing_mode.hh engines/image/image_engine.h engines/image/image_instance_data.hh engines/image/image_partial_updater.hh engines/image/image_private.hh engines/image/image_shader_params.hh + engines/image/image_space.hh engines/image/image_space_image.hh engines/image/image_space_node.hh - engines/image/image_space.hh engines/image/image_texture_info.hh engines/image/image_usage.hh engines/image/image_wrappers.hh @@ -362,6 +365,7 @@ set(GLSL_SRC intern/shaders/common_colormanagement_lib.glsl intern/shaders/common_globals_lib.glsl + intern/shaders/common_gpencil_lib.glsl intern/shaders/common_pointcloud_lib.glsl intern/shaders/common_hair_lib.glsl intern/shaders/common_hair_refine_vert.glsl @@ -400,6 +404,9 @@ set(GLSL_SRC engines/gpencil/shaders/gpencil_depth_merge_vert.glsl engines/gpencil/shaders/gpencil_vfx_frag.glsl + engines/gpencil/gpencil_defines.h + engines/gpencil/gpencil_shader_shared.h + engines/select/shaders/selection_id_3D_vert.glsl engines/select/shaders/selection_id_frag.glsl diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 99d9a0d121f..00822946fe5 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -37,6 +37,7 @@ struct bContext; struct rcti; void DRW_engines_register(void); +void DRW_engines_register_experimental(void); void DRW_engines_free(void); bool DRW_engine_render_support(struct DrawEngineType *draw_engine_type); diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index 253981d321b..ee70cebcfd2 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -104,10 +104,12 @@ void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb) for (int i = 0; i < 2; i++) { if (mb->position_vbo_cache[i]) { BLI_ghash_free(mb->position_vbo_cache[i], NULL, (GHashValFreeFP)GPU_vertbuf_discard); + mb->position_vbo_cache[i] = NULL; } if (mb->hair_motion_step_cache[i]) { BLI_ghash_free( mb->hair_motion_step_cache[i], NULL, (GHashValFreeFP)EEVEE_motion_hair_step_free); + mb->hair_motion_step_cache[i] = NULL; } } } diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c index 96b0df5b7db..25939926cfa 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c +++ b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c @@ -116,7 +116,7 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id; EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + (int)shdw_data->type_data_id; - int cascade_nbr = csm_render->cascade_count; + int cascade_count = csm_render->cascade_count; float cascade_fade = csm_render->cascade_fade; float cascade_max_dist = csm_render->cascade_max_dist; float cascade_exponent = csm_render->cascade_exponent; @@ -223,19 +223,19 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, float p[4] = {1.0f, 1.0f, csm_end, 1.0f}; /* TODO: we don't need full m4 multiply here */ mul_m4_v4(vp_projmat, p); - splits_end_ndc[cascade_nbr - 1] = p[2]; + splits_end_ndc[cascade_count - 1] = p[2]; if (is_persp) { - splits_end_ndc[cascade_nbr - 1] /= p[3]; + splits_end_ndc[cascade_count - 1] /= p[3]; } } csm_data->split_start[0] = csm_start; - csm_data->split_end[cascade_nbr - 1] = csm_end; + csm_data->split_end[cascade_count - 1] = csm_end; - for (int c = 1; c < cascade_nbr; c++) { + for (int c = 1; c < cascade_count; c++) { /* View Space */ - float linear_split = interpf(csm_end, csm_start, c / (float)cascade_nbr); - float exp_split = csm_start * powf(csm_end / csm_start, c / (float)cascade_nbr); + float linear_split = interpf(csm_end, csm_start, c / (float)cascade_count); + float exp_split = csm_start * powf(csm_end / csm_start, c / (float)cascade_count); if (is_persp) { csm_data->split_start[c] = interpf(exp_split, linear_split, cascade_exponent); @@ -276,13 +276,13 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, } /* Set last cascade split fade distance into the first split_start. */ - float prev_split = (cascade_nbr > 1) ? csm_data->split_end[cascade_nbr - 2] : - csm_data->split_start[0]; + float prev_split = (cascade_count > 1) ? csm_data->split_end[cascade_count - 2] : + csm_data->split_start[0]; csm_data->split_start[0] = interpf( - prev_split, csm_data->split_end[cascade_nbr - 1], cascade_fade); + prev_split, csm_data->split_end[cascade_count - 1], cascade_fade); /* For each cascade */ - for (int c = 0; c < cascade_nbr; c++) { + for (int c = 0; c < cascade_count; c++) { float(*projmat)[4] = csm_render->projmat[c]; /* Given 8 frustum corners */ float corners[8][3] = { diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl index 584aacc9e19..ddc6a0b9661 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl @@ -26,10 +26,13 @@ struct ClosureEvalGlossy { #ifdef STEP_RESOLVE /* SSR */ /* Prototype. */ +# ifndef GPU_METAL +/* MSL does not require prototypes. */ void raytrace_resolve(ClosureInputGlossy cl_in, inout ClosureEvalGlossy cl_eval, inout ClosureEvalCommon cl_common, inout ClosureOutputGlossy cl_out); +# endif #endif ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in, diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl index f66f45635f4..fefc8743691 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl @@ -32,8 +32,10 @@ struct Closure { #endif }; +#ifndef GPU_METAL /* Prototype */ Closure nodetree_exec(void); +#endif /* clang-format off */ /* Avoid multi-line defines. */ diff --git a/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl index c6f61d1d443..bdcc0a2ba93 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl @@ -50,12 +50,6 @@ out vec4 FragColor; /* -------------- Utils ------------- */ -vec3 safe_color(vec3 c) -{ - /* Clamp to avoid black square artifacts if a pixel goes NaN. */ - return clamp(c, vec3(0.0), vec3(1e20)); /* 1e20 arbitrary. */ -} - /* 3-tap median filter */ vec3 median(vec3 a, vec3 b, vec3 c) { @@ -156,7 +150,7 @@ vec4 step_blit(void) float br = max_v3(m); /* Under-threshold part: quadratic curve */ - float rq = clamp(br - curveThreshold.x, 0, curveThreshold.y); + float rq = clamp(br - curveThreshold.x, 0.0, curveThreshold.y); rq = curveThreshold.z * rq * rq; /* Combine and apply the brightness response curve. */ diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl index 57027c71156..688ae4915e1 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl @@ -142,7 +142,7 @@ void dof_resolve_load_layer(sampler2D color_tex, out float out_weight) { vec2 pixel_co = gl_FragCoord.xy / 2.0; - vec2 uv = pixel_co / textureSize(color_tex, 0).xy; + vec2 uv = pixel_co / vec2(textureSize(color_tex, 0).xy); out_color = textureLod(color_tex, uv, 0.0); out_weight = textureLod(weight_tex, uv, 0.0).r; } diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl index b05223e755d..51a351babd3 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl @@ -95,7 +95,7 @@ void main() weights = dof_layer_weight(cocs) * dof_sample_weight(cocs); /* Filter NaNs. */ - weights = mix(weights, vec4(0.0), equal(cocs, vec4(0.0))); + weights = select(weights, vec4(0.0), equal(cocs, vec4(0.0))); color1 = colors[0] * weights[0]; color2 = colors[1] * weights[1]; diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl index 235145b221b..178ef46862b 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl @@ -54,12 +54,12 @@ void main() bvec4 focus = lessThanEqual(cocs, vec4(0.5)); if (any(defocus) && any(focus)) { /* For the same reason as in the flatten pass. This is a case we cannot optimize for. */ - cocs = mix(cocs, vec4(DOF_TILE_MIXED), focus); - cocs = mix(cocs, vec4(DOF_TILE_MIXED), defocus); + cocs = select(cocs, vec4(DOF_TILE_MIXED), focus); + cocs = select(cocs, vec4(DOF_TILE_MIXED), defocus); } else { - cocs = mix(cocs, vec4(DOF_TILE_FOCUS), focus); - cocs = mix(cocs, vec4(DOF_TILE_DEFOCUS), defocus); + cocs = select(cocs, vec4(DOF_TILE_FOCUS), focus); + cocs = select(cocs, vec4(DOF_TILE_DEFOCUS), defocus); } outCoc.y = max_v4(cocs); } diff --git a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl index ce455123987..aaf673eecd2 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl @@ -38,7 +38,7 @@ uniform vec2 texelSize; /* On some AMD card / driver combination, it is needed otherwise, * the shader does not write anything. */ -#if defined(GPU_INTEL) || defined(GPU_ATI) +#if (defined(GPU_INTEL) || defined(GPU_ATI)) && defined(GPU_OPENGL) out vec4 fragColor; #endif @@ -68,9 +68,14 @@ void main() float val = minmax4(samp.x, samp.y, samp.z, samp.w); #endif -#if defined(GPU_INTEL) || defined(GPU_ATI) +#if (defined(GPU_INTEL) || defined(GPU_ATI)) && defined(GPU_OPENGL) /* Use color format instead of 24bit depth texture */ fragColor = vec4(val); #endif + +#if !(defined(GPU_INTEL) && defined(GPU_OPENGL)) + /* If using Intel workaround, do not write out depth as there will be no depth target and this is + * invalid. */ gl_FragDepth = val; +#endif } diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl index f4ff28eaee4..c9010749060 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl @@ -60,11 +60,11 @@ void main() vec3 P = transform_point(ViewMatrixInverse, vP); vec3 vV = viewCameraVec(vP); vec3 V = cameraVec(P); - vec3 vN = normal_decode(texture(normalBuffer, uvs, 0).rg, vV); + vec3 vN = normal_decode(textureLod(normalBuffer, uvs, 0.0).rg, vV); vec3 N = transform_direction(ViewMatrixInverse, vN); /* Retrieve pixel data */ - vec4 speccol_roughness = texture(specroughBuffer, uvs, 0).rgba; + vec4 speccol_roughness = textureLod(specroughBuffer, uvs, 0.0).rgba; /* Early out */ if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) { 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 300477570d0..fc8091161d7 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 @@ -74,7 +74,7 @@ bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity) * offset. If the offset coordinate is zero then * velocity is irrelevant. */ - vec2 point = sign(offset * velocity); + vec2 point = sign(vec2(offset) * velocity); float dist = (point.x + point.y); /** diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl index 84626eac4cf..e1035af37be 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl @@ -289,7 +289,7 @@ vec3 probe_evaluate_grid(GridData gd, vec3 P, vec3 N, vec3 localpos) weight += prbIrradianceSmooth; /* Trilinear weights */ - vec3 trilinear = mix(1.0 - trilinear_weight, trilinear_weight, offset); + vec3 trilinear = mix(1.0 - trilinear_weight, trilinear_weight, vec3(offset)); weight *= trilinear.x * trilinear.y * trilinear.z; /* Avoid zero weight */ diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl index cf44a04b707..5674e464f4c 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl @@ -16,11 +16,11 @@ void main() { #if 0 /* Reconstructing Target uvs like this avoid missing pixels if NPO2 */ - vec2 uvs = gl_FragCoord.xy * 2.0 / vec2(textureSize(source, 0)); + vec2 uvs = gl_FragCoord.xy * 2.0 / vec2(textureSize(source, 0).xy); FragColor = textureLod(source, vec3(uvs, layer), 0.0); #else - vec2 texel_size = 1.0 / vec2(textureSize(source, 0)); + vec2 texel_size = 1.0 / vec2(textureSize(source, 0).xy); vec2 uvs = gl_FragCoord.xy * 2.0 * texel_size; vec4 ofs = texel_size.xyxy * vec4(0.75, 0.75, -0.75, -0.75); diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index 25661a0d731..9f1afc4767c 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -31,7 +31,7 @@ layout(location = 3) out vec4 volumePhase; void main() { - ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice); + ivec3 volume_cell = ivec3(ivec2(gl_FragCoord.xy), slice); vec3 ndc_cell = volume_to_ndc((vec3(volume_cell) + volJitter.xyz) * volInvTexSize.xyz); viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); 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 12b7d8acbea..11f57c0a82e 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -52,7 +52,7 @@ void main() ivec2 texco = ivec2(gl_FragCoord.xy); #endif for (int i = 0; i <= slice; i++) { - ivec3 volume_cell = ivec3(gl_FragCoord.xy, i); + ivec3 volume_cell = ivec3(ivec2(gl_FragCoord.xy), i); vec3 Lscat = texelFetch(volumeScattering, volume_cell, 0).rgb; vec3 s_extinction = texelFetch(volumeExtinction, volume_cell, 0).rgb; 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 dc755aeab8b..df3f92966e6 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl @@ -22,7 +22,7 @@ layout(location = 1) out vec4 outTransmittance; void main() { - ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice); + ivec3 volume_cell = ivec3(ivec2(gl_FragCoord.xy), slice); /* Emission */ outScattering = texelFetch(volumeEmission, volume_cell, 0); diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc new file mode 100644 index 00000000000..390ab1f3e50 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +#include "BKE_global.h" +#include "BLI_rect.h" + +#include "GPU_framebuffer.h" + +#include "ED_view3d.h" + +#include "DRW_render.h" + +struct EEVEE_Data { + DrawEngineType *engine_type; + DRWViewportEmptyList *fbl; + DRWViewportEmptyList *txl; + DRWViewportEmptyList *psl; + DRWViewportEmptyList *stl; + void *instance; +}; + +static void eevee_engine_init(void *vedata) +{ + UNUSED_VARS(vedata); +} + +static void eevee_draw_scene(void *vedata) +{ + UNUSED_VARS(vedata); +} + +static void eevee_cache_init(void *vedata) +{ + UNUSED_VARS(vedata); +} + +static void eevee_cache_populate(void *vedata, Object *object) +{ + UNUSED_VARS(vedata, object); +} + +static void eevee_cache_finish(void *vedata) +{ + UNUSED_VARS(vedata); +} + +static void eevee_engine_free() +{ +} + +static void eevee_instance_free(void *instance) +{ + UNUSED_VARS(instance); +} + +static void eevee_render_to_image(void *UNUSED(vedata), + struct RenderEngine *engine, + struct RenderLayer *layer, + const struct rcti *UNUSED(rect)) +{ + UNUSED_VARS(engine, layer); +} + +static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) +{ + UNUSED_VARS(engine, scene, view_layer); +} + +static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data); + +extern "C" { + +DrawEngineType draw_engine_eevee_next_type = { + nullptr, + nullptr, + N_("Eevee"), + &eevee_data_size, + &eevee_engine_init, + &eevee_engine_free, + &eevee_instance_free, + &eevee_cache_init, + &eevee_cache_populate, + &eevee_cache_finish, + &eevee_draw_scene, + nullptr, + nullptr, + &eevee_render_to_image, + nullptr, +}; + +RenderEngineType DRW_engine_viewport_eevee_next_type = { + nullptr, + nullptr, + "BLENDER_EEVEE_NEXT", + N_("Eevee Next"), + RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT, + nullptr, + &DRW_render_to_image, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + &eevee_render_update_passes, + &draw_engine_eevee_next_type, + {nullptr, nullptr, nullptr}, +}; +} diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.h b/source/blender/draw/engines/eevee_next/eevee_engine.h new file mode 100644 index 00000000000..9cee11bbba8 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_engine.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2016 Blender Foundation. */ + +/** \file + * \ingroup DNA + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern DrawEngineType draw_engine_eevee_next_type; +extern RenderEngineType DRW_engine_viewport_eevee_next_type; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index eec38f98788..be35660a4d7 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -268,9 +268,9 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, !BLI_listbase_is_empty(&gpl->mask_layers); float vert_col_opacity = (override_vertcol) ? - (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) : - pd->is_render ? gpl->vertex_paint_opacity : - pd->vertex_paint_opacity; + (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) : + (pd->is_render ? gpl->vertex_paint_opacity : + pd->vertex_paint_opacity); /* Negate thickness sign to tag that strokes are in screen space. * Convert to world units (by default, 1 meter = 2000 pixels). */ float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / GPENCIL_PIXEL_FACTOR); @@ -388,13 +388,11 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, DRW_shgroup_uniform_texture(grp, "gpSceneDepthTexture", depth_tex); DRW_shgroup_uniform_texture_ref(grp, "gpMaskTexture", mask_tex); DRW_shgroup_uniform_vec3_copy(grp, "gpNormal", tgp_ob->plane_normal); - DRW_shgroup_uniform_bool_copy(grp, "strokeOrder3d", tgp_ob->is_drawmode3d); - DRW_shgroup_uniform_float_copy(grp, "thicknessScale", tgp_ob->object_scale); - DRW_shgroup_uniform_vec2_copy(grp, "sizeViewportInv", DRW_viewport_invert_size_get()); - DRW_shgroup_uniform_vec2_copy(grp, "sizeViewport", DRW_viewport_size_get()); - DRW_shgroup_uniform_float_copy(grp, "thicknessOffset", (float)gpl->line_change); - DRW_shgroup_uniform_float_copy(grp, "thicknessWorldScale", thickness_scale); - DRW_shgroup_uniform_float_copy(grp, "vertexColorOpacity", vert_col_opacity); + DRW_shgroup_uniform_bool_copy(grp, "gpStrokeOrder3d", tgp_ob->is_drawmode3d); + DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", tgp_ob->object_scale); + DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", (float)gpl->line_change); + DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale); + DRW_shgroup_uniform_float_copy(grp, "gpVertexColorOpacity", vert_col_opacity); /* If random color type, need color by layer. */ float gpl_color[4]; @@ -403,9 +401,9 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, gpencil_layer_random_color_get(ob, gpl, gpl_color); gpl_color[3] = 1.0f; } - DRW_shgroup_uniform_vec4_copy(grp, "layerTint", gpl_color); + DRW_shgroup_uniform_vec4_copy(grp, "gpLayerTint", gpl_color); - DRW_shgroup_uniform_float_copy(grp, "layerOpacity", layer_alpha); + DRW_shgroup_uniform_float_copy(grp, "gpLayerOpacity", layer_alpha); DRW_shgroup_stencil_mask(grp, 0xFF); } diff --git a/source/blender/draw/engines/gpencil/gpencil_defines.h b/source/blender/draw/engines/gpencil/gpencil_defines.h new file mode 100644 index 00000000000..6eb7bd23e4e --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_defines.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define GPENCIL_MATERIAL_BUFFER_LEN 256 + +#define GPENCIL_LIGHT_BUFFER_LEN 128 + +/* High bits are used to pass material ID to fragment shader. */ +#define GPENCIl_MATID_SHIFT 16u diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index c3f96ecd0a2..65ddb80ad55 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -58,7 +58,8 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_ static void gpencil_uv_transform_get(const float ofs[2], const float scale[2], const float rotation, - float r_uvmat[3][2]) + float r_rot_scale[2][2], + float r_offset[2]) { /* OPTI this could use 3x2 matrices and reduce the number of operations drastically. */ float mat[4][4]; @@ -70,9 +71,9 @@ static void gpencil_uv_transform_get(const float ofs[2], rotate_m4(mat, 'Z', -rotation); translate_m4(mat, ofs[0], ofs[1], 0.0f); /* Convert to 3x2 */ - copy_v2_v2(r_uvmat[0], mat[0]); - copy_v2_v2(r_uvmat[1], mat[1]); - copy_v2_v2(r_uvmat[2], mat[3]); + copy_v2_v2(r_rot_scale[0], mat[0]); + copy_v2_v2(r_rot_scale[1], mat[1]); + copy_v2_v2(r_offset, mat[3]); } static void gpencil_shade_color(float color[3]) @@ -167,7 +168,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje int mat_len = max_ii(1, BKE_object_material_count_eval(ob)); - bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN); + bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GPENCIL_MATERIAL_BUFFER_LEN); if (reuse_matpool) { /* Share the matpool with other objects. Return offset to first material. */ @@ -188,7 +189,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje GPENCIL_MaterialPool *pool = matpool; for (int i = 0; i < mat_len; i++) { - if ((i > 0) && (pool->used_count == GP_MATERIAL_BUFFER_LEN)) { + if ((i > 0) && (pool->used_count == GPENCIL_MATERIAL_BUFFER_LEN)) { pool->next = gpencil_material_pool_add(pd); pool = pool->next; } @@ -235,8 +236,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style, lighting_mode); /* Dots or Squares rotation. */ - mat_data->alignment_rot_cos = cosf(gp_style->alignment_rotation); - mat_data->alignment_rot_sin = sinf(gp_style->alignment_rotation); + mat_data->alignment_rot[0] = cosf(gp_style->alignment_rotation); + mat_data->alignment_rot[1] = sinf(gp_style->alignment_rotation); /* Stroke Style */ if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) { @@ -266,7 +267,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje gpencil_uv_transform_get(gp_style->texture_offset, gp_style->texture_scale, gp_style->texture_angle, - mat_data->fill_uv_transform); + (float(*)[2])mat_data->fill_uv_rot_scale, + mat_data->fill_uv_offset); copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor; } @@ -278,7 +280,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje gpencil_uv_transform_get(gp_style->texture_offset, gp_style->texture_scale, gp_style->texture_angle, - mat_data->fill_uv_transform); + (float(*)[2])mat_data->fill_uv_rot_scale, + mat_data->fill_uv_offset); copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); copy_v4_v4(mat_data->fill_mix_color, gp_style->mix_rgba); mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor; @@ -303,11 +306,11 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, GPUUniformBuf **r_ubo_mat) { GPENCIL_MaterialPool *matpool = first_pool; - int pool_id = mat_id / GP_MATERIAL_BUFFER_LEN; + int pool_id = mat_id / GPENCIL_MATERIAL_BUFFER_LEN; for (int i = 0; i < pool_id; i++) { matpool = matpool->next; } - mat_id = mat_id % GP_MATERIAL_BUFFER_LEN; + mat_id = mat_id % GPENCIL_MATERIAL_BUFFER_LEN; *r_ubo_mat = matpool->ubo; if (r_tex_fill) { *r_tex_fill = matpool->tex_fill[mat_id]; @@ -379,16 +382,16 @@ void gpencil_light_pool_populate(GPENCIL_LightPool *lightpool, Object *ob) if (la->type == LA_SPOT) { copy_m4_m4(mat, ob->imat); gp_light->type = GP_LIGHT_TYPE_SPOT; - gp_light->spotsize = cosf(la->spotsize * 0.5f); - gp_light->spotblend = (1.0f - gp_light->spotsize) * la->spotblend; + gp_light->spot_size = cosf(la->spotsize * 0.5f); + gp_light->spot_blend = (1.0f - gp_light->spot_size) * la->spotblend; } else if (la->type == LA_AREA) { /* Simulate area lights using a spot light. */ normalize_m4_m4(mat, ob->obmat); invert_m4(mat); gp_light->type = GP_LIGHT_TYPE_SPOT; - gp_light->spotsize = cosf(M_PI_2); - gp_light->spotblend = (1.0f - gp_light->spotsize) * 1.0f; + gp_light->spot_size = cosf(M_PI_2); + gp_light->spot_blend = (1.0f - gp_light->spot_size) * 1.0f; } else if (la->type == LA_SUN) { normalize_v3_v3(gp_light->forward, ob->obmat[2]); diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index b7bba35e298..585a508d9ce 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -276,7 +276,7 @@ void GPENCIL_cache_init(void *ved) GPUShader *sh = GPENCIL_shader_depth_merge_get(); grp = DRW_shgroup_create(sh, psl->merge_depth_ps); DRW_shgroup_uniform_texture_ref(grp, "depthBuf", &pd->depth_tx); - DRW_shgroup_uniform_bool(grp, "strokeOrder3d", &pd->is_stroke_order_3d, 1); + DRW_shgroup_uniform_bool(grp, "gpStrokeOrder3d", &pd->is_stroke_order_3d, 1); DRW_shgroup_uniform_vec4(grp, "gpModelMatrix", pd->object_bound_mat[0], 4); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } @@ -405,8 +405,8 @@ static void gpencil_sbuffer_cache_populate(gpIterPopulateData *iter) * Remember, sbuffer stroke indices start from 0. So we add last index to avoid * masking issues. */ iter->grp = DRW_shgroup_create_sub(iter->grp); - DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", iter->ubo_mat); - DRW_shgroup_uniform_float_copy(iter->grp, "strokeIndexOffset", iter->stroke_index_last); + DRW_shgroup_uniform_block(iter->grp, "materials", iter->ubo_mat); + DRW_shgroup_uniform_float_copy(iter->grp, "gpStrokeIndexOffset", iter->stroke_index_last); const DRWContextState *ctx = DRW_context_state_get(); ToolSettings *ts = ctx->scene->toolsettings; @@ -453,12 +453,12 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl, /* Iterator dependent uniforms. */ DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp; - DRW_shgroup_uniform_block(grp, "gpLightBlock", iter->ubo_lights); - DRW_shgroup_uniform_block(grp, "gpMaterialBlock", iter->ubo_mat); + DRW_shgroup_uniform_block(grp, "lights", iter->ubo_lights); + DRW_shgroup_uniform_block(grp, "materials", iter->ubo_mat); DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill); DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke); DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs); - DRW_shgroup_uniform_float_copy(grp, "strokeIndexOffset", iter->stroke_index_offset); + DRW_shgroup_uniform_float_copy(grp, "gpStrokeIndexOffset", iter->stroke_index_offset); } static void gpencil_stroke_cache_populate(bGPDlayer *gpl, @@ -500,7 +500,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl, iter->grp = DRW_shgroup_create_sub(iter->grp); if (iter->ubo_mat != ubo_mat) { - DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", ubo_mat); + DRW_shgroup_uniform_block(iter->grp, "materials", ubo_mat); iter->ubo_mat = ubo_mat; } if (tex_fill) { diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 9ef558c7a75..332c7f67c64 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -19,6 +19,9 @@ extern "C" { #endif +#include "gpencil_defines.h" +#include "gpencil_shader_shared.h" + extern DrawEngineType draw_engine_gpencil_type; struct GPENCIL_Data; @@ -39,69 +42,17 @@ struct bGPDstroke; #define GP_MAX_MASKBITS 256 -/* UBO structure. Watch out for padding. Must match GLSL declaration. */ -typedef struct gpMaterial { - float stroke_color[4]; - float fill_color[4]; - float fill_mix_color[4]; - float fill_uv_transform[3][2], alignment_rot_cos, alignment_rot_sin; - float stroke_texture_mix; - float stroke_u_scale; - float fill_texture_mix; - int flag; -} gpMaterial; - -/* gpMaterial->flag */ -/* WATCH Keep in sync with GLSL declaration. */ -#define GP_STROKE_ALIGNMENT_STROKE 1 -#define GP_STROKE_ALIGNMENT_OBJECT 2 -#define GP_STROKE_ALIGNMENT_FIXED 3 -#define GP_STROKE_ALIGNMENT 0x3 -#define GP_STROKE_OVERLAP (1 << 2) -#define GP_STROKE_TEXTURE_USE (1 << 3) -#define GP_STROKE_TEXTURE_STENCIL (1 << 4) -#define GP_STROKE_TEXTURE_PREMUL (1 << 5) -#define GP_STROKE_DOTS (1 << 6) -#define GP_STROKE_HOLDOUT (1 << 7) -#define GP_FILL_HOLDOUT (1 << 8) -#define GP_FILL_TEXTURE_USE (1 << 10) -#define GP_FILL_TEXTURE_PREMUL (1 << 11) -#define GP_FILL_TEXTURE_CLIP (1 << 12) -#define GP_FILL_GRADIENT_USE (1 << 13) -#define GP_FILL_GRADIENT_RADIAL (1 << 14) - -#define GPENCIL_LIGHT_BUFFER_LEN 128 - -/* UBO structure. Watch out for padding. Must match GLSL declaration. */ -typedef struct gpLight { - float color[3], type; - float right[3], spotsize; - float up[3], spotblend; - float forward[4]; - float position[4]; -} gpLight; - -/* gpLight->type */ -/* WATCH Keep in sync with GLSL declaration. */ -#define GP_LIGHT_TYPE_POINT 0.0 -#define GP_LIGHT_TYPE_SPOT 1.0 -#define GP_LIGHT_TYPE_SUN 2.0 -#define GP_LIGHT_TYPE_AMBIENT 3.0 - -BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16) -BLI_STATIC_ASSERT_ALIGN(gpLight, 16) - /* *********** Draw Data *********** */ typedef struct GPENCIL_MaterialPool { /* Single linked-list. */ struct GPENCIL_MaterialPool *next; /* GPU representation of materials. */ - gpMaterial mat_data[GP_MATERIAL_BUFFER_LEN]; + gpMaterial mat_data[GPENCIL_MATERIAL_BUFFER_LEN]; /* Matching ubo. */ struct GPUUniformBuf *ubo; /* Texture per material. NULL means none. */ - struct GPUTexture *tex_fill[GP_MATERIAL_BUFFER_LEN]; - struct GPUTexture *tex_stroke[GP_MATERIAL_BUFFER_LEN]; + struct GPUTexture *tex_fill[GPENCIL_MATERIAL_BUFFER_LEN]; + struct GPUTexture *tex_stroke[GPENCIL_MATERIAL_BUFFER_LEN]; /* Number of material used in this pool. */ int used_count; } GPENCIL_MaterialPool; diff --git a/source/blender/draw/engines/gpencil/gpencil_shader.c b/source/blender/draw/engines/gpencil/gpencil_shader.c index d61bbe146d4..dd12fcc70d8 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader.c @@ -46,18 +46,6 @@ static struct { GPUShader *fx_rim_sh; GPUShader *fx_shadow_sh; GPUShader *fx_transform_sh; - /* general drawing shaders */ - GPUShader *gpencil_fill_sh; - GPUShader *gpencil_stroke_sh; - GPUShader *gpencil_point_sh; - GPUShader *gpencil_edit_point_sh; - GPUShader *gpencil_line_sh; - GPUShader *gpencil_drawing_fill_sh; - GPUShader *gpencil_fullscreen_sh; - GPUShader *gpencil_simple_fullscreen_sh; - GPUShader *gpencil_blend_fullscreen_sh; - GPUShader *gpencil_background_sh; - GPUShader *gpencil_paper_sh; } g_shaders = {{NULL}}; void GPENCIL_shader_free(void) @@ -78,17 +66,6 @@ void GPENCIL_shader_free(void) DRW_SHADER_FREE_SAFE(g_shaders.fx_rim_sh); DRW_SHADER_FREE_SAFE(g_shaders.fx_shadow_sh); DRW_SHADER_FREE_SAFE(g_shaders.fx_transform_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_fill_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_stroke_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_point_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_edit_point_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_line_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_drawing_fill_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_fullscreen_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_simple_fullscreen_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_blend_fullscreen_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_background_sh); - DRW_SHADER_FREE_SAFE(g_shaders.gpencil_paper_sh); } GPUShader *GPENCIL_shader_antialiasing(int stage) @@ -96,40 +73,9 @@ GPUShader *GPENCIL_shader_antialiasing(int stage) BLI_assert(stage < 3); if (!g_shaders.antialiasing_sh[stage]) { - char stage_define[32]; - BLI_snprintf(stage_define, sizeof(stage_define), "#define SMAA_STAGE %d\n", stage); - - g_shaders.antialiasing_sh[stage] = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){ - "#define SMAA_INCLUDE_VS 1\n", - "#define SMAA_INCLUDE_PS 0\n", - "uniform vec4 viewportMetrics;\n", - datatoc_common_smaa_lib_glsl, - datatoc_gpencil_antialiasing_vert_glsl, - NULL, - }, - .frag = - (const char *[]){ - "#define SMAA_INCLUDE_VS 0\n", - "#define SMAA_INCLUDE_PS 1\n", - "uniform vec4 viewportMetrics;\n", - datatoc_common_smaa_lib_glsl, - datatoc_gpencil_antialiasing_frag_glsl, - NULL, - }, - .defs = - (const char *[]){ - "uniform float lumaWeight;\n", - "#define SMAA_GLSL_3\n", - "#define SMAA_RT_METRICS viewportMetrics\n", - "#define SMAA_PRESET_HIGH\n", - "#define SMAA_LUMA_WEIGHT float4(lumaWeight, lumaWeight, lumaWeight, 0.0)\n", - "#define SMAA_NO_DISCARD\n", - stage_define, - NULL, - }, - }); + char stage_info_name[32]; + SNPRINTF(stage_info_name, "gpencil_antialiasing_stage_%d", stage); + g_shaders.antialiasing_sh[stage] = GPU_shader_create_from_info_name(stage_info_name); } return g_shaders.antialiasing_sh[stage]; } @@ -137,29 +83,7 @@ GPUShader *GPENCIL_shader_antialiasing(int stage) GPUShader *GPENCIL_shader_geometry_get(void) { if (!g_shaders.gpencil_sh) { - g_shaders.gpencil_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){ - datatoc_common_view_lib_glsl, - datatoc_gpencil_common_lib_glsl, - datatoc_gpencil_vert_glsl, - NULL, - }, - .frag = - (const char *[]){ - datatoc_common_colormanagement_lib_glsl, - datatoc_gpencil_common_lib_glsl, - datatoc_gpencil_frag_glsl, - NULL, - }, - .defs = - (const char *[]){ - "#define GP_MATERIAL_BUFFER_LEN " STRINGIFY(GP_MATERIAL_BUFFER_LEN) "\n", - "#define GPENCIL_LIGHT_BUFFER_LEN " STRINGIFY(GPENCIL_LIGHT_BUFFER_LEN) "\n", - "#define UNIFORM_RESOURCE_ID\n", - NULL, - }, - }); + g_shaders.gpencil_sh = GPU_shader_create_from_info_name("gpencil_geometry"); } return g_shaders.gpencil_sh; } @@ -167,19 +91,7 @@ GPUShader *GPENCIL_shader_geometry_get(void) GPUShader *GPENCIL_shader_layer_blend_get(void) { if (!g_shaders.layer_blend_sh) { - g_shaders.layer_blend_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){ - datatoc_common_fullscreen_vert_glsl, - NULL, - }, - .frag = - (const char *[]){ - datatoc_gpencil_common_lib_glsl, - datatoc_gpencil_layer_blend_frag_glsl, - NULL, - }, - }); + g_shaders.layer_blend_sh = GPU_shader_create_from_info_name("gpencil_layer_blend"); } return g_shaders.layer_blend_sh; } @@ -187,8 +99,7 @@ GPUShader *GPENCIL_shader_layer_blend_get(void) GPUShader *GPENCIL_shader_mask_invert_get(void) { if (!g_shaders.mask_invert_sh) { - g_shaders.mask_invert_sh = DRW_shader_create_fullscreen(datatoc_gpencil_mask_invert_frag_glsl, - NULL); + g_shaders.mask_invert_sh = GPU_shader_create_from_info_name("gpencil_mask_invert"); } return g_shaders.mask_invert_sh; } @@ -196,19 +107,7 @@ GPUShader *GPENCIL_shader_mask_invert_get(void) GPUShader *GPENCIL_shader_depth_merge_get(void) { if (!g_shaders.depth_merge_sh) { - g_shaders.depth_merge_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){ - datatoc_common_view_lib_glsl, - datatoc_gpencil_depth_merge_vert_glsl, - NULL, - }, - .frag = - (const char *[]){ - datatoc_gpencil_depth_merge_frag_glsl, - NULL, - }, - }); + g_shaders.depth_merge_sh = GPU_shader_create_from_info_name("gpencil_depth_merge"); } return g_shaders.depth_merge_sh; } @@ -218,8 +117,7 @@ GPUShader *GPENCIL_shader_depth_merge_get(void) GPUShader *GPENCIL_shader_fx_blur_get(void) { if (!g_shaders.fx_blur_sh) { - g_shaders.fx_blur_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, - "#define BLUR\n"); + g_shaders.fx_blur_sh = GPU_shader_create_from_info_name("gpencil_fx_blur"); } return g_shaders.fx_blur_sh; } @@ -227,8 +125,7 @@ GPUShader *GPENCIL_shader_fx_blur_get(void) GPUShader *GPENCIL_shader_fx_colorize_get(void) { if (!g_shaders.fx_colorize_sh) { - g_shaders.fx_colorize_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, - "#define COLORIZE\n"); + g_shaders.fx_colorize_sh = GPU_shader_create_from_info_name("gpencil_fx_colorize"); } return g_shaders.fx_colorize_sh; } @@ -236,8 +133,7 @@ GPUShader *GPENCIL_shader_fx_colorize_get(void) GPUShader *GPENCIL_shader_fx_composite_get(void) { if (!g_shaders.fx_composite_sh) { - g_shaders.fx_composite_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, - "#define COMPOSITE\n"); + g_shaders.fx_composite_sh = GPU_shader_create_from_info_name("gpencil_fx_composite"); } return g_shaders.fx_composite_sh; } @@ -245,24 +141,7 @@ GPUShader *GPENCIL_shader_fx_composite_get(void) GPUShader *GPENCIL_shader_fx_glow_get(void) { if (!g_shaders.fx_glow_sh) { - g_shaders.fx_glow_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){ - datatoc_common_fullscreen_vert_glsl, - NULL, - }, - .frag = - (const char *[]){ - datatoc_gpencil_common_lib_glsl, - datatoc_gpencil_vfx_frag_glsl, - NULL, - }, - .defs = - (const char *[]){ - "#define GLOW\n", - NULL, - }, - }); + g_shaders.fx_glow_sh = GPU_shader_create_from_info_name("gpencil_fx_glow"); } return g_shaders.fx_glow_sh; } @@ -270,8 +149,7 @@ GPUShader *GPENCIL_shader_fx_glow_get(void) GPUShader *GPENCIL_shader_fx_pixelize_get(void) { if (!g_shaders.fx_pixel_sh) { - g_shaders.fx_pixel_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, - "#define PIXELIZE\n"); + g_shaders.fx_pixel_sh = GPU_shader_create_from_info_name("gpencil_fx_pixelize"); } return g_shaders.fx_pixel_sh; } @@ -279,24 +157,7 @@ GPUShader *GPENCIL_shader_fx_pixelize_get(void) GPUShader *GPENCIL_shader_fx_rim_get(void) { if (!g_shaders.fx_rim_sh) { - g_shaders.fx_rim_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){ - datatoc_common_fullscreen_vert_glsl, - NULL, - }, - .frag = - (const char *[]){ - datatoc_gpencil_common_lib_glsl, - datatoc_gpencil_vfx_frag_glsl, - NULL, - }, - .defs = - (const char *[]){ - "#define RIM\n", - NULL, - }, - }); + g_shaders.fx_rim_sh = GPU_shader_create_from_info_name("gpencil_fx_rim"); } return g_shaders.fx_rim_sh; } @@ -304,8 +165,7 @@ GPUShader *GPENCIL_shader_fx_rim_get(void) GPUShader *GPENCIL_shader_fx_shadow_get(void) { if (!g_shaders.fx_shadow_sh) { - g_shaders.fx_shadow_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, - "#define SHADOW\n"); + g_shaders.fx_shadow_sh = GPU_shader_create_from_info_name("gpencil_fx_shadow"); } return g_shaders.fx_shadow_sh; } @@ -313,8 +173,7 @@ GPUShader *GPENCIL_shader_fx_shadow_get(void) GPUShader *GPENCIL_shader_fx_transform_get(void) { if (!g_shaders.fx_transform_sh) { - g_shaders.fx_transform_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, - "#define TRANSFORM\n"); + g_shaders.fx_transform_sh = GPU_shader_create_from_info_name("gpencil_fx_transform"); } return g_shaders.fx_transform_sh; } diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h new file mode 100644 index 00000000000..889652c739b --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef GPU_SHADER +# include "GPU_shader_shared_utils.h" + +typedef struct gpMaterial gpMaterial; +typedef struct gpLight gpLight; +typedef enum gpMaterialFlag gpMaterialFlag; +typedef enum gpLightType gpLightType; +#endif + +enum gpMaterialFlag { + GP_STROKE_ALIGNMENT_STROKE = 1u, + GP_STROKE_ALIGNMENT_OBJECT = 2u, + GP_STROKE_ALIGNMENT_FIXED = 3u, + GP_STROKE_ALIGNMENT = 0x3u, + GP_STROKE_OVERLAP = (1u << 2u), + GP_STROKE_TEXTURE_USE = (1u << 3u), + GP_STROKE_TEXTURE_STENCIL = (1u << 4u), + GP_STROKE_TEXTURE_PREMUL = (1u << 5u), + GP_STROKE_DOTS = (1u << 6u), + GP_STROKE_HOLDOUT = (1u << 7u), + GP_FILL_HOLDOUT = (1u << 8u), + GP_FILL_TEXTURE_USE = (1u << 10u), + GP_FILL_TEXTURE_PREMUL = (1u << 11u), + GP_FILL_TEXTURE_CLIP = (1u << 12u), + GP_FILL_GRADIENT_USE = (1u << 13u), + GP_FILL_GRADIENT_RADIAL = (1u << 14u), + GP_FILL_FLAGS = (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP | + GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL | GP_FILL_HOLDOUT), +}; + +enum gpLightType { + GP_LIGHT_TYPE_POINT = 0u, + GP_LIGHT_TYPE_SPOT = 1u, + GP_LIGHT_TYPE_SUN = 2u, + GP_LIGHT_TYPE_AMBIENT = 3u, +}; + +/* Avoid compiler funkiness with enum types not being strongly typed in C. */ +#ifndef GPU_SHADER +# define gpMaterialFlag uint +# define gpLightType uint +#endif + +struct gpMaterial { + float4 stroke_color; + float4 fill_color; + float4 fill_mix_color; + float4 fill_uv_rot_scale; +#ifndef GPU_SHADER + float2 fill_uv_offset; + float2 alignment_rot; + float stroke_texture_mix; + float stroke_u_scale; + float fill_texture_mix; + gpMaterialFlag flag; +#else + /* Some drivers are completely messing the alignment or the fetches here. + * We are forced to pack these into vec4 otherwise we only get 0.0 as value. */ + /* NOTE(@fclem): This was the case on MacOS OpenGL implementation. + * This might be fixed in newer APIs. */ + float4 packed1; + float4 packed2; +# define _fill_uv_offset packed1.xy +# define _alignment_rot packed1.zw +# define _stroke_texture_mix packed2.x +# define _stroke_u_scale packed2.y +# define _fill_texture_mix packed2.z + /** NOTE(@fclem): Needs floatBitsToUint(). */ +# define _flag packed2.w +#endif +}; +BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16) + +struct gpLight { +#ifndef GPU_SHADER + float3 color; + gpLightType type; + float3 right; + float spot_size; + float3 up; + float spot_blend; + float3 forward; + float _pad0; + float3 position; + float _pad1; +#else + /* Some drivers are completely messing the alignment or the fetches here. + * We are forced to pack these into vec4 otherwise we only get 0.0 as value. */ + /* NOTE(@fclem): This was the case on MacOS OpenGL implementation. + * This might be fixed in newer APIs. */ + float4 packed0; + float4 packed1; + float4 packed2; + float4 packed3; + float4 packed4; +# define _color packed0.xyz +# define _type packed0.w +# define _right packed1.xyz +# define _spot_size packed1.w +# define _up packed2.xyz +# define _spot_blend packed2.w +# define _forward packed3.xyz +# define _position packed4.xyz +#endif +}; +BLI_STATIC_ASSERT_ALIGN(gpLight, 16) + +#ifndef GPU_SHADER +# undef gpMaterialFlag +# undef gpLightType +#endif diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl index 7758fdceb46..bbea8747ed0 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl @@ -1,64 +1,43 @@ -uniform sampler2D edgesTex; -uniform sampler2D areaTex; -uniform sampler2D searchTex; -uniform sampler2D blendTex; -uniform sampler2D colorTex; -uniform sampler2D revealTex; -uniform bool onlyAlpha; -uniform bool doAntiAliasing; - -in vec2 uvs; -in vec2 pixcoord; -in vec4 offset[3]; - -#if SMAA_STAGE == 0 -out vec2 fragColor; -#elif SMAA_STAGE == 1 -out vec4 fragColor; -#elif SMAA_STAGE == 2 -/* Reminder: Blending func is `fragRevealage * DST + fragColor`. */ -layout(location = 0, index = 0) out vec4 outColor; -layout(location = 0, index = 1) out vec4 outReveal; -#endif +#pragma BLENDER_REQUIRE(common_smaa_lib.glsl) void main() { #if SMAA_STAGE == 0 /* Detect edges in color and revealage buffer. */ - fragColor = SMAALumaEdgeDetectionPS(uvs, offset, colorTex); - fragColor = max(fragColor, SMAALumaEdgeDetectionPS(uvs, offset, revealTex)); + out_edges = SMAALumaEdgeDetectionPS(uvs, offset, colorTex); + out_edges = max(out_edges, SMAALumaEdgeDetectionPS(uvs, offset, revealTex)); /* Discard if there is no edge. */ - if (dot(fragColor, float2(1.0, 1.0)) == 0.0) { + if (dot(out_edges, float2(1.0, 1.0)) == 0.0) { discard; } #elif SMAA_STAGE == 1 - fragColor = SMAABlendingWeightCalculationPS( + out_weights = SMAABlendingWeightCalculationPS( uvs, pixcoord, offset, edgesTex, areaTex, searchTex, vec4(0)); #elif SMAA_STAGE == 2 /* Resolve both buffers. */ if (doAntiAliasing) { - outColor = SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex); - outReveal = SMAANeighborhoodBlendingPS(uvs, offset[0], revealTex, blendTex); + out_color = SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex); + out_reveal = SMAANeighborhoodBlendingPS(uvs, offset[0], revealTex, blendTex); } else { - outColor = texture(colorTex, uvs); - outReveal = texture(revealTex, uvs); + out_color = texture(colorTex, uvs); + out_reveal = texture(revealTex, uvs); } /* Revealage, how much light passes through. */ /* Average for alpha channel. */ - outReveal.a = clamp(dot(outReveal.rgb, vec3(0.333334)), 0.0, 1.0); + out_reveal.a = clamp(dot(out_reveal.rgb, vec3(0.333334)), 0.0, 1.0); /* Color buf is already premultiplied. Just add it to the color. */ /* Add the alpha. */ - outColor.a = 1.0 - outReveal.a; + out_color.a = 1.0 - out_reveal.a; if (onlyAlpha) { /* Special case in wireframe xray mode. */ - outColor = vec4(0.0); - outReveal.rgb = outReveal.aaa; + out_color = vec4(0.0); + out_reveal.rgb = out_reveal.aaa; } #endif } diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl index 07734d19972..b76433a23e5 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl @@ -1,7 +1,5 @@ -out vec2 uvs; -out vec2 pixcoord; -out vec4 offset[3]; +#pragma BLENDER_REQUIRE(common_smaa_lib.glsl) void main() { diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index b4a26ec8103..cf38c1fc12c 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -149,6 +149,8 @@ void blend_mode_output( } } +#ifndef USE_GPU_SHADER_CREATE_INFO + IN_OUT ShaderStageInterface { vec4 finalColorMul; @@ -165,6 +167,8 @@ IN_OUT ShaderStageInterface flat float depth; }; +#endif + #ifdef GPU_FRAGMENT_SHADER # define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0)) diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl index 2f711f6a2c5..9723ea307c3 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl @@ -1,7 +1,4 @@ -uniform sampler2D depthBuf; -uniform bool strokeOrder3d; - void main() { float depth = textureLod(depthBuf, gl_FragCoord.xy / vec2(textureSize(depthBuf, 0)), 0).r; diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl index 0c5260a9ec4..e162c5bf45e 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl @@ -1,6 +1,4 @@ -uniform vec4 gpModelMatrix[4]; - void main() { mat4 model_matrix = mat4(gpModelMatrix[0], gpModelMatrix[1], gpModelMatrix[2], gpModelMatrix[3]); diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl index 87365c2844d..d0dcea34c6e 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl @@ -1,12 +1,6 @@ -uniform sampler2D gpFillTexture; -uniform sampler2D gpStrokeTexture; -uniform sampler2D gpSceneDepthTexture; -uniform sampler2D gpMaskTexture; -uniform vec3 gpNormal; - -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 revealColor; +#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl) +#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl) float length_squared(vec2 v) { @@ -21,36 +15,37 @@ vec3 gpencil_lighting(void) { vec3 light_accum = vec3(0.0); for (int i = 0; i < GPENCIL_LIGHT_BUFFER_LEN; i++) { - if (lights[i].color_type.x == -1.0) { + if (lights[i]._color.x == -1.0) { break; } - vec3 L = lights[i].position.xyz - finalPos; + vec3 L = lights[i]._position - gp_interp.pos; float vis = 1.0; + gpLightType type = floatBitsToUint(lights[i]._type); /* Spot Attenuation. */ - if (lights[i].color_type.w == GP_LIGHT_TYPE_SPOT) { - mat3 rot_scale = mat3(lights[i].right.xyz, lights[i].up.xyz, lights[i].forward.xyz); + if (type == GP_LIGHT_TYPE_SPOT) { + mat3 rot_scale = mat3(lights[i]._right, lights[i]._up, lights[i]._forward); vec3 local_L = rot_scale * L; local_L /= abs(local_L.z); float ellipse = inversesqrt(length_squared(local_L)); - vis *= smoothstep(0.0, 1.0, (ellipse - lights[i].spot_size) / lights[i].spot_blend); + vis *= smoothstep(0.0, 1.0, (ellipse - lights[i]._spot_size) / lights[i]._spot_blend); /* Also mask +Z cone. */ vis *= step(0.0, local_L.z); } /* Inverse square decay. Skip for suns. */ float L_len_sqr = length_squared(L); - if (lights[i].color_type.w < GP_LIGHT_TYPE_SUN) { + if (type < GP_LIGHT_TYPE_SUN) { vis /= L_len_sqr; } else { - L = lights[i].forward.xyz; + L = lights[i]._forward; L_len_sqr = 1.0; } /* Lambertian falloff */ - if (lights[i].color_type.w != GP_LIGHT_TYPE_AMBIENT) { + if (type != GP_LIGHT_TYPE_AMBIENT) { L /= sqrt(L_len_sqr); vis *= clamp(dot(gpNormal, L), 0.0, 1.0); } - light_accum += vis * lights[i].color_type.rgb; + light_accum += vis * lights[i]._color; } /* Clamp to avoid NaNs. */ return clamp(light_accum, 0.0, 1e10); @@ -59,21 +54,21 @@ vec3 gpencil_lighting(void) void main() { vec4 col; - if (GP_FLAG_TEST(matFlag, GP_STROKE_TEXTURE_USE)) { - bool premul = GP_FLAG_TEST(matFlag, GP_STROKE_TEXTURE_PREMUL); - col = texture_read_as_linearrgb(gpStrokeTexture, premul, finalUvs); + if (flag_test(gp_interp.mat_flag, GP_STROKE_TEXTURE_USE)) { + bool premul = flag_test(gp_interp.mat_flag, GP_STROKE_TEXTURE_PREMUL); + col = texture_read_as_linearrgb(gpStrokeTexture, premul, gp_interp.uv); } - else if (GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_USE)) { - bool use_clip = GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_CLIP); - vec2 uvs = (use_clip) ? clamp(finalUvs, 0.0, 1.0) : finalUvs; - bool premul = GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_PREMUL); + else if (flag_test(gp_interp.mat_flag, GP_FILL_TEXTURE_USE)) { + bool use_clip = flag_test(gp_interp.mat_flag, GP_FILL_TEXTURE_CLIP); + vec2 uvs = (use_clip) ? clamp(gp_interp.uv, 0.0, 1.0) : gp_interp.uv; + bool premul = flag_test(gp_interp.mat_flag, GP_FILL_TEXTURE_PREMUL); col = texture_read_as_linearrgb(gpFillTexture, premul, uvs); } - else if (GP_FLAG_TEST(matFlag, GP_FILL_GRADIENT_USE)) { - bool radial = GP_FLAG_TEST(matFlag, GP_FILL_GRADIENT_RADIAL); - float fac = clamp(radial ? length(finalUvs * 2.0 - 1.0) : finalUvs.x, 0.0, 1.0); - int matid = matFlag >> GP_MATID_SHIFT; - col = mix(MATERIAL(matid).fill_color, MATERIAL(matid).fill_mix_color, fac); + else if (flag_test(gp_interp.mat_flag, GP_FILL_GRADIENT_USE)) { + bool radial = flag_test(gp_interp.mat_flag, GP_FILL_GRADIENT_RADIAL); + float fac = clamp(radial ? length(gp_interp.uv * 2.0 - 1.0) : gp_interp.uv.x, 0.0, 1.0); + uint matid = gp_interp.mat_flag >> GPENCIl_MATID_SHIFT; + col = mix(materials[matid].fill_color, materials[matid].fill_mix_color, fac); } else /* SOLID */ { col = vec4(1.0); @@ -82,18 +77,21 @@ void main() /* Composite all other colors on top of texture color. * Everything is premult by col.a to have the stencil effect. */ - fragColor = col * finalColorMul + col.a * finalColorAdd; + fragColor = col * gp_interp.color_mul + col.a * gp_interp.color_add; fragColor.rgb *= gpencil_lighting(); - fragColor *= stroke_round_cap_mask( - strokePt1, strokePt2, strokeAspect, strokeThickness, strokeHardeness); + fragColor *= gpencil_stroke_round_cap_mask(gp_interp.sspos.xy, + gp_interp.sspos.zw, + gp_interp.aspect, + gp_interp.thickness.x, + gp_interp.hardness); /* To avoid aliasing artifacts, we reduce the opacity of small strokes. */ - fragColor *= smoothstep(0.0, 1.0, unclampedThickness); + fragColor *= smoothstep(0.0, 1.0, gp_interp.thickness.y); /* Holdout materials. */ - if (GP_FLAG_TEST(matFlag, GP_STROKE_HOLDOUT | GP_FILL_HOLDOUT)) { + if (flag_test(gp_interp.mat_flag, GP_STROKE_HOLDOUT | GP_FILL_HOLDOUT)) { revealColor = fragColor.aaaa; } else { @@ -129,8 +127,8 @@ void main() * This has a cost as the depth test cannot happen early. * We could do this in the vertex shader but then perspective interpolation of uvs and * fragment clipping gets really complicated. */ - if (depth >= 0.0) { - gl_FragDepth = depth; + if (gp_interp.depth >= 0.0) { + gl_FragDepth = gp_interp.depth; } else { gl_FragDepth = gl_FragCoord.z; diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl index 6fbc7f47dac..805aec940d8 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl @@ -1,16 +1,5 @@ -uniform sampler2D colorBuf; -uniform sampler2D revealBuf; -uniform sampler2D maskBuf; -uniform int blendMode; -uniform float blendOpacity; - -in vec4 uvcoordsvar; - -/* Reminder: This is considered SRC color in blend equations. - * Same operation on all buffers. */ -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 fragRevealage; +#pragma BLENDER_REQUIRE(gpencil_common_lib.glsl) void main() { diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl index b21b4147087..7b95ea1d8b2 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl @@ -1,9 +1,4 @@ -in vec4 uvcoordsvar; - -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 fragRevealage; - void main() { /* Blend mode does the inversion. */ diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl index 225601eb9ba..c0ff8d945f2 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl @@ -1,5 +1,141 @@ +#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl) + +void gpencil_color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, float mix_tex) +{ + /* Mix stroke with other colors. */ + vec4 mixed_col = stroke_col; + mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a * gpVertexColorOpacity); + mixed_col.rgb = mix(mixed_col.rgb, gpLayerTint.rgb, gpLayerTint.a); + mixed_col.a *= vert_strength * gpLayerOpacity; + /** + * This is what the fragment shader looks like. + * out = col * gp_interp.color_mul + col.a * gp_interp.color_add. + * gp_interp.color_mul is how much of the texture color to keep. + * gp_interp.color_add is how much of the mixed color to add. + * Note that we never add alpha. This is to keep the texture act as a stencil. + * We do however, modulate the alpha (reduce it). + */ + /* We add the mixed color. This is 100% mix (no texture visible). */ + gp_interp.color_mul = vec4(mixed_col.aaa, mixed_col.a); + gp_interp.color_add = vec4(mixed_col.rgb * mixed_col.a, 0.0); + /* Then we blend according to the texture mix factor. + * Note that we keep the alpha modulation. */ + gp_interp.color_mul.rgb *= mix_tex; + gp_interp.color_add.rgb *= 1.0 - mix_tex; +} + void main() { - gpencil_vertex(); + float vert_strength; + vec4 vert_color; + vec3 vert_N; + + gpMaterial gp_mat = materials[ma1.x + gpMaterialOffset]; + gpMaterialFlag gp_flag = floatBitsToInt(gp_mat._flag); + + gl_Position = gpencil_vertex(ma, + ma1, + ma2, + ma3, + pos, + pos1, + pos2, + pos3, + uv1, + uv2, + col1, + col2, + fcol1, + vec4(drw_view.viewport_size, drw_view.viewport_size_inverse), + gp_flag, + gp_mat._alignment_rot, + gp_interp.pos, + vert_N, + vert_color, + vert_strength, + gp_interp.uv, + gp_interp.sspos, + gp_interp.aspect, + gp_interp.thickness, + gp_interp.hardness); + + if (GPENCIL_IS_STROKE_VERTEX) { + gp_interp.uv.x *= gp_mat._stroke_u_scale; + + /* Special case: We don't use vertex color if material Holdout. */ + if (flag_test(gp_flag, GP_STROKE_HOLDOUT)) { + vert_color = vec4(0.0); + } + + gpencil_color_output( + gp_mat.stroke_color, vert_color, vert_strength, gp_mat._stroke_texture_mix); + + gp_interp.mat_flag = gp_flag & ~GP_FILL_FLAGS; + + if (gpStrokeOrder3d) { + /* Use the fragment depth (see fragment shader). */ + gp_interp.depth = -1.0; + } + else if (flag_test(gp_flag, GP_STROKE_OVERLAP)) { + /* Use the index of the point as depth. + * This means the stroke can overlap itself. */ + float point_index = float(ma1.z); + gp_interp.depth = (point_index + gpStrokeIndexOffset + 1.0) * 0.0000002; + } + else { + /* Use the index of first point of the stroke as depth. + * We render using a greater depth test this means the stroke + * cannot overlap itself. + * We offset by one so that the fill can be overlapped by its stroke. + * The offset is ok since we pad the strokes data because of adjacency infos. */ + float stroke_index = float(ma1.y); + gp_interp.depth = (stroke_index + gpStrokeIndexOffset + 1.0) * 0.0000002; + } + } + else { + vec4 fill_col = gp_mat.fill_color; + + /* Special case: We don't modulate alpha in gradient mode. */ + if (flag_test(gp_flag, GP_FILL_GRADIENT_USE)) { + fill_col.a = 1.0; + } + + /* Decode fill opacity. */ + vec4 fcol_decode = vec4(fcol1.rgb, floor(fcol1.a / 10.0)); + float fill_opacity = fcol1.a - (fcol_decode.a * 10); + fcol_decode.a /= 10000.0; + + /* Special case: We don't use vertex color if material Holdout. */ + if (flag_test(gp_flag, GP_FILL_HOLDOUT)) { + fcol_decode = vec4(0.0); + } + + /* Apply opacity. */ + fill_col.a *= fill_opacity; + /* If factor is > 1 force opacity. */ + if (fill_opacity > 1.0) { + fill_col.a += fill_opacity - 1.0; + } + + fill_col.a = clamp(fill_col.a, 0.0, 1.0); + + gpencil_color_output(fill_col, fcol_decode, 1.0, gp_mat._fill_texture_mix); + + gp_interp.mat_flag = gp_flag & GP_FILL_FLAGS; + gp_interp.mat_flag |= uint(ma1.x) << GPENCIl_MATID_SHIFT; + + gp_interp.uv = mat2(gp_mat.fill_uv_rot_scale.xy, gp_mat.fill_uv_rot_scale.zw) * uv1.xy + + gp_mat._fill_uv_offset; + + if (gpStrokeOrder3d) { + /* Use the fragment depth (see fragment shader). */ + gp_interp.depth = -1.0; + } + else { + /* Use the index of first point of the stroke as depth. */ + float stroke_index = float(ma1.y); + gp_interp.depth = (stroke_index + gpStrokeIndexOffset) * 0.0000002; + } + } } diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl index 269ed49c4d0..5bae00b070f 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl @@ -1,13 +1,5 @@ -uniform sampler2D colorBuf; -uniform sampler2D revealBuf; - -in vec4 uvcoordsvar; - -/* Reminder: This is considered SRC color in blend equations. - * Same operation on all buffers. */ -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 fragRevealage; +#pragma BLENDER_REQUIRE(gpencil_common_lib.glsl) float gaussian_weight(float x) { @@ -16,8 +8,6 @@ float gaussian_weight(float x) #if defined(COMPOSITE) -uniform bool isFirstPass; - void main() { if (isFirstPass) { @@ -35,11 +25,6 @@ void main() #elif defined(COLORIZE) -uniform vec3 lowColor; -uniform vec3 highColor; -uniform float factor; -uniform int mode; - const mat3 sepia_mat = mat3( vec3(0.393, 0.349, 0.272), vec3(0.769, 0.686, 0.534), vec3(0.189, 0.168, 0.131)); @@ -80,9 +65,6 @@ void main() #elif defined(BLUR) -uniform vec2 offset; -uniform int sampCount; - void main() { vec2 pixel_size = 1.0 / vec2(textureSize(revealBuf, 0).xy); @@ -108,14 +90,6 @@ void main() #elif defined(TRANSFORM) -uniform vec2 axisFlip = vec2(1.0); -uniform vec2 waveDir = vec2(0.0); -uniform vec2 waveOffset = vec2(0.0); -uniform float wavePhase = 0.0; -uniform vec2 swirlCenter = vec2(0.0); -uniform float swirlAngle = 0.0; -uniform float swirlRadius = 0.0; - void main() { vec2 uv = (uvcoordsvar.xy - 0.5) * axisFlip + 0.5; @@ -142,14 +116,6 @@ void main() #elif defined(GLOW) -uniform vec4 glowColor; -uniform vec2 offset; -uniform int sampCount; -uniform vec4 threshold; -uniform bool firstPass; -uniform bool glowUnder; -uniform int blendMode; - void main() { vec2 pixel_size = 1.0 / vec2(textureSize(revealBuf, 0).xy); @@ -210,14 +176,6 @@ void main() #elif defined(RIM) -uniform vec2 blurDir; -uniform vec2 uvOffset; -uniform vec3 rimColor; -uniform vec3 maskColor; -uniform int sampCount; -uniform int blendMode; -uniform bool isFirstPass; - void main() { /* Blur revealage buffer. */ @@ -260,17 +218,6 @@ void main() #elif defined(SHADOW) -uniform vec4 shadowColor; -uniform vec2 uvRotX; -uniform vec2 uvRotY; -uniform vec2 uvOffset; -uniform vec2 blurDir; -uniform vec2 waveDir; -uniform vec2 waveOffset; -uniform float wavePhase; -uniform int sampCount; -uniform bool isFirstPass; - vec2 compute_uvs(float x) { vec2 uv = uvcoordsvar.xy; @@ -327,11 +274,6 @@ void main() #elif defined(PIXELIZE) -uniform vec2 targetPixelSize; -uniform vec2 targetPixelOffset; -uniform vec2 accumOffset; -uniform int sampCount; - void main() { vec2 pixel = floor((uvcoordsvar.xy - targetPixelOffset) / targetPixelSize); diff --git a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh new file mode 100644 index 00000000000..3b4de704c00 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name GPencil Object rendering + * \{ */ + +GPU_SHADER_INTERFACE_INFO(gpencil_geometry_iface, "gp_interp") + .smooth(Type::VEC4, "color_mul") + .smooth(Type::VEC4, "color_add") + .smooth(Type::VEC3, "pos") + .smooth(Type::VEC2, "uv") + .no_perspective(Type::VEC2, "thickness") + .no_perspective(Type::FLOAT, "hardness") + .flat(Type::VEC2, "aspect") + .flat(Type::VEC4, "sspos") + .flat(Type::UINT, "mat_flag") + .flat(Type::FLOAT, "depth"); + +GPU_SHADER_CREATE_INFO(gpencil_geometry) + .do_static_compilation(true) + .typedef_source("gpencil_defines.h") + .typedef_source("gpencil_shader_shared.h") + .sampler(0, ImageType::FLOAT_2D, "gpFillTexture") + .sampler(1, ImageType::FLOAT_2D, "gpStrokeTexture") + .sampler(2, ImageType::DEPTH_2D, "gpSceneDepthTexture") + .sampler(3, ImageType::FLOAT_2D, "gpMaskTexture") + .uniform_buf(2, "gpMaterial", "materials[GPENCIL_MATERIAL_BUFFER_LEN]", Frequency::BATCH) + .uniform_buf(3, "gpLight", "lights[GPENCIL_LIGHT_BUFFER_LEN]", Frequency::BATCH) + /* Per Object */ + .push_constant(Type::VEC3, "gpNormal") + .push_constant(Type::BOOL, "gpStrokeOrder3d") + .push_constant(Type::INT, "gpMaterialOffset") + /* Per Layer */ + .push_constant(Type::FLOAT, "gpVertexColorOpacity") + .push_constant(Type::VEC4, "gpLayerTint") + .push_constant(Type::FLOAT, "gpLayerOpacity") + .push_constant(Type::FLOAT, "gpStrokeIndexOffset") + .fragment_out(0, Type::VEC4, "fragColor") + .fragment_out(1, Type::VEC4, "revealColor") + .vertex_out(gpencil_geometry_iface) + .vertex_source("gpencil_vert.glsl") + .fragment_source("gpencil_frag.glsl") + .additional_info("draw_gpencil"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Fullscreen shaders + * \{ */ + +GPU_SHADER_CREATE_INFO(gpencil_layer_blend) + .do_static_compilation(true) + .sampler(0, ImageType::FLOAT_2D, "colorBuf") + .sampler(1, ImageType::FLOAT_2D, "revealBuf") + .sampler(2, ImageType::FLOAT_2D, "maskBuf") + .push_constant(Type::INT, "blendMode") + .push_constant(Type::FLOAT, "blendOpacity") + /* Reminder: This is considered SRC color in blend equations. + * Same operation on all buffers. */ + .fragment_out(0, Type::VEC4, "fragColor") + .fragment_out(1, Type::VEC4, "fragRevealage") + .fragment_source("gpencil_layer_blend_frag.glsl") + .additional_info("draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_mask_invert) + .do_static_compilation(true) + .fragment_out(0, Type::VEC4, "fragColor") + .fragment_out(1, Type::VEC4, "fragRevealage") + .fragment_source("gpencil_mask_invert_frag.glsl") + .additional_info("draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_depth_merge) + .do_static_compilation(true) + .push_constant(Type::VEC4, "gpModelMatrix", 4) + .push_constant(Type::BOOL, "strokeOrder3d") + .sampler(0, ImageType::DEPTH_2D, "depthBuf") + .vertex_source("gpencil_depth_merge_vert.glsl") + .fragment_source("gpencil_depth_merge_frag.glsl") + .additional_info("draw_view"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Anti-Aliasing + * \{ */ + +GPU_SHADER_INTERFACE_INFO(gpencil_antialiasing_iface, "") + .smooth(Type::VEC2, "uvs") + .smooth(Type::VEC2, "pixcoord") + .smooth(Type::VEC4, "offset[3]"); + +GPU_SHADER_CREATE_INFO(gpencil_antialiasing) + .define("SMAA_GLSL_3") + .define("SMAA_RT_METRICS", "viewportMetrics") + .define("SMAA_PRESET_HIGH") + .define("SMAA_LUMA_WEIGHT", "float4(lumaWeight, lumaWeight, lumaWeight, 0.0)") + .define("SMAA_NO_DISCARD") + .vertex_out(gpencil_antialiasing_iface) + .push_constant(Type::VEC4, "viewportMetrics") + .push_constant(Type::FLOAT, "lumaWeight") + .vertex_source("gpencil_antialiasing_vert.glsl") + .fragment_source("gpencil_antialiasing_frag.glsl"); + +GPU_SHADER_CREATE_INFO(gpencil_antialiasing_stage_0) + .define("SMAA_STAGE", "0") + .sampler(0, ImageType::FLOAT_2D, "colorTex") + .sampler(1, ImageType::FLOAT_2D, "revealTex") + .fragment_out(0, Type::VEC2, "out_edges") + .additional_info("gpencil_antialiasing") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(gpencil_antialiasing_stage_1) + .define("SMAA_STAGE", "1") + .sampler(0, ImageType::FLOAT_2D, "edgesTex") + .sampler(1, ImageType::FLOAT_2D, "areaTex") + .sampler(2, ImageType::FLOAT_2D, "searchTex") + .fragment_out(0, Type::VEC4, "out_weights") + .additional_info("gpencil_antialiasing") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(gpencil_antialiasing_stage_2) + .define("SMAA_STAGE", "2") + .sampler(0, ImageType::FLOAT_2D, "colorTex") + .sampler(1, ImageType::FLOAT_2D, "revealTex") + .sampler(2, ImageType::FLOAT_2D, "blendTex") + .push_constant(Type::FLOAT, "mixFactor") + .push_constant(Type::FLOAT, "taaAccumulatedWeight") + .push_constant(Type::BOOL, "doAntiAliasing") + .push_constant(Type::BOOL, "onlyAlpha") + /* Reminder: Blending func is `fragRevealage * DST + fragColor`. */ + .fragment_out(0, Type::VEC4, "out_color", DualBlend::SRC_0) + .fragment_out(0, Type::VEC4, "out_reveal", DualBlend::SRC_1) + .additional_info("gpencil_antialiasing") + .do_static_compilation(true); + +/** \} */ diff --git a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh new file mode 100644 index 00000000000..165b47f82f8 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(gpencil_fx_common) + .sampler(0, ImageType::FLOAT_2D, "colorBuf") + .sampler(1, ImageType::FLOAT_2D, "revealBuf") + /* Reminder: This is considered SRC color in blend equations. + * Same operation on all buffers. */ + .fragment_out(0, Type::VEC4, "fragColor") + .fragment_out(1, Type::VEC4, "fragRevealage") + .fragment_source("gpencil_vfx_frag.glsl"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_composite) + .do_static_compilation(true) + .define("COMPOSITE") + .push_constant(Type::BOOL, "isFirstPass") + .additional_info("gpencil_fx_common", "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_colorize) + .do_static_compilation(true) + .define("COLORIZE") + .push_constant(Type::VEC3, "lowColor") + .push_constant(Type::VEC3, "highColor") + .push_constant(Type::FLOAT, "factor") + .push_constant(Type::INT, "mode") + .additional_info("gpencil_fx_common", "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_blur) + .do_static_compilation(true) + .define("BLUR") + .push_constant(Type::VEC2, "offset") + .push_constant(Type::INT, "sampCount") + .additional_info("gpencil_fx_common", "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_transform) + .do_static_compilation(true) + .define("TRANSFORM") + .push_constant(Type::VEC2, "axisFlip") + .push_constant(Type::VEC2, "waveDir") + .push_constant(Type::VEC2, "waveOffset") + .push_constant(Type::FLOAT, "wavePhase") + .push_constant(Type::VEC2, "swirlCenter") + .push_constant(Type::FLOAT, "swirlAngle") + .push_constant(Type::FLOAT, "swirlRadius") + .additional_info("gpencil_fx_common", "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_glow) + .do_static_compilation(true) + .define("GLOW") + .push_constant(Type::VEC4, "glowColor") + .push_constant(Type::VEC2, "offset") + .push_constant(Type::INT, "sampCount") + .push_constant(Type::VEC4, "threshold") + .push_constant(Type::BOOL, "firstPass") + .push_constant(Type::BOOL, "glowUnder") + .push_constant(Type::INT, "blendMode") + .additional_info("gpencil_fx_common", "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_rim) + .do_static_compilation(true) + .define("RIM") + .push_constant(Type::VEC2, "blurDir") + .push_constant(Type::VEC2, "uvOffset") + .push_constant(Type::VEC3, "rimColor") + .push_constant(Type::VEC3, "maskColor") + .push_constant(Type::INT, "sampCount") + .push_constant(Type::INT, "blendMode") + .push_constant(Type::BOOL, "isFirstPass") + .additional_info("gpencil_fx_common", "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_shadow) + .do_static_compilation(true) + .define("SHADOW") + .push_constant(Type::VEC4, "shadowColor") + .push_constant(Type::VEC2, "uvRotX") + .push_constant(Type::VEC2, "uvRotY") + .push_constant(Type::VEC2, "uvOffset") + .push_constant(Type::VEC2, "blurDir") + .push_constant(Type::VEC2, "waveDir") + .push_constant(Type::VEC2, "waveOffset") + .push_constant(Type::FLOAT, "wavePhase") + .push_constant(Type::INT, "sampCount") + .push_constant(Type::BOOL, "isFirstPass") + .additional_info("gpencil_fx_common", "draw_fullscreen"); + +GPU_SHADER_CREATE_INFO(gpencil_fx_pixelize) + .do_static_compilation(true) + .define("PIXELIZE") + .push_constant(Type::VEC2, "targetPixelSize") + .push_constant(Type::VEC2, "targetPixelOffset") + .push_constant(Type::VEC2, "accumOffset") + .push_constant(Type::INT, "sampCount") + .additional_info("gpencil_fx_common", "draw_fullscreen"); diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh index 46482ab6668..f43bbadfa91 100644 --- a/source/blender/draw/engines/image/image_drawing_mode.hh +++ b/source/blender/draw/engines/image/image_drawing_mode.hh @@ -154,16 +154,15 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD * bug or a feature. For now we just acquire to determine if there is a texture. */ void *lock; ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock); - if (tile_buffer == nullptr) { - continue; + if (tile_buffer != nullptr) { + instance_data.float_buffers.mark_used(tile_buffer); + + DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp); + float4 min_max_uv(tile_x, tile_y, tile_x + 1, tile_y + 1); + DRW_shgroup_uniform_vec4_copy(shsub, "min_max_uv", min_max_uv); + DRW_shgroup_call_obmat(shsub, info.batch, image_mat); } - instance_data.float_buffers.mark_used(tile_buffer); BKE_image_release_ibuf(image, tile_buffer, lock); - - DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp); - float4 min_max_uv(tile_x, tile_y, tile_x + 1, tile_y + 1); - DRW_shgroup_uniform_vec4_copy(shsub, "min_max_uv", min_max_uv); - DRW_shgroup_call_obmat(shsub, info.batch, image_mat); } } } @@ -387,11 +386,9 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD tile_user.tile = image_tile.get_tile_number(); ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock); - if (tile_buffer == nullptr) { - /* Couldn't load the image buffer of the tile. */ - continue; + if (tile_buffer != nullptr) { + do_full_update_texture_slot(instance_data, info, texture_buffer, *tile_buffer, image_tile); } - do_full_update_texture_slot(instance_data, info, texture_buffer, *tile_buffer, image_tile); BKE_image_release_ibuf(image, tile_buffer, lock); } GPU_texture_update(info.texture, GPU_DATA_FLOAT, texture_buffer.rect_float); diff --git a/source/blender/draw/engines/overlay/overlay_metaball.c b/source/blender/draw/engines/overlay/overlay_metaball.c index b747000abc4..7805b63993e 100644 --- a/source/blender/draw/engines/overlay/overlay_metaball.c +++ b/source/blender/draw/engines/overlay/overlay_metaball.c @@ -92,6 +92,11 @@ void OVERLAY_edit_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob) select_id += 0x10000; } + + /* Needed so object centers and geometry are not detected as meta-elements. */ + if (is_select) { + DRW_select_load_id(-1); + } } void OVERLAY_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob) diff --git a/source/blender/draw/engines/overlay/shaders/background_frag.glsl b/source/blender/draw/engines/overlay/shaders/background_frag.glsl index 52052d414f8..19313c0415b 100644 --- a/source/blender/draw/engines/overlay/shaders/background_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/background_frag.glsl @@ -38,8 +38,8 @@ void main() * This removes the alpha channel and put the background behind reference images * while masking the reference images by the render alpha. */ - float alpha = texture(colorBuffer, uvcoordsvar.st).a; - float depth = texture(depthBuffer, uvcoordsvar.st).r; + float alpha = texture(colorBuffer, uvcoordsvar.xy).a; + float depth = texture(depthBuffer, uvcoordsvar.xy).r; vec3 bg_col; vec3 col_high; diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl index 0e4757f8ea8..19d54a57479 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -169,7 +169,7 @@ void diag_dir(bvec4 edges1, bvec4 edges2, out vec2 line_start, out vec2 line_end void main() { - uint ref = textureLod(outlineId, uvcoordsvar.st, 0.0).r; + uint ref = textureLod(outlineId, uvcoordsvar.xy, 0.0).r; uint ref_col = ref; vec2 uvs = gl_FragCoord.xy * sizeViewportInv.xy; diff --git a/source/blender/draw/engines/overlay/shaders/volume_gridlines_vert.glsl b/source/blender/draw/engines/overlay/shaders/volume_gridlines_vert.glsl index f714646fe40..507beb8a418 100644 --- a/source/blender/draw/engines/overlay/shaders/volume_gridlines_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/volume_gridlines_vert.glsl @@ -53,7 +53,7 @@ vec4 flag_to_color(uint flag) if (bool(flag & uint(16))) { color.rgb += vec3(0.9, 0.3, 0.0); /* orange */ } - if (color.rgb == vec3(0.0)) { + if (is_zero(color.rgb)) { color.rgb += vec3(0.5, 0.0, 0.0); /* medium red */ } return color; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl index 59222b588a0..7704e7ed0b7 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl @@ -9,11 +9,11 @@ void main() float cavity = 0.0, edges = 0.0, curvature = 0.0; #ifdef USE_CAVITY - cavity_compute(uvcoordsvar.st, depthBuffer, normalBuffer, cavity, edges); + cavity_compute(uvcoordsvar.xy, depthBuffer, normalBuffer, cavity, edges); #endif #ifdef USE_CURVATURE - curvature_compute(uvcoordsvar.st, objectIdBuffer, normalBuffer, curvature); + curvature_compute(uvcoordsvar.xy, objectIdBuffer, normalBuffer, curvature); #endif float final_cavity_factor = clamp((1.0 - cavity) * (1.0 + edges) * (1.0 + curvature), 0.0, 4.0); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl index ae564435258..30daca6b7e3 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl @@ -1,7 +1,7 @@ void main() { - float depth = texture(depthBuffer, uvcoordsvar.st).r; + float depth = texture(depthBuffer, uvcoordsvar.xy).r; /* Fix issues with Intel drivers (see T80023). */ fragColor = vec4(0.0); /* Discard background pixels. */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl index a2c45d2f8e3..35bea830bac 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl @@ -9,8 +9,8 @@ void main() /* Revealage is actually stored in transparentAccum alpha channel. * This is a workaround to older hardware not having separate blend equation per render target. */ - vec4 trans_accum = texture(transparentAccum, uvcoordsvar.st); - float trans_weight = texture(transparentRevealage, uvcoordsvar.st).r; + vec4 trans_accum = texture(transparentAccum, uvcoordsvar.xy); + float trans_weight = texture(transparentRevealage, uvcoordsvar.xy).r; float trans_reveal = trans_accum.a; /* Listing 4 */ 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 076f6e80104..4ff281ccd29 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -94,7 +94,7 @@ vec4 flag_to_color(uint flag) if (bool(flag & uint(16))) { color.rgb += vec3(0.9, 0.3, 0.0); /* orange */ } - if (color.rgb == vec3(0.0)) { + if (is_zero(color.rgb)) { color.rgb += vec3(0.5, 0.0, 0.0); /* medium red */ } return color; diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index bce001659b2..ed94c485b32 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -16,39 +16,39 @@ * * All types are not copyable and Buffers are not Movable. * - * `drw::UniformArrayBuffer<T, len>` + * `draw::UniformArrayBuffer<T, len>` * Uniform buffer object containing an array of T with len elements. * Data can be accessed using the [] operator. * - * `drw::UniformBuffer<T>` + * `draw::UniformBuffer<T>` * A uniform buffer object class inheriting from T. * Data can be accessed just like a normal T object. * - * `drw::StorageArrayBuffer<T, len>` + * `draw::StorageArrayBuffer<T, len>` * Storage buffer object containing an array of T with len elements. * The item count can be changed after creation using `resize()`. * However, this requires the invalidation of the whole buffer and * discarding all data inside it. * Data can be accessed using the [] operator. * - * `drw::StorageBuffer<T>` + * `draw::StorageBuffer<T>` * A storage buffer object class inheriting from T. * Data can be accessed just like a normal T object. * - * `drw::Texture` - * A simple wrapper to #GPUTexture. A #drw::Texture can be created without allocation. + * `draw::Texture` + * A simple wrapper to #GPUTexture. A #draw::Texture can be created without allocation. * The `ensure_[1d|2d|3d|cube][_array]()` method is here to make sure the underlying texture * will meet the requirements and create (or recreate) the #GPUTexture if needed. * - * `drw::TextureFromPool` + * `draw::TextureFromPool` * A GPUTexture from the viewport texture pool. This texture can be shared with other engines * and its content is undefined when acquiring it. - * A #drw::TextureFromPool is acquired for rendering using `acquire()` and released once the + * A #draw::TextureFromPool is acquired for rendering using `acquire()` and released once the * rendering is done using `release()`. The same texture can be acquired & released multiple * time in one draw loop. * The `sync()` method *MUST* be called once during the cache populate (aka: Sync) phase. * - * `drw::Framebuffer` + * `draw::Framebuffer` * Simple wrapper to #GPUFramebuffer that can be moved. * */ @@ -63,9 +63,9 @@ #include "BLI_utility_mixins.hh" #include "GPU_framebuffer.h" +#include "GPU_storage_buffer.h" #include "GPU_texture.h" #include "GPU_uniform_buffer.h" -#include "GPU_vertex_buffer.h" namespace blender::draw { @@ -165,7 +165,7 @@ class UniformCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable #ifdef DEBUG const char *name_ = typeid(T).name(); #else - constexpr static const char *name_ = "UniformBuffer"; + const char *name_ = "UniformBuffer"; #endif public: @@ -200,41 +200,49 @@ class UniformCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable template<typename T, int64_t len, bool device_only> class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable { protected: - /* Use vertex buffer for now. Until there is a complete GPUStorageBuf implementation. */ - GPUVertBuf *ssbo_; + GPUStorageBuf *ssbo_; #ifdef DEBUG const char *name_ = typeid(T).name(); #else - constexpr static const char *name_ = "StorageBuffer"; + const char *name_ = "StorageBuffer"; #endif public: - StorageCommon() + StorageCommon(const char *name = nullptr) { + if (name) { + name_ = name; + } init(len); } ~StorageCommon() { - GPU_vertbuf_discard(ssbo_); + GPU_storagebuf_free(ssbo_); } void resize(int64_t new_size) { BLI_assert(new_size > 0); if (new_size != this->len_) { - GPU_vertbuf_discard(ssbo_); + GPU_storagebuf_free(ssbo_); this->init(new_size); } } - operator GPUVertBuf *() const + void push_update(void) + { + BLI_assert(device_only == false); + GPU_storagebuf_update(ssbo_, this->data_); + } + + operator GPUStorageBuf *() const { return ssbo_; } /* To be able to use it with DRW_shgroup_*_ref(). */ - GPUVertBuf **operator&() + GPUStorageBuf **operator&() { return &ssbo_; } @@ -243,17 +251,8 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable void init(int64_t new_size) { this->len_ = new_size; - - GPUVertFormat format = {0}; - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; - ssbo_ = GPU_vertbuf_create_with_format_ex(&format, usage); - GPU_vertbuf_data_alloc(ssbo_, divide_ceil_u(sizeof(T) * this->len_, 4)); - if (!device_only) { - this->data_ = (T *)GPU_vertbuf_get_data(ssbo_); - GPU_vertbuf_use(ssbo_); - } + ssbo_ = GPU_storagebuf_create_ex(sizeof(T) * this->len_, nullptr, usage, this->name_); } }; @@ -322,13 +321,14 @@ template< bool device_only = false> class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> { public: - void push_update(void) + StorageArrayBuffer(const char *name = nullptr) : detail::StorageCommon<T, len, device_only>(name) { - BLI_assert(!device_only); - /* Get the data again to tag for update. The actual pointer should not - * change. */ - this->data_ = (T *)GPU_vertbuf_get_data(this->ssbo_); - GPU_vertbuf_use(this->ssbo_); + /* TODO(@fclem): We should map memory instead. */ + this->data_ = (T *)MEM_mallocN_aligned(len * sizeof(T), 16, this->name_); + } + ~StorageArrayBuffer() + { + MEM_freeN(this->data_); } }; @@ -339,14 +339,10 @@ template< bool device_only = false> class StorageBuffer : public T, public detail::StorageCommon<T, 1, device_only> { public: - void push_update(void) + StorageBuffer(const char *name = nullptr) : detail::StorageCommon<T, 1, device_only>(name) { - BLI_assert(!device_only); - /* TODO(fclem): Avoid a full copy. */ - T &vert_data = *(T *)GPU_vertbuf_get_data(this->ssbo_); - vert_data = *this; - - GPU_vertbuf_use(this->ssbo_); + /* TODO(@fclem): How could we map this? */ + this->data_ = static_cast<T *>(this); } StorageBuffer<T> &operator=(const T &other) @@ -365,6 +361,9 @@ class StorageBuffer : public T, public detail::StorageCommon<T, 1, device_only> class Texture : NonCopyable { protected: GPUTexture *tx_ = nullptr; + GPUTexture *stencil_view_ = nullptr; + Vector<GPUTexture *, 0> mip_views_; + Vector<GPUTexture *, 0> layer_views_; const char *name_; public: @@ -515,6 +514,68 @@ class Texture : NonCopyable { } /** + * Ensure the availability of mipmap views. + * MIP view covers all layers of array textures. + */ + bool ensure_mip_views(bool cube_as_array = false) + { + int mip_len = GPU_texture_mip_count(tx_); + if (mip_views_.size() != mip_len) { + for (GPUTexture *&view : mip_views_) { + GPU_TEXTURE_FREE_SAFE(view); + } + eGPUTextureFormat format = GPU_texture_format(tx_); + for (auto i : IndexRange(mip_len)) { + mip_views_.append( + GPU_texture_create_view(name_, tx_, format, i, 1, 0, 9999, cube_as_array)); + } + return true; + } + return false; + } + + GPUTexture *mip_view(int miplvl) + { + return mip_views_[miplvl]; + } + + /** + * Ensure the availability of mipmap views. + * Layer views covers all layers of array textures. + */ + bool ensure_layer_views(bool cube_as_array = false) + { + int layer_len = GPU_texture_layer_count(tx_); + if (layer_views_.size() != layer_len) { + for (GPUTexture *&view : layer_views_) { + GPU_TEXTURE_FREE_SAFE(view); + } + eGPUTextureFormat format = GPU_texture_format(tx_); + for (auto i : IndexRange(layer_len)) { + layer_views_.append( + GPU_texture_create_view(name_, tx_, format, 0, 9999, i, 1, cube_as_array)); + } + return true; + } + return false; + } + + GPUTexture *layer_view(int layer) + { + return layer_views_[layer]; + } + + GPUTexture *stencil_view(bool cube_as_array = false) + { + if (stencil_view_ == nullptr) { + eGPUTextureFormat format = GPU_texture_format(tx_); + stencil_view_ = GPU_texture_create_view(name_, tx_, format, 0, 9999, 0, 9999, cube_as_array); + GPU_texture_stencil_texture_mode_set(stencil_view_, true); + } + return stencil_view_; + } + + /** * Returns true if the texture has been allocated or acquired from the pool. */ bool is_valid(void) const @@ -603,11 +664,19 @@ class Texture : NonCopyable { } /** - * Free the internal texture but not the #drw::Texture itself. + * Free the internal texture but not the #draw::Texture itself. */ void free() { GPU_TEXTURE_FREE_SAFE(tx_); + for (GPUTexture *&view : mip_views_) { + GPU_TEXTURE_FREE_SAFE(view); + } + for (GPUTexture *&view : layer_views_) { + GPU_TEXTURE_FREE_SAFE(view); + } + GPU_TEXTURE_FREE_SAFE(stencil_view_); + mip_views_.clear(); } /** @@ -692,23 +761,35 @@ class TextureFromPool : public Texture, NonMovable { public: TextureFromPool(const char *name = "gpu::Texture") : Texture(name){}; - /* Always use `release()` after rendering. */ + /* Always use `release()` after rendering and `sync()` in sync phase. */ void acquire(int2 extent, eGPUTextureFormat format, void *owner_) { - if (this->tx_ == nullptr) { - if (tx_tmp_saved_ != nullptr) { + BLI_assert(this->tx_ == nullptr); + if (this->tx_ != nullptr) { + return; + } + if (tx_tmp_saved_ != nullptr) { + if (GPU_texture_width(tx_tmp_saved_) != extent.x || + GPU_texture_height(tx_tmp_saved_) != extent.y || + GPU_texture_format(tx_tmp_saved_) != format) { + this->tx_tmp_saved_ = nullptr; + } + else { this->tx_ = tx_tmp_saved_; return; } - DrawEngineType *owner = (DrawEngineType *)owner_; - this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner); } + DrawEngineType *owner = (DrawEngineType *)owner_; + this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner); } void release(void) { - tx_tmp_saved_ = this->tx_; - this->tx_ = nullptr; + /* Allows multiple release. */ + if (this->tx_ != nullptr) { + tx_tmp_saved_ = this->tx_; + this->tx_ = nullptr; + } } /** @@ -730,6 +811,9 @@ class TextureFromPool : public Texture, NonMovable { bool ensure_cube_array(int, int, int, eGPUTextureFormat, float *) = delete; void filter_mode(bool) = delete; void free() = delete; + GPUTexture *mip_view(int) = delete; + GPUTexture *layer_view(int) = delete; + GPUTexture *stencil_view() = delete; }; /** \} */ diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 37c1365a5f2..8dbf5483d47 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -30,6 +30,7 @@ #include "GPU_framebuffer.h" #include "GPU_primitive.h" #include "GPU_shader.h" +#include "GPU_storage_buffer.h" #include "GPU_texture.h" #include "GPU_uniform_buffer.h" @@ -466,6 +467,10 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, * \warning this keeps the ref to groups_ref until it actually dispatch. */ void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]); +/** + * \note No need for a barrier. \a indirect_buf is internally synchronized. + */ +void DRW_shgroup_call_compute_indirect(DRWShadingGroup *shgroup, GPUStorageBuf *indirect_buf); void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); @@ -568,6 +573,12 @@ void DRW_shgroup_uniform_block_ex(DRWShadingGroup *shgroup, void DRW_shgroup_uniform_block_ref_ex(DRWShadingGroup *shgroup, const char *name, struct GPUUniformBuf **ubo DRW_DEBUG_FILE_LINE_ARGS); +void DRW_shgroup_storage_block_ex(DRWShadingGroup *shgroup, + const char *name, + const struct GPUStorageBuf *ssbo DRW_DEBUG_FILE_LINE_ARGS); +void DRW_shgroup_storage_block_ref_ex(DRWShadingGroup *shgroup, + const char *name, + struct GPUStorageBuf **ssbo DRW_DEBUG_FILE_LINE_ARGS); void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, @@ -643,6 +654,10 @@ void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, DRW_shgroup_uniform_block_ex(shgroup, name, ubo, __FILE__, __LINE__) # define DRW_shgroup_uniform_block_ref(shgroup, name, ubo) \ DRW_shgroup_uniform_block_ref_ex(shgroup, name, ubo, __FILE__, __LINE__) +# define DRW_shgroup_storage_block(shgroup, name, ubo) \ + DRW_shgroup_storage_block_ex(shgroup, name, ubo, __FILE__, __LINE__) +# define DRW_shgroup_storage_block_ref(shgroup, name, ubo) \ + DRW_shgroup_storage_block_ref_ex(shgroup, name, ubo, __FILE__, __LINE__) #else # define DRW_shgroup_vertex_buffer(shgroup, name, vert) \ DRW_shgroup_vertex_buffer_ex(shgroup, name, vert) @@ -652,6 +667,10 @@ void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, DRW_shgroup_uniform_block_ex(shgroup, name, ubo) # define DRW_shgroup_uniform_block_ref(shgroup, name, ubo) \ DRW_shgroup_uniform_block_ref_ex(shgroup, name, ubo) +# define DRW_shgroup_storage_block(shgroup, name, ubo) \ + DRW_shgroup_storage_block_ex(shgroup, name, ubo) +# define DRW_shgroup_storage_block_ref(shgroup, name, ubo) \ + DRW_shgroup_storage_block_ref_ex(shgroup, name, ubo) #endif bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index aea71d965d1..a21a6409ca5 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -138,20 +138,20 @@ static void curves_batch_cache_fill_segments_proc_pos(Curves *curves, Span<float3> positions = geometry.positions(); for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range = geometry.range_for_curve(i); + const IndexRange curve_range = geometry.points_for_curve(i); - Span<float3> spline_positions = positions.slice(curve_range); + Span<float3> curve_positions = positions.slice(curve_range); float total_len = 0.0f; float *seg_data_first; - for (const int i_spline : spline_positions.index_range()) { + for (const int i_curve : curve_positions.index_range()) { float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step); - copy_v3_v3(seg_data, spline_positions[i_spline]); - if (i_spline == 0) { + copy_v3_v3(seg_data, curve_positions[i_curve]); + if (i_curve == 0) { seg_data_first = seg_data; } else { - total_len += blender::math::distance(spline_positions[i_spline - 1], - spline_positions[i_spline]); + total_len += blender::math::distance(curve_positions[i_curve - 1], + curve_positions[i_curve]); } seg_data[3] = total_len; } @@ -159,7 +159,7 @@ static void curves_batch_cache_fill_segments_proc_pos(Curves *curves, *(float *)GPU_vertbuf_raw_step(length_step) = total_len; if (total_len > 0.0f) { /* Divide by total length to have a [0-1] number. */ - for ([[maybe_unused]] const int i_spline : spline_positions.index_range()) { + for ([[maybe_unused]] const int i_curve : curve_positions.index_range()) { seg_data_first[3] /= total_len; seg_data_first += 4; } @@ -218,8 +218,8 @@ static void curves_batch_cache_fill_strands_data(Curves *curves, const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( curves->geometry); - for (const int i : IndexRange(geometry.curves_size())) { - const IndexRange curve_range = geometry.range_for_curve(i); + for (const int i : IndexRange(geometry.curves_num())) { + const IndexRange curve_range = geometry.points_for_curve(i); *(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start(); *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1; diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 08c33555b71..a4465b9aed4 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -28,6 +28,8 @@ #include "draw_cache.h" #include "draw_cache_impl.h" +#include "../engines/gpencil/gpencil_defines.h" + #define BEZIER_HANDLE (1 << 3) #define COLOR_SHIFT 5 @@ -321,7 +323,7 @@ static void gpencil_buffer_add_point(gpStrokeVert *verts, vert->point_id = v; vert->thickness = max_ff(0.0f, gps->thickness * pt->pressure) * (round_cap1 ? 1.0f : -1.0f); /* Tag endpoint material to -1 so they get discarded by vertex shader. */ - vert->mat = (is_endpoint) ? -1 : (gps->mat_nr % GP_MATERIAL_BUFFER_LEN); + vert->mat = (is_endpoint) ? -1 : (gps->mat_nr % GPENCIL_MATERIAL_BUFFER_LEN); float aspect_ratio = gps->aspect_ratio[0] / max_ff(gps->aspect_ratio[1], 1e-8); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 8682a15feba..8aa7ff66d65 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1244,15 +1244,17 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION); GPU_shader_bind(shader); - GPU_vertbuf_bind_as_ssbo(src_buffer, 0); - GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_handles, 1); - GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_quadtree, 2); - GPU_vertbuf_bind_as_ssbo(cache->patch_coords, 3); - GPU_vertbuf_bind_as_ssbo(cache->verts_orig_index, 4); - GPU_vertbuf_bind_as_ssbo(patch_arrays_buffer, 5); - GPU_vertbuf_bind_as_ssbo(patch_index_buffer, 6); - GPU_vertbuf_bind_as_ssbo(patch_param_buffer, 7); - GPU_vertbuf_bind_as_ssbo(pos_nor, 8); + int binding_point = 0; + GPU_vertbuf_bind_as_ssbo(src_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_handles, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_quadtree, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->patch_coords, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->verts_orig_index, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_arrays_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1308,15 +1310,17 @@ void draw_subdiv_extract_uvs(const DRWSubdivCache *cache, GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION_FVAR); GPU_shader_bind(shader); - GPU_vertbuf_bind_as_ssbo(src_buffer, 0); - GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_handles, 1); - GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_quadtree, 2); - GPU_vertbuf_bind_as_ssbo(cache->patch_coords, 3); - GPU_vertbuf_bind_as_ssbo(cache->verts_orig_index, 4); - GPU_vertbuf_bind_as_ssbo(patch_arrays_buffer, 5); - GPU_vertbuf_bind_as_ssbo(patch_index_buffer, 6); - GPU_vertbuf_bind_as_ssbo(patch_param_buffer, 7); - GPU_vertbuf_bind_as_ssbo(uvs, 8); + int binding_point = 0; + GPU_vertbuf_bind_as_ssbo(src_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_handles, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_quadtree, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->patch_coords, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->verts_orig_index, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_arrays_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(uvs, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); /* The buffer offset has the stride baked in (which is 2 as we have UVs) so remove the stride by * dividing by 2 */ @@ -1380,13 +1384,15 @@ void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, GPU_shader_bind(shader); + int binding_point = 0; /* subdiv_polygon_offset is always at binding point 0 for each shader using it. */ - GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, 0); - GPU_vertbuf_bind_as_ssbo(src_data, 1); - GPU_vertbuf_bind_as_ssbo(cache->face_ptex_offset_buffer, 2); - GPU_vertbuf_bind_as_ssbo(cache->patch_coords, 3); - GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, 4); - GPU_vertbuf_bind_as_ssbo(dst_data, 5); + GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(src_data, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->face_ptex_offset_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->patch_coords, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, binding_point++); + GPU_vertbuf_bind_as_ssbo(dst_data, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, dst_offset, cache->num_subdiv_quads); @@ -1406,12 +1412,15 @@ void draw_subdiv_build_sculpt_data_buffer(const DRWSubdivCache *cache, GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_SCULPT_DATA, nullptr); GPU_shader_bind(shader); + /* Mask VBO is always at binding point 0. */ if (mask_vbo) { GPU_vertbuf_bind_as_ssbo(mask_vbo, 0); } - GPU_vertbuf_bind_as_ssbo(face_set_vbo, 1); - GPU_vertbuf_bind_as_ssbo(sculpt_data, 2); + int binding_point = 1; + GPU_vertbuf_bind_as_ssbo(face_set_vbo, binding_point++); + GPU_vertbuf_bind_as_ssbo(sculpt_data, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads, mask_vbo != nullptr); @@ -1439,6 +1448,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache, GPU_vertbuf_bind_as_ssbo(face_adjacency_lists, binding_point++); GPU_vertbuf_bind_as_ssbo(vertex_loop_map, binding_point++); GPU_vertbuf_bind_as_ssbo(vertex_normals, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_verts); @@ -1463,6 +1473,7 @@ void draw_subdiv_finalize_normals(const DRWSubdivCache *cache, GPU_vertbuf_bind_as_ssbo(vertex_normals, binding_point++); GPU_vertbuf_bind_as_ssbo(subdiv_loop_subdiv_vert_index, binding_point++); GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1482,9 +1493,12 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache, GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_NORMALS_FINALIZE, "#define CUSTOM_NORMALS"); GPU_shader_bind(shader); - GPU_vertbuf_bind_as_ssbo(src_custom_normals, 0); + int binding_point = 0; + GPU_vertbuf_bind_as_ssbo(src_custom_normals, binding_point++); /* outputPosNor is bound at index 2 in the base shader. */ - GPU_vertbuf_bind_as_ssbo(pos_nor, 2); + binding_point = 2; + GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1519,15 +1533,22 @@ void draw_subdiv_build_tris_buffer(const DRWSubdivCache *cache, do_single_material ? SHADER_BUFFER_TRIS : SHADER_BUFFER_TRIS_MULTIPLE_MATERIALS, defines); GPU_shader_bind(shader); - /* Outputs */ - GPU_indexbuf_bind_as_ssbo(subdiv_tris, 1); - if (!do_single_material) { - GPU_vertbuf_bind_as_ssbo(cache->polygon_mat_offset, 2); /* subdiv_polygon_offset is always at binding point 0 for each shader using it. */ GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, 0); } + int binding_point = 1; + + /* Outputs */ + GPU_indexbuf_bind_as_ssbo(subdiv_tris, binding_point++); + + if (!do_single_material) { + GPU_vertbuf_bind_as_ssbo(cache->polygon_mat_offset, binding_point++); + } + + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); + drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); /* This generates an index buffer, so we need to put a barrier on the element array. */ @@ -1574,18 +1595,20 @@ void draw_subdiv_build_fdots_buffers(const DRWSubdivCache *cache, GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION_FACE_DOTS); GPU_shader_bind(shader); - GPU_vertbuf_bind_as_ssbo(src_buffer, 0); - GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_handles, 1); - GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_quadtree, 2); - GPU_vertbuf_bind_as_ssbo(cache->fdots_patch_coords, 3); - GPU_vertbuf_bind_as_ssbo(cache->verts_orig_index, 4); - GPU_vertbuf_bind_as_ssbo(patch_arrays_buffer, 5); - GPU_vertbuf_bind_as_ssbo(patch_index_buffer, 6); - GPU_vertbuf_bind_as_ssbo(patch_param_buffer, 7); - GPU_vertbuf_bind_as_ssbo(fdots_pos, 8); - GPU_vertbuf_bind_as_ssbo(fdots_nor, 9); - GPU_indexbuf_bind_as_ssbo(fdots_indices, 10); - GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, 11); + int binding_point = 0; + GPU_vertbuf_bind_as_ssbo(src_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_handles, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->gpu_patch_map.patch_map_quadtree, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->fdots_patch_coords, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->verts_orig_index, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_arrays_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(fdots_pos, binding_point++); + GPU_vertbuf_bind_as_ssbo(fdots_nor, binding_point++); + GPU_indexbuf_bind_as_ssbo(fdots_indices, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_coarse_poly); @@ -1607,8 +1630,10 @@ void draw_subdiv_build_lines_buffer(const DRWSubdivCache *cache, GPUIndexBuf *li GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_LINES, nullptr); GPU_shader_bind(shader); - GPU_vertbuf_bind_as_ssbo(cache->edges_orig_index, 0); - GPU_indexbuf_bind_as_ssbo(lines_indices, 1); + int binding_point = 0; + GPU_vertbuf_bind_as_ssbo(cache->edges_orig_index, binding_point++); + GPU_indexbuf_bind_as_ssbo(lines_indices, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1648,9 +1673,11 @@ void draw_subdiv_build_edge_fac_buffer(const DRWSubdivCache *cache, GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_EDGE_FAC, defines); GPU_shader_bind(shader); - GPU_vertbuf_bind_as_ssbo(pos_nor, 0); - GPU_vertbuf_bind_as_ssbo(edge_idx, 1); - GPU_vertbuf_bind_as_ssbo(edge_fac, 2); + int binding_point = 0; + GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + GPU_vertbuf_bind_as_ssbo(edge_idx, binding_point++); + GPU_vertbuf_bind_as_ssbo(edge_fac, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1673,14 +1700,16 @@ void draw_subdiv_build_lnor_buffer(const DRWSubdivCache *cache, GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_LNOR, "#define SUBDIV_POLYGON_OFFSET\n"); GPU_shader_bind(shader); + int binding_point = 0; /* Inputs */ - GPU_vertbuf_bind_as_ssbo(pos_nor, 1); - GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, 2); /* subdiv_polygon_offset is always at binding point 0 for each shader using it. */ - GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, 0); + GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, binding_point++); /* Outputs */ - GPU_vertbuf_bind_as_ssbo(lnor, 3); + GPU_vertbuf_bind_as_ssbo(lnor, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1699,13 +1728,15 @@ void draw_subdiv_build_edituv_stretch_area_buffer(const DRWSubdivCache *cache, "#define SUBDIV_POLYGON_OFFSET\n"); GPU_shader_bind(shader); + int binding_point = 0; /* Inputs */ - GPU_vertbuf_bind_as_ssbo(coarse_data, 1); /* subdiv_polygon_offset is always at binding point 0 for each shader using it. */ - GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, 0); + GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(coarse_data, binding_point++); /* Outputs */ - GPU_vertbuf_bind_as_ssbo(subdiv_data, 2); + GPU_vertbuf_bind_as_ssbo(subdiv_data, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1725,12 +1756,14 @@ void draw_subdiv_build_edituv_stretch_angle_buffer(const DRWSubdivCache *cache, GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_UV_STRETCH_ANGLE, nullptr); GPU_shader_bind(shader); + int binding_point = 0; /* Inputs */ - GPU_vertbuf_bind_as_ssbo(pos_nor, 0); - GPU_vertbuf_bind_as_ssbo(uvs, 1); + GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + GPU_vertbuf_bind_as_ssbo(uvs, binding_point++); /* Outputs */ - GPU_vertbuf_bind_as_ssbo(stretch_angles, 2); + GPU_vertbuf_bind_as_ssbo(stretch_angles, binding_point++); + BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, uvs_offset, 0, cache->num_subdiv_quads); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 2886fe53879..39ae01697a1 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -85,6 +85,7 @@ #include "engines/basic/basic_engine.h" #include "engines/eevee/eevee_engine.h" +#include "engines/eevee_next/eevee_engine.h" #include "engines/external/external_engine.h" #include "engines/gpencil/gpencil_engine.h" #include "engines/image/image_engine.h" @@ -615,7 +616,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s } if (G_draw.view_ubo == NULL) { - G_draw.view_ubo = GPU_uniformbuf_create_ex(sizeof(DRWViewUboStorage), NULL, "G_draw.view_ubo"); + G_draw.view_ubo = GPU_uniformbuf_create_ex(sizeof(ViewInfos), NULL, "G_draw.view_ubo"); } if (dst->draw_list == NULL) { @@ -2898,6 +2899,13 @@ void DRW_engine_register(DrawEngineType *draw_engine_type) g_registered_engines.len = BLI_listbase_count(&g_registered_engines.engines); } +void DRW_engines_register_experimental(void) +{ + if (U.experimental.enable_eevee_next) { + RE_engines_register(&DRW_engine_viewport_eevee_next_type); + } +} + void DRW_engines_register(void) { RE_engines_register(&DRW_engine_viewport_eevee_type); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 06643439bbc..832897b7040 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -27,6 +27,7 @@ #include "GPU_viewport.h" #include "draw_instance_data.h" +#include "draw_shader_shared.h" #ifdef __cplusplus extern "C" { @@ -191,6 +192,7 @@ typedef enum { /* Compute Commands. */ DRW_CMD_COMPUTE = 8, DRW_CMD_COMPUTE_REF = 9, + DRW_CMD_COMPUTE_INDIRECT = 10, /* Other Commands */ DRW_CMD_BARRIER = 11, @@ -240,6 +242,10 @@ typedef struct DRWCommandComputeRef { int *groups_ref; } DRWCommandComputeRef; +typedef struct DRWCommandComputeIndirect { + GPUStorageBuf *indirect_buf; +} DRWCommandComputeIndirect; + typedef struct DRWCommandBarrier { eGPUBarrier type; } DRWCommandBarrier; @@ -282,6 +288,7 @@ typedef union DRWCommand { DRWCommandDrawProcedural procedural; DRWCommandCompute compute; DRWCommandComputeRef compute_ref; + DRWCommandComputeIndirect compute_indirect; DRWCommandBarrier barrier; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; @@ -309,6 +316,8 @@ typedef enum { DRW_UNIFORM_IMAGE_REF, DRW_UNIFORM_BLOCK, DRW_UNIFORM_BLOCK_REF, + DRW_UNIFORM_STORAGE_BLOCK, + DRW_UNIFORM_STORAGE_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, @@ -343,6 +352,11 @@ struct DRWUniform { GPUUniformBuf *block; GPUUniformBuf **block_ref; }; + /* DRW_UNIFORM_STORAGE_BLOCK */ + union { + GPUStorageBuf *ssbo; + GPUStorageBuf **ssbo_ref; + }; /* DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE */ union { GPUVertBuf *vertbuf; @@ -411,32 +425,14 @@ struct DRWPass { char name[MAX_PASS_NAME]; }; -/* keep in sync with viewBlock */ -typedef struct DRWViewUboStorage { - /* View matrices */ - float persmat[4][4]; - float persinv[4][4]; - float viewmat[4][4]; - float viewinv[4][4]; - float winmat[4][4]; - float wininv[4][4]; - - float clipplanes[6][4]; - float viewvecs[2][4]; - /* Should not be here. Not view dependent (only main view). */ - float viewcamtexcofac[4]; -} DRWViewUboStorage; - -BLI_STATIC_ASSERT_ALIGN(DRWViewUboStorage, 16) - #define MAX_CULLED_VIEWS 32 struct DRWView { /** Parent view if this is a sub view. NULL otherwise. */ struct DRWView *parent; - DRWViewUboStorage storage; - /** Number of active clipplanes. */ + ViewInfos storage; + /** Number of active clip planes. */ int clip_planes_len; /** Does culling result needs to be updated. */ bool is_dirty; @@ -620,7 +616,7 @@ typedef struct DRWManager { uint primary_view_ct; /** TODO(@fclem): Remove this. Only here to support * shaders without common_view_lib.glsl */ - DRWViewUboStorage view_storage_cpy; + ViewInfos view_storage_cpy; #ifdef USE_GPU_SELECT uint select_id; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 95691a0df68..b01c901c77f 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -216,6 +216,8 @@ static void drw_shgroup_uniform(DRWShadingGroup *shgroup, BLI_assert(arraysize > 0 && arraysize <= 16); BLI_assert(length >= 0 && length <= 16); BLI_assert(!ELEM(type, + DRW_UNIFORM_STORAGE_BLOCK, + DRW_UNIFORM_STORAGE_BLOCK_REF, DRW_UNIFORM_BLOCK, DRW_UNIFORM_BLOCK_REF, DRW_UNIFORM_TEXTURE, @@ -310,6 +312,50 @@ void DRW_shgroup_uniform_block_ref_ex(DRWShadingGroup *shgroup, drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK_REF, ubo, 0, 0, 1); } +void DRW_shgroup_storage_block_ex(DRWShadingGroup *shgroup, + const char *name, + const GPUStorageBuf *ssbo DRW_DEBUG_FILE_LINE_ARGS) +{ + BLI_assert(ssbo != NULL); + /* TODO(@fclem): Fix naming inconsistency. */ + int loc = GPU_shader_get_ssbo(shgroup->shader, name); + if (loc == -1) { +#ifdef DRW_UNUSED_RESOURCE_TRACKING + printf("%s:%d: Unable to locate binding of shader storage buffer object: %s.\n", + file, + line, + name); +#else + /* TODO(@fclem): Would be good to have, but eevee has too much of this for the moment. */ + // BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects."); +#endif + return; + } + drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_STORAGE_BLOCK, ssbo, 0, 0, 1); +} + +void DRW_shgroup_storage_block_ref_ex(DRWShadingGroup *shgroup, + const char *name, + GPUStorageBuf **ssbo DRW_DEBUG_FILE_LINE_ARGS) +{ + BLI_assert(ssbo != NULL); + /* TODO(@fclem): Fix naming inconsistency. */ + int loc = GPU_shader_get_ssbo(shgroup->shader, name); + if (loc == -1) { +#ifdef DRW_UNUSED_RESOURCE_TRACKING + printf("%s:%d: Unable to locate binding of shader storage buffer object: %s.\n", + file, + line, + name); +#else + /* TODO(@fclem): Would be good to have, but eevee has too much of this for the moment. */ + // BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects."); +#endif + return; + } + drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_STORAGE_BLOCK_REF, ssbo, 0, 0, 1); +} + void DRW_shgroup_uniform_bool(DRWShadingGroup *shgroup, const char *name, const int *value, @@ -774,6 +820,12 @@ static void drw_command_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]) cmd->groups_ref = groups_ref; } +static void drw_command_compute_indirect(DRWShadingGroup *shgroup, GPUStorageBuf *indirect_buf) +{ + DRWCommandComputeIndirect *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE_INDIRECT); + cmd->indirect_buf = indirect_buf; +} + static void drw_command_barrier(DRWShadingGroup *shgroup, eGPUBarrier type) { DRWCommandBarrier *cmd = drw_command_create(shgroup, DRW_CMD_BARRIER); @@ -912,6 +964,12 @@ void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]) drw_command_compute_ref(shgroup, groups_ref); } +void DRW_shgroup_call_compute_indirect(DRWShadingGroup *shgroup, GPUStorageBuf *indirect_buf) +{ + BLI_assert(GPU_compute_shader_support()); + + drw_command_compute_indirect(shgroup, indirect_buf); +} void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type) { BLI_assert(GPU_compute_shader_support()); @@ -1767,7 +1825,7 @@ static void draw_frustum_bound_sphere_calc(const BoundBox *bbox, } } -static void draw_view_matrix_state_update(DRWViewUboStorage *storage, +static void draw_view_matrix_state_update(ViewInfos *storage, const float viewmat[4][4], const float winmat[4][4]) { @@ -1979,7 +2037,7 @@ void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len) BLI_assert(plane_len <= MAX_CLIP_PLANES); view->clip_planes_len = plane_len; if (plane_len > 0) { - memcpy(view->storage.clipplanes, planes, sizeof(float[4]) * plane_len); + memcpy(view->storage.clip_planes, planes, sizeof(float[4]) * plane_len); } } @@ -2031,21 +2089,21 @@ float DRW_view_far_distance_get(const DRWView *view) void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse) { view = (view) ? view : DST.view_default; - const DRWViewUboStorage *storage = &view->storage; + const ViewInfos *storage = &view->storage; copy_m4_m4(mat, (inverse) ? storage->viewinv : storage->viewmat); } void DRW_view_winmat_get(const DRWView *view, float mat[4][4], bool inverse) { view = (view) ? view : DST.view_default; - const DRWViewUboStorage *storage = &view->storage; + const ViewInfos *storage = &view->storage; copy_m4_m4(mat, (inverse) ? storage->wininv : storage->winmat); } void DRW_view_persmat_get(const DRWView *view, float mat[4][4], bool inverse) { view = (view) ? view : DST.view_default; - const DRWViewUboStorage *storage = &view->storage; + const ViewInfos *storage = &view->storage; copy_m4_m4(mat, (inverse) ? storage->persinv : storage->persmat); } diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 07e47a0c691..7d6ce51ff35 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -319,6 +319,7 @@ void DRW_state_reset(void) GPU_texture_unbind_all(); GPU_uniformbuf_unbind_all(); + GPU_storagebuf_unbind_all(); /* Should stay constant during the whole rendering. */ GPU_point_size(5); @@ -621,6 +622,12 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, case DRW_UNIFORM_BLOCK_REF: GPU_uniformbuf_bind(*uni->block_ref, uni->location); break; + case DRW_UNIFORM_STORAGE_BLOCK: + GPU_storagebuf_bind(uni->ssbo, uni->location); + break; + case DRW_UNIFORM_STORAGE_BLOCK_REF: + GPU_storagebuf_bind(*uni->ssbo_ref, uni->location); + break; case DRW_UNIFORM_BLOCK_OBMATS: state->obmats_loc = uni->location; GPU_uniformbuf_bind(DST.vmempool->matrices_ubo[0], uni->location); @@ -915,6 +922,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) if (G.debug & G_DEBUG_GPU) { GPU_texture_unbind_all(); GPU_uniformbuf_unbind_all(); + GPU_storagebuf_unbind_all(); } } GPU_shader_bind(shgroup->shader); @@ -1043,6 +1051,9 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->compute_ref.groups_ref[1], cmd->compute_ref.groups_ref[2]); break; + case DRW_CMD_COMPUTE_INDIRECT: + GPU_compute_dispatch_indirect(shgroup->shader, cmd->compute_indirect.indirect_buf); + break; case DRW_CMD_BARRIER: GPU_memory_barrier(cmd->barrier.type); break; @@ -1057,8 +1068,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) } } -static void drw_update_view(void) +static void drw_update_view(const float viewport_size[2]) { + ViewInfos *storage = &DST.view_active->storage; + copy_v2_v2(storage->viewport_size, viewport_size); + copy_v2_v2(storage->viewport_size_inverse, viewport_size); + invert_v2(storage->viewport_size_inverse); + /* TODO(fclem): update a big UBO and only bind ranges here. */ GPU_uniformbuf_update(G_draw.view_ubo, &DST.view_active->storage); @@ -1086,8 +1102,11 @@ static void drw_draw_pass_ex(DRWPass *pass, BLI_assert(DST.buffer_finish_called && "DRW_render_instance_buffer_finish had not been called before drawing"); - if (DST.view_previous != DST.view_active || DST.view_active->is_dirty) { - drw_update_view(); + float viewport[4]; + GPU_viewport_size_get_f(viewport); + if (DST.view_previous != DST.view_active || DST.view_active->is_dirty || + !equals_v2v2(DST.view_active->storage.viewport_size, &viewport[2])) { + drw_update_view(&viewport[2]); DST.view_active->is_dirty = false; DST.view_previous = DST.view_active; } diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index 687cf8bd2fb..5fc76bc25e6 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -2,6 +2,10 @@ #ifndef GPU_SHADER # include "GPU_shader_shared_utils.h" + +typedef struct ViewInfos ViewInfos; +typedef struct ObjectMatrices ObjectMatrices; +typedef struct ObjectInfos ObjectInfos; #endif #define DRW_SHADER_SHARED_H @@ -21,6 +25,14 @@ struct ViewInfos { float4 viewvecs[2]; /* Should not be here. Not view dependent (only main view). */ float4 viewcamtexcofac; + + float2 viewport_size; + float2 viewport_size_inverse; + + /** Frustum culling data. */ + /** NOTE: vec3 arrays are padded to vec4. */ + float4 frustum_corners[8]; + float4 frustum_planes[6]; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc index b54df928418..b36cb5c809e 100644 --- a/source/blender/draw/intern/draw_texture_pool.cc +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -23,6 +23,10 @@ struct DRWTexturePool { Vector<DRWTexturePoolHandle> handles; /* Cache last result to avoid linear search each time. */ int last_user_id = -1; + + Vector<GPUTexture *> tmp_tex_pruned; + Vector<GPUTexture *> tmp_tex_released; + Vector<GPUTexture *> tmp_tex_acquired; }; DRWTexturePool *DRW_texture_pool_create() @@ -94,6 +98,68 @@ GPUTexture *DRW_texture_pool_query( return handle.texture; } +GPUTexture *DRW_texture_pool_texture_acquire(DRWTexturePool *pool, + int width, + int height, + eGPUTextureFormat format) +{ + GPUTexture *tmp_tex = nullptr; + int64_t found_index = 0; + + auto texture_match = [&](GPUTexture *tex) -> bool { + /* TODO(@fclem): We could reuse texture using texture views if the formats are compatible. */ + return (GPU_texture_format(tex) == format) && (GPU_texture_width(tex) == width) && + (GPU_texture_height(tex) == height); + }; + + /* Search released texture first. */ + for (auto i : pool->tmp_tex_released.index_range()) { + if (texture_match(pool->tmp_tex_released[i])) { + tmp_tex = pool->tmp_tex_released[i]; + found_index = i; + break; + } + } + + if (tmp_tex) { + pool->tmp_tex_released.remove_and_reorder(found_index); + } + else { + /* Then search pruned texture. */ + for (auto i : pool->tmp_tex_pruned.index_range()) { + if (texture_match(pool->tmp_tex_pruned[i])) { + tmp_tex = pool->tmp_tex_pruned[i]; + found_index = i; + break; + } + } + + if (tmp_tex) { + pool->tmp_tex_pruned.remove_and_reorder(found_index); + } + } + + if (!tmp_tex) { + /* Create a new texture in last resort. */ + char name[16] = "DRW_tex_pool"; + if (G.debug & G_DEBUG_GPU) { + int texture_id = pool->handles.size(); + SNPRINTF(name, "DRW_tex_pool_%d", texture_id); + } + tmp_tex = GPU_texture_create_2d(name, width, height, 1, format, nullptr); + } + + pool->tmp_tex_acquired.append(tmp_tex); + + return tmp_tex; +} + +void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex) +{ + pool->tmp_tex_acquired.remove_first_occurrence_and_reorder(tmp_tex); + pool->tmp_tex_released.append(tmp_tex); +} + void DRW_texture_pool_reset(DRWTexturePool *pool) { pool->last_user_id = -1; @@ -117,4 +183,11 @@ void DRW_texture_pool_reset(DRWTexturePool *pool) pool->handles.remove_and_reorder(i); } } + + BLI_assert(pool->tmp_tex_acquired.is_empty()); + for (GPUTexture *tmp_tex : pool->tmp_tex_pruned) { + GPU_texture_free(tmp_tex); + } + pool->tmp_tex_pruned = pool->tmp_tex_released; + pool->tmp_tex_released.clear(); } diff --git a/source/blender/draw/intern/draw_texture_pool.h b/source/blender/draw/intern/draw_texture_pool.h index 5a7915e15e7..1c30ea88552 100644 --- a/source/blender/draw/intern/draw_texture_pool.h +++ b/source/blender/draw/intern/draw_texture_pool.h @@ -30,6 +30,17 @@ void DRW_texture_pool_free(DRWTexturePool *pool); GPUTexture *DRW_texture_pool_query( DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user); /** + * Returns a temporary texture that needs to be released after use. Texture content is undefined. + */ +GPUTexture *DRW_texture_pool_texture_acquire(DRWTexturePool *pool, + int width, + int height, + eGPUTextureFormat format); +/** + * Releases a previously acquired texture. + */ +void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex); +/** * Resets the user bits for each texture in the pool and delete unused ones. */ void DRW_texture_pool_reset(DRWTexturePool *pool); diff --git a/source/blender/draw/intern/draw_view_data.cc b/source/blender/draw/intern/draw_view_data.cc index 682728eb22b..0e55d28f6df 100644 --- a/source/blender/draw/intern/draw_view_data.cc +++ b/source/blender/draw/intern/draw_view_data.cc @@ -37,7 +37,10 @@ struct DRWViewData { DRWViewData *DRW_view_data_create(ListBase *engine_types) { + const int engine_types_len = BLI_listbase_count(engine_types); + DRWViewData *view_data = new DRWViewData(); + view_data->engines.reserve(engine_types_len); LISTBASE_FOREACH (DRWRegisteredDrawEngine *, type, engine_types) { ViewportEngineData engine = {}; engine.engine_type = type; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index ae536f0b87c..27149a80f9b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -419,7 +419,7 @@ static void extract_edituv_points_init_subdiv(const DRWSubdivCache *subdiv_cache } static void extract_edituv_points_iter_subdiv_bm(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *mr, + const MeshRenderData *UNUSED(mr), void *_data, uint subdiv_quad_index, const BMFace *coarse_quad) @@ -431,11 +431,8 @@ static void extract_edituv_points_iter_subdiv_bm(const DRWSubdivCache *subdiv_ca uint end_loop_idx = (subdiv_quad_index + 1) * 4; for (uint i = start_loop_idx; i < end_loop_idx; i++) { const int vert_origindex = subdiv_loop_vert_index[i]; - const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && - vert_origindex != -1 && - mr->v_origindex[vert_origindex] != ORIGINDEX_NONE); edituv_point_add(data, - (BM_elem_flag_test(coarse_quad, BM_ELEM_HIDDEN)) || !real_vert, + (BM_elem_flag_test(coarse_quad, BM_ELEM_HIDDEN) || vert_origindex == -1), BM_elem_flag_test(coarse_quad, BM_ELEM_SELECT) != 0, i); } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc index 2dfd2cd43dc..f79fe345f5a 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc @@ -137,7 +137,7 @@ static void extract_edituv_data_iter_subdiv_bm(const DRWSubdivCache *subdiv_cach uint end_loop_idx = (subdiv_quad_index + 1) * 4; for (uint i = start_loop_idx; i < end_loop_idx; i++) { const int vert_origindex = subdiv_loop_vert_index[i]; - const int edge_origindex = subdiv_loop_edge_index[i]; + int edge_origindex = subdiv_loop_edge_index[i]; EditLoopData *edit_loop_data = &data->vbo_data[i]; memset(edit_loop_data, 0, sizeof(EditLoopData)); @@ -149,6 +149,22 @@ static void extract_edituv_data_iter_subdiv_bm(const DRWSubdivCache *subdiv_cach mesh_render_data_loop_flag(mr, l, data->cd_ofs, edit_loop_data); mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, edit_loop_data); } + else { + if (edge_origindex == -1) { + /* Find if the loop's vert is not part of an edit edge. + * For this, we check if the previous loop was on an edge. */ + const uint loop_index_last = (i == start_loop_idx) ? end_loop_idx - 1 : i - 1; + edge_origindex = subdiv_loop_edge_index[loop_index_last]; + } + if (edge_origindex != -1) { + /* Mapped points on an edge between two edit verts. */ + BMEdge *eed = BM_edge_at_index(mr->bm, edge_origindex); + BMLoop *l = BM_face_edge_share_loop(const_cast<BMFace *>(coarse_quad), eed); + mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, edit_loop_data); + } + } + + mesh_render_data_face_flag(mr, coarse_quad, data->cd_ofs, edit_loop_data); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index b532ff0080e..4532f3c3710 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -134,7 +134,7 @@ static void extract_uv_init_subdiv(const DRWSubdivCache *subdiv_cache, uint uv_layers; if (!mesh_extract_uv_format_init( &format, cache, &coarse_mesh->ldata, MR_EXTRACT_MESH, uv_layers)) { - // TODO(kevindietrich): handle this more gracefully. + /* TODO(kevindietrich): handle this more gracefully. */ v_len = 1; } diff --git a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl new file mode 100644 index 00000000000..9e3c219a30b --- /dev/null +++ b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl @@ -0,0 +1,409 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +#ifndef DRW_GPENCIL_INFO +# error "Missing additional info draw_gpencil" +#endif + +#ifdef GPU_FRAGMENT_SHADER +float gpencil_stroke_round_cap_mask(vec2 p1, vec2 p2, vec2 aspect, float thickness, float hardfac) +{ + /* We create our own uv space to avoid issues with triangulation and linear + * interpolation artifacts. */ + vec2 line = p2.xy - p1.xy; + vec2 pos = gl_FragCoord.xy - p1.xy; + float line_len = length(line); + float half_line_len = line_len * 0.5; + /* Normalize */ + line = (line_len > 0.0) ? (line / line_len) : vec2(1.0, 0.0); + /* Create a uv space that englobe the whole segment into a capsule. */ + vec2 uv_end; + uv_end.x = max(abs(dot(line, pos) - half_line_len) - half_line_len, 0.0); + uv_end.y = dot(vec2(-line.y, line.x), pos); + /* Divide by stroke radius. */ + uv_end /= thickness; + uv_end *= aspect; + + float dist = clamp(1.0 - length(uv_end) * 2.0, 0.0, 1.0); + if (hardfac > 0.999) { + return step(1e-8, dist); + } + else { + /* Modulate the falloff profile */ + float hardness = 1.0 - hardfac; + dist = pow(dist, mix(0.01, 10.0, hardness)); + return smoothstep(0.0, 1.0, dist); + } +} +#endif + +vec2 gpencil_decode_aspect(int packed_data) +{ + float asp = float(uint(packed_data) & 0x1FFu) * (1.0 / 255.0); + return (asp > 1.0) ? vec2(1.0, (asp - 1.0)) : vec2(asp, 1.0); +} + +float gpencil_decode_uvrot(int packed_data) +{ + uint udata = uint(packed_data); + float uvrot = 1e-8 + float((udata & 0x1FE00u) >> 9u) * (1.0 / 255.0); + return ((udata & 0x20000u) != 0u) ? -uvrot : uvrot; +} + +float gpencil_decode_hardness(int packed_data) +{ + return float((uint(packed_data) & 0x3FC0000u) >> 18u) * (1.0 / 255.0); +} + +vec2 gpencil_project_to_screenspace(vec4 v, vec4 viewport_size) +{ + return ((v.xy / v.w) * 0.5 + 0.5) * viewport_size.xy; +} + +float gpencil_stroke_thickness_modulate(float thickness, vec4 ndc_pos, vec4 viewport_size) +{ + /* Modify stroke thickness by object and layer factors. */ + thickness = max(1.0, thickness * gpThicknessScale + gpThicknessOffset); + + if (gpThicknessIsScreenSpace) { + /* Multiply offset by view Z so that offset is constant in screenspace. + * (e.i: does not change with the distance to camera) */ + thickness *= ndc_pos.w; + } + else { + /* World space point size. */ + thickness *= gpThicknessWorldScale * ProjectionMatrix[1][1] * viewport_size.y; + } + return thickness; +} + +float gpencil_clamp_small_stroke_thickness(float thickness, vec4 ndc_pos) +{ + /* To avoid aliasing artifacts, we clamp the line thickness and + * reduce its opacity in the fragment shader.*/ + float min_thickness = ndc_pos.w * 1.3; + thickness = max(min_thickness, thickness); + + return thickness; +} + +#ifdef GPU_VERTEX_SHADER + +/* Trick to detect if a drawcall is stroke or fill. + * This does mean that we need to draw an empty stroke segment before starting + * to draw the real stroke segments. */ +# define GPENCIL_IS_STROKE_VERTEX (gl_InstanceID != 0) + +/** + * Returns value of gl_Position. + * + * To declare in vertex shader. + * in ivec4 ma, ma1, ma2, ma3; + * in vec4 pos, pos1, pos2, pos3, uv1, uv2, col1, col2, fcol1; + * + * All of these attributes are quad loaded the same way + * as GL_LINES_ADJACENCY would feed a geometry shader: + * - ma reference the previous adjacency point. + * - ma1 reference the current line first point. + * - ma2 reference the current line second point. + * - ma3 reference the next adjacency point. + * Note that we are rendering quad instances and not using any index buffer + *(except for fills). + * + * Material : x is material index, y is stroke_id, z is point_id, + * w is aspect & rotation & hardness packed. + * Position : contains thickness in 4th component. + * UV : xy is UV for fills, z is U of stroke, w is strength. + * + * + * WARNING: Max attribute count is actually 14 because OSX OpenGL implementation + * considers gl_VertexID and gl_InstanceID as vertex attribute. (see T74536) + */ +vec4 gpencil_vertex(ivec4 ma, + ivec4 ma1, + ivec4 ma2, + ivec4 ma3, + vec4 pos, + vec4 pos1, + vec4 pos2, + vec4 pos3, + vec4 uv1, + vec4 uv2, + vec4 col1, + vec4 col2, + vec4 fcol1, + vec4 viewport_size, + gpMaterialFlag material_flags, + vec2 alignment_rot, + /* World Position. */ + out vec3 out_P, + /* World Normal. */ + out vec3 out_N, + /* Vertex Color. */ + out vec4 out_color, + /* Stroke Strength. */ + out float out_strength, + /* UV coordinates. */ + out vec2 out_uv, + /* Screen-Space segment endpoints. */ + out vec4 out_sspos, + /* Stroke aspect ratio. */ + out vec2 out_aspect, + /* Stroke thickness (x: clamped, y: unclamped). */ + out vec2 out_thickness, + /* Stroke hardness. */ + out float out_hardness) +{ +# define thickness1 pos1.w +# define thickness2 pos2.w +# define strength1 uv1.w +# define strength2 uv2.w +/* Packed! need to be decoded. */ +# define hardness1 ma1.w +# define hardness2 ma2.w +# define uvrot1 ma1.w +# define aspect1 ma1.w + + vec4 out_ndc; + + if (GPENCIL_IS_STROKE_VERTEX) { + bool is_dot = flag_test(material_flags, GP_STROKE_ALIGNMENT); + bool is_squares = !flag_test(material_flags, GP_STROKE_ALIGNMENT); + + /* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */ + if (!is_dot && ma.x == -1 && ma2.x == -1) { + is_dot = true; + is_squares = false; + } + + /* Endpoints, we discard the vertices. */ + if (ma1.x == -1 || (!is_dot && ma2.x == -1)) { + /* We set the vertex at the camera origin to generate 0 fragments. */ + out_ndc = vec4(0.0, 0.0, -3e36, 0.0); + return out_ndc; + } + + /* Avoid using a vertex attribute for quad positioning. */ + float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */ + float y = float(gl_VertexID & 2) - 1.0; /* [-1..1] */ + + bool use_curr = is_dot || (x == -1.0); + + vec3 wpos_adj = transform_point(ModelMatrix, (use_curr) ? pos.xyz : pos3.xyz); + vec3 wpos1 = transform_point(ModelMatrix, pos1.xyz); + vec3 wpos2 = transform_point(ModelMatrix, pos2.xyz); + + vec3 T; + if (is_dot) { + /* Shade as facing billboards. */ + T = ViewMatrixInverse[0].xyz; + } + else if (use_curr && ma.x != -1) { + T = wpos1 - wpos_adj; + } + else { + T = wpos2 - wpos1; + } + T = safe_normalize(T); + + vec3 B = cross(T, ViewMatrixInverse[2].xyz); + out_N = normalize(cross(B, T)); + + vec4 ndc_adj = point_world_to_ndc(wpos_adj); + vec4 ndc1 = point_world_to_ndc(wpos1); + vec4 ndc2 = point_world_to_ndc(wpos2); + + out_ndc = (use_curr) ? ndc1 : ndc2; + out_P = (use_curr) ? wpos1 : wpos2; + out_strength = abs((use_curr) ? strength1 : strength2); + + vec2 ss_adj = gpencil_project_to_screenspace(ndc_adj, viewport_size); + vec2 ss1 = gpencil_project_to_screenspace(ndc1, viewport_size); + vec2 ss2 = gpencil_project_to_screenspace(ndc2, viewport_size); + /* Screenspace Lines tangents. */ + float line_len; + vec2 line = safe_normalize_len(ss2 - ss1, line_len); + vec2 line_adj = safe_normalize((use_curr) ? (ss1 - ss_adj) : (ss_adj - ss2)); + + float thickness = abs((use_curr) ? thickness1 : thickness2); + thickness = gpencil_stroke_thickness_modulate(thickness, out_ndc, viewport_size); + float clamped_thickness = gpencil_clamp_small_stroke_thickness(thickness, out_ndc); + + out_uv = vec2(x, y) * 0.5 + 0.5; + out_hardness = gpencil_decode_hardness(use_curr ? hardness1 : hardness2); + + if (is_dot) { + uint alignement_mode = material_flags & GP_STROKE_ALIGNMENT; + + /* For one point strokes use object alignment. */ + if (alignement_mode == GP_STROKE_ALIGNMENT_STROKE && ma.x == -1 && ma2.x == -1) { + alignement_mode = GP_STROKE_ALIGNMENT_OBJECT; + } + + vec2 x_axis; + if (alignement_mode == GP_STROKE_ALIGNMENT_STROKE) { + x_axis = (ma2.x == -1) ? line_adj : line; + } + else if (alignement_mode == GP_STROKE_ALIGNMENT_FIXED) { + /* Default for no-material drawing. */ + x_axis = vec2(1.0, 0.0); + } + else { /* GP_STROKE_ALIGNMENT_OBJECT */ + vec4 ndc_x = point_world_to_ndc(wpos1 + ModelMatrix[0].xyz); + vec2 ss_x = gpencil_project_to_screenspace(ndc_x, viewport_size); + x_axis = safe_normalize(ss_x - ss1); + } + + /* Rotation: Encoded as Cos + Sin sign. */ + float uv_rot = gpencil_decode_uvrot(uvrot1); + float rot_sin = sqrt(max(0.0, 1.0 - uv_rot * uv_rot)) * sign(uv_rot); + float rot_cos = abs(uv_rot); + /* TODO(@fclem): Optimize these 2 matrix mul into one by only having one rotation angle and + * using a cosine approximation. */ + x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis; + x_axis = mat2(alignment_rot.x, -alignment_rot.y, alignment_rot.y, alignment_rot.x) * x_axis; + /* Rotate 90° Counter-Clockwise. */ + vec2 y_axis = vec2(-x_axis.y, x_axis.x); + + out_aspect = gpencil_decode_aspect(aspect1); + + x *= out_aspect.x; + y *= out_aspect.y; + + /* Invert for vertex shader. */ + out_aspect = 1.0 / out_aspect; + + out_ndc.xy += (x * x_axis + y * y_axis) * viewport_size.zw * clamped_thickness; + + out_sspos.xy = ss1; + out_sspos.zw = ss1 + x_axis * 0.5; + out_thickness.x = (is_squares) ? 1e18 : (clamped_thickness / out_ndc.w); + out_thickness.y = (is_squares) ? 1e18 : (thickness / out_ndc.w); + } + else { + bool is_stroke_start = (ma.x == -1 && x == -1); + bool is_stroke_end = (ma3.x == -1 && x == 1); + + /* Mitter tangent vector. */ + vec2 miter_tan = safe_normalize(line_adj + line); + float miter_dot = dot(miter_tan, line_adj); + /* Break corners after a certain angle to avoid really thick corners. */ + const float miter_limit = 0.5; /* cos(60°) */ + bool miter_break = (miter_dot < miter_limit); + miter_tan = (miter_break || is_stroke_start || is_stroke_end) ? line : + (miter_tan / miter_dot); + /* Rotate 90° Counter-Clockwise. */ + vec2 miter = vec2(-miter_tan.y, miter_tan.x); + + out_sspos.xy = ss1; + out_sspos.zw = ss2; + out_thickness.x = clamped_thickness / out_ndc.w; + out_thickness.y = thickness / out_ndc.w; + out_aspect = vec2(1.0); + + vec2 screen_ofs = miter * y; + + /* Reminder: we packed the cap flag into the sign of strength and thickness sign. */ + if ((is_stroke_start && strength1 > 0.0) || (is_stroke_end && thickness1 > 0.0) || + (miter_break && !is_stroke_start && !is_stroke_end)) { + screen_ofs += line * x; + } + + out_ndc.xy += screen_ofs * viewport_size.zw * clamped_thickness; + + out_uv.x = (use_curr) ? uv1.z : uv2.z; + } + + out_color = (use_curr) ? col1 : col2; + } + else { + /* Fill vertex. */ + out_P = transform_point(ModelMatrix, pos1.xyz); + out_ndc = point_world_to_ndc(out_P); + out_uv = uv1.xy; + out_thickness.x = 1e18; + out_thickness.y = 1e20; + out_hardness = 1.0; + out_aspect = vec2(1.0); + out_sspos = vec4(0.0); + + /* Flat normal following camera and object bounds. */ + vec3 V = cameraVec(ModelMatrix[3].xyz); + vec3 N = normal_world_to_object(V); + N *= OrcoTexCoFactors[1].xyz; + N = normal_object_to_world(N); + out_N = safe_normalize(N); + + /* Decode fill opacity. */ + out_color = vec4(fcol1.rgb, floor(fcol1.a / 10.0)); + out_color.a /= 10000.0; + + /* We still offset the fills a little to avoid overlaps */ + out_ndc.z += 0.000002; + } + +# undef thickness1 +# undef thickness2 +# undef strength1 +# undef strength2 +# undef hardness1 +# undef hardness2 +# undef uvrot1 +# undef aspect1 + + return out_ndc; +} + +vec4 gpencil_vertex(ivec4 ma, + ivec4 ma1, + ivec4 ma2, + ivec4 ma3, + vec4 pos, + vec4 pos1, + vec4 pos2, + vec4 pos3, + vec4 uv1, + vec4 uv2, + vec4 col1, + vec4 col2, + vec4 fcol1, + vec4 viewport_size, + out vec3 out_P, + out vec3 out_N, + out vec4 out_color, + out float out_strength, + out vec2 out_uv, + out vec4 out_sspos, + out vec2 out_aspect, + out vec2 out_thickness, + out float out_hardness) +{ + return gpencil_vertex(ma, + ma1, + ma2, + ma3, + pos, + pos1, + pos2, + pos3, + uv1, + uv2, + col1, + col2, + fcol1, + viewport_size, + GP_STROKE_ALIGNMENT_OBJECT, + vec2(1.0, 0.0), + out_P, + out_N, + out_color, + out_strength, + out_uv, + out_sspos, + out_aspect, + out_thickness, + out_hardness); +} + +#endif diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index ed8b8aeb849..7f94b7ea1c1 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -158,7 +158,7 @@ float hair_shaperadius(float shape, float root, float tip, float time) return (radius * (root - tip)) + tip; } -# ifdef OS_MAC +# if defined(OS_MAC) && defined(GPU_OPENGL) in float dummy; # endif @@ -178,7 +178,7 @@ void hair_get_pos_tan_binor_time(bool is_persp, wpos = data.point_position; time = data.point_time; -# ifdef OS_MAC +# if defined(OS_MAC) && defined(GPU_OPENGL) /* Generate a dummy read to avoid the driver bug with shaders having no * vertex reads on macOS (T60171) */ wpos.y += dummy * 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 7b701d1d81b..6d4452c18c8 100644 --- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl @@ -86,7 +86,7 @@ float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection) float line_unit_box_intersect_dist_safe(vec3 lineorigin, vec3 linedirection) { vec3 safe_linedirection = max(vec3(1e-8), abs(linedirection)) * - mix(vec3(1.0), -vec3(1.0), lessThan(linedirection, vec3(0.0))); + select(vec3(1.0), -vec3(1.0), lessThan(linedirection, vec3(0.0))); return line_unit_box_intersect_dist(lineorigin, safe_linedirection); } diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index bc31649fd0f..4d0ffaeb40f 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -68,12 +68,12 @@ mat2 rot2_from_angle(float a) #define avg9(a, b, c, d, e, f, g, h, i) (a + b + c + d + e + f + g + h + i) * (1.0 / 9.0) /* clang-format off */ -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)); } +#define min_v2(v) min((v).x, (v).y) +#define min_v3(v) min((v).x, min((v).y, (v).z)) +#define min_v4(v) min(min((v).x, (v).y), min((v).z, (v).w)) +#define max_v2(v) max((v).x, (v).y) +#define max_v3(v) max((v).x, max((v).y, (v).z)) +#define max_v4(v) 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); } @@ -84,11 +84,14 @@ 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 safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; } -vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } -vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } +vec2 safe_rcp(vec2 a) { return select(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } +vec3 safe_rcp(vec3 a) { return select(vec3(0.0), (1.0 / a), notEqual(a, vec3(0.0))); } +vec4 safe_rcp(vec4 a) { return select(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } float safe_sqrt(float a) { return sqrt(max(a, 0.0)); } +float safe_acos(float a) { return acos(clamp(a, -1.0, 1.0)); } + float sqr(float a) { return a * a; } vec2 sqr(vec2 a) { return a * a; } vec3 sqr(vec3 a) { return a * a; } @@ -102,6 +105,12 @@ float pow8(float x) { return sqr(sqr(sqr(x))); } float len_squared(vec3 a) { return dot(a, a); } float len_squared(vec2 a) { return dot(a, a); } +bool flag_test(uint flag, uint val) { return (flag & val) != 0u; } +bool flag_test(int flag, int val) { return (flag & val) != 0; } + +void set_flag_from_test(inout uint value, bool test, uint flag) { if (test) { value |= flag; } else { value &= ~flag; } } +void set_flag_from_test(inout int value, bool test, int flag) { if (test) { value |= flag; } else { value &= ~flag; } } + #define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights))); #define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights))); @@ -109,6 +118,52 @@ float len_squared(vec2 a) { return dot(a, a); } #define saturate(a) clamp(a, 0.0, 1.0) +#define in_range_inclusive(val, min_v, max_v) \ + (all(greaterThanEqual(val, min_v)) && all(lessThanEqual(val, max_v))) +#define in_range_exclusive(val, min_v, max_v) \ + (all(greaterThan(val, min_v)) && all(lessThan(val, max_v))) +#define in_texture_range(texel, tex) \ + (all(greaterThanEqual(texel, ivec2(0))) && all(lessThan(texel, textureSize(tex, 0).xy))) + +uint divide_ceil_u(uint visible_count, uint divisor) +{ + return (visible_count + (divisor - 1u)) / divisor; +} + +int divide_ceil_i(int visible_count, int divisor) +{ + return (visible_count + (divisor - 1)) / divisor; +} + +uint bit_field_mask(uint bit_width, uint bit_min) +{ + /* Cannot bit shift more than 31 positions. */ + uint mask = (bit_width > 31u) ? 0x0u : (0xFFFFFFFFu << bit_width); + return ~mask << bit_min; +} + +uvec2 unpackUvec2x16(uint data) +{ + return (uvec2(data) >> uvec2(0u, 16u)) & uvec2(0xFFFFu); +} + +uint packUvec2x16(uvec2 data) +{ + data = (data & 0xFFFFu) << uvec2(0u, 16u); + return data.x | data.y; +} + +uvec4 unpackUvec4x8(uint data) +{ + return (uvec4(data) >> uvec4(0u, 8u, 16u, 24u)) & uvec4(0xFFu); +} + +uint packUvec4x8(uvec4 data) +{ + data = (data & 0xFFu) << uvec4(0u, 8u, 16u, 24u); + return data.x | data.y | data.z | data.w; +} + float distance_squared(vec2 a, vec2 b) { a -= b; @@ -130,6 +185,21 @@ vec3 safe_normalize(vec3 v) return v / len; } +vec2 safe_normalize_len(vec2 v, out float len) +{ + len = length(v); + if (isnan(len) || len == 0.0) { + return vec2(1.0, 0.0); + } + return v / len; +} + +vec2 safe_normalize(vec2 v) +{ + float len; + return safe_normalize_len(v, len); +} + vec3 normalize_len(vec3 v, out float len) { len = length(v); @@ -141,6 +211,11 @@ vec4 safe_color(vec4 c) /* Clamp to avoid black square artifacts if a pixel goes NaN. */ return clamp(c, vec4(0.0), vec4(1e20)); /* 1e20 arbitrary. */ } +vec3 safe_color(vec3 c) +{ + /* Clamp to avoid black square artifacts if a pixel goes NaN. */ + return clamp(c, vec3(0.0), vec3(1e20)); /* 1e20 arbitrary. */ +} /** \} */ @@ -188,3 +263,16 @@ vec3 neon_gradient(float t) { return clamp(vec3(t * 1.3 + 0.1, sqr(abs(0.43 - t) * 1.7), (1.0 - t) * 1.7), 0.0, 1.0); } +vec3 heatmap_gradient(float t) +{ + float a = pow(t, 1.5) * 0.8 + 0.2; + float b = smoothstep(0.0, 0.35, t) + t * 0.5; + float c = smoothstep(0.5, 1.0, t); + float d = max(1.0 - t * 1.7, t * 7.0 - 6.0); + return saturate(a * vec3(b, c, d)); +} +vec3 hue_gradient(float t) +{ + vec3 p = abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0); + return (clamp(p - 1.0, 0.0, 1.0)); +} diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 2ac157ad208..6c58752c8bb 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -174,7 +174,7 @@ flat in int resourceIDFrag; /* Breaking this across multiple lines causes issues for some older GLSL compilers. */ /* clang-format off */ -#if !defined(GPU_INTEL) && !defined(GPU_DEPRECATED_AMD_DRIVER) && !defined(OS_MAC) && !defined(INSTANCED_ATTR) && !defined(DRW_LEGACY_MODEL_MATRIX) +#if !defined(GPU_INTEL) && !defined(GPU_DEPRECATED_AMD_DRIVER) && (!defined(OS_MAC) || defined(GPU_METAL)) && !defined(INSTANCED_ATTR) && !defined(DRW_LEGACY_MODEL_MATRIX) /* clang-format on */ /* Temporary until we fully make the switch. */ @@ -251,7 +251,7 @@ uniform mat4 ModelMatrixInverse; /* Due to some shader compiler bug, we somewhat need to access gl_VertexID * to make vertex shaders work. even if it's actually dead code. */ -#ifdef GPU_INTEL +#if defined(GPU_INTEL) && defined(GPU_OPENGL) # define GPU_INTEL_VERTEX_SHADER_WORKAROUND gl_Position.x = float(gl_VertexID); #else # define GPU_INTEL_VERTEX_SHADER_WORKAROUND diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh index 11b33dec0d4..392b016fc3b 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -4,4 +4,5 @@ GPU_SHADER_CREATE_INFO(draw_object_infos) .typedef_source("draw_shader_shared.h") + .define("OBINFO_LIB") .uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH); diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh index 4a148901d66..a699b9013ef 100644 --- a/source/blender/draw/intern/shaders/draw_view_info.hh +++ b/source/blender/draw/intern/shaders/draw_view_info.hh @@ -103,4 +103,28 @@ GPU_SHADER_CREATE_INFO(draw_pointcloud) GPU_SHADER_CREATE_INFO(draw_volume).additional_info("draw_modelmat", "draw_resource_id_uniform"); +GPU_SHADER_CREATE_INFO(draw_gpencil) + .typedef_source("gpencil_shader_shared.h") + .define("DRW_GPENCIL_INFO") + .vertex_in(0, Type::IVEC4, "ma") + .vertex_in(1, Type::IVEC4, "ma1") + .vertex_in(2, Type::IVEC4, "ma2") + .vertex_in(3, Type::IVEC4, "ma3") + .vertex_in(4, Type::VEC4, "pos") + .vertex_in(5, Type::VEC4, "pos1") + .vertex_in(6, Type::VEC4, "pos2") + .vertex_in(7, Type::VEC4, "pos3") + .vertex_in(8, Type::VEC4, "uv1") + .vertex_in(9, Type::VEC4, "uv2") + .vertex_in(10, Type::VEC4, "col1") + .vertex_in(11, Type::VEC4, "col2") + .vertex_in(12, Type::VEC4, "fcol1") + /* Per Object */ + .push_constant(Type::FLOAT, "gpThicknessScale") /* TODO(fclem): Replace with object info. */ + .push_constant(Type::FLOAT, "gpThicknessWorldScale") /* TODO(fclem): Same as above. */ + .define("gpThicknessIsScreenSpace", "(gpThicknessWorldScale < 0.0)") + /* Per Layer */ + .push_constant(Type::FLOAT, "gpThicknessOffset") + .additional_info("draw_modelmat", "draw_resource_id_uniform", "draw_object_infos"); + /** \} */ diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index df418b204f9..08379be36fa 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -1131,7 +1131,7 @@ static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAn { AnimChanRearrangeFp rearrange_func; ListBase anim_data_visible = {NULL, NULL}; - const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ac->obact); + const bool is_liboverride = (ac->obact != NULL) ? ID_IS_OVERRIDE_LIBRARY(ac->obact) : false; /* hack: invert mode so that functions will work in right order */ mode *= -1; diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index f18873cc22b..ed40845a47c 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -1292,8 +1292,8 @@ void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu, { uint i; BezTriple *bezt; - const float flat_direction_left[2] = {-handle_length, 0.f}; - const float flat_direction_right[2] = {handle_length, 0.f}; + const float flat_direction_left[2] = {-handle_length, 0.0f}; + const float flat_direction_right[2] = {handle_length, 0.0f}; /* Loop through an F-Curves keyframes. */ for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) { diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 24ba62fc0f7..aec2b0f769a 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -382,6 +382,68 @@ void blend_to_neighbor_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const /* ---------------- */ +float get_default_rna_value(FCurve *fcu, PropertyRNA *prop, PointerRNA *ptr) +{ + const int len = RNA_property_array_length(ptr, prop); + + float default_value = 0; + /* Find the default value of that property. */ + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + if (len) { + default_value = RNA_property_boolean_get_default_index(ptr, prop, fcu->array_index); + } + else { + default_value = RNA_property_boolean_get_default(ptr, prop); + } + break; + case PROP_INT: + if (len) { + default_value = RNA_property_int_get_default_index(ptr, prop, fcu->array_index); + } + else { + default_value = RNA_property_int_get_default(ptr, prop); + } + break; + case PROP_FLOAT: + if (len) { + default_value = RNA_property_float_get_default_index(ptr, prop, fcu->array_index); + } + else { + default_value = RNA_property_float_get_default(ptr, prop); + } + break; + + default: + break; + } + return default_value; +} + +/* This function blends the selected keyframes to the default value of the property the fcurve + * drives. */ +void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor) +{ + PointerRNA ptr; + PropertyRNA *prop; + + /* Check if path is valid. */ + if (!RNA_path_resolve_property(id_ptr, fcu->rna_path, &ptr, &prop)) { + return; + } + + const float default_value = get_default_rna_value(fcu, prop, &ptr); + + /* Blend selected keys to default */ + for (int i = 0; i < fcu->totvert; i++) { + if (fcu->bezt[i].f2 & SELECT) { + fcu->bezt[i].vec[1][1] = interpf(default_value, fcu->bezt[i].vec[1][1], factor); + } + } +} + +/* ---------------- */ + void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor) { BezTriple left_bezt = fcurve_segment_start_get(fcu, segment->start_index); diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index dcf8835c911..6fcdd21bad8 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -715,54 +715,55 @@ const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + Scene *scene = CTX_data_scene(C); KeyingSet *ks; EnumPropertyItem *item = NULL, item_tmp = {0}; int totitem = 0; int i = 0; - if (C != NULL) { - Scene *scene = CTX_data_scene(C); - /* active Keying Set - * - only include entry if it exists - */ - if (scene->active_keyingset) { - /* active Keying Set */ - item_tmp.identifier = "__ACTIVE__"; - item_tmp.name = "Active Keying Set"; - item_tmp.value = i; - RNA_enum_item_add(&item, &totitem, &item_tmp); + if (C == NULL) { + return DummyRNA_DEFAULT_items; + } - /* separator */ - RNA_enum_item_add_separator(&item, &totitem); - } + /* active Keying Set + * - only include entry if it exists + */ + if (scene->active_keyingset) { + /* active Keying Set */ + item_tmp.identifier = "__ACTIVE__"; + item_tmp.name = "Active Keying Set"; + item_tmp.value = i; + RNA_enum_item_add(&item, &totitem, &item_tmp); + + /* separator */ + RNA_enum_item_add_separator(&item, &totitem); + } - i++; + i++; - /* user-defined Keying Sets - * - these are listed in the order in which they were defined for the active scene - */ - if (scene->keyingsets.first) { - for (ks = scene->keyingsets.first; ks; ks = ks->next, i++) { - if (ANIM_keyingset_context_ok_poll(C, ks)) { - item_tmp.identifier = ks->idname; - item_tmp.name = ks->name; - item_tmp.description = ks->description; - item_tmp.value = i; - RNA_enum_item_add(&item, &totitem, &item_tmp); - } + /* user-defined Keying Sets + * - these are listed in the order in which they were defined for the active scene + */ + if (scene->keyingsets.first) { + for (ks = scene->keyingsets.first; ks; ks = ks->next, i++) { + if (ANIM_keyingset_context_ok_poll(C, ks)) { + item_tmp.identifier = ks->idname; + item_tmp.name = ks->name; + item_tmp.description = ks->description; + item_tmp.value = i; + RNA_enum_item_add(&item, &totitem, &item_tmp); } - - /* separator */ - RNA_enum_item_add_separator(&item, &totitem); } + + /* separator */ + RNA_enum_item_add_separator(&item, &totitem); } /* builtin Keying Sets */ i = -1; for (ks = builtin_keyingsets.first; ks; ks = ks->next, i--) { - /* Only show #KeyingSet if context is suitable or if there is no context which is needed - * to support key-bindings to be assigned since key bindings are not context aware. */ - if ((C == NULL) || ANIM_keyingset_context_ok_poll(C, ks)) { + /* only show KeyingSet if context is suitable */ + if (ANIM_keyingset_context_ok_poll(C, ks)) { item_tmp.identifier = ks->idname; item_tmp.name = ks->name; item_tmp.description = ks->description; diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 45d64807e65..be4829c02be 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -377,6 +377,15 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op) BKE_pose_channels_hash_free(pose); } + /* Armature ID itself is not freed below, however it has been modified (and is now completely + * empty). This needs to be told to the depsgraph, it will also ensure that the global + * memfile undo system properly detects the change. + * + * FIXME: Modifying an existing obdata because we are joining an object using it into another + * object is a very questionable behavior, which also does not match with other object types + * joining. */ + DEG_id_tag_update_ex(bmain, &curarm->id, ID_RECALC_GEOMETRY); + /* Fix all the drivers (and animation data) */ BKE_fcurves_main_cb(bmain, joined_armature_fix_animdata_cb, &afd); BLI_ghash_free(afd.names_map, MEM_freeN, NULL); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index d7240782840..08d5d6558e0 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -951,127 +951,180 @@ bool ED_armature_edit_select_pick_bone(bContext *C, Base *basact, EditBone *ebone, const int selmask, - const bool extend, - const bool deselect, - const bool toggle) + const struct SelectPick_Params *params) { - if (!ebone) { - return false; - } - ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); + bool changed = false; + bool found = false; - BLI_assert(BKE_object_is_in_editmode(basact->object)); - bArmature *arm = basact->object->data; - - if (!EBONE_SELECTABLE(arm, ebone)) { - return false; + if (ebone) { + bArmature *arm = basact->object->data; + if (EBONE_SELECTABLE(arm, ebone)) { + found = true; + } } - if (!extend && !deselect && !toggle) { - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( - view_layer, v3d, &bases_len); - ED_armature_edit_deselect_all_multi_ex(bases, bases_len); - MEM_freeN(bases); + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && + (ED_armature_ebone_selectflag_get(ebone) & selmask)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( + view_layer, v3d, &bases_len); + ED_armature_edit_deselect_all_multi_ex(bases, bases_len); + MEM_freeN(bases); + changed = true; + } } - /* By definition the non-root connected bones have no root point drawn, - * so a root selection needs to be delivered to the parent tip. */ + if (found) { + BLI_assert(BKE_object_is_in_editmode(basact->object)); + bArmature *arm = basact->object->data; - if (selmask & BONE_SELECTED) { - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - /* Bone is in a chain. */ - if (extend) { - /* Select this bone. */ - ebone->flag |= BONE_TIPSEL; - ebone->parent->flag |= BONE_TIPSEL; - } - else if (deselect) { - /* Deselect this bone. */ - ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* Only deselect parent tip if it is not selected. */ - if (!(ebone->parent->flag & BONE_SELECTED)) { - ebone->parent->flag &= ~BONE_TIPSEL; - } - } - else if (toggle) { - /* Toggle inverts this bone's selection. */ - if (ebone->flag & BONE_SELECTED) { - /* Deselect this bone. */ - ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* Only deselect parent tip if it is not selected. */ - if (!(ebone->parent->flag & BONE_SELECTED)) { - ebone->parent->flag &= ~BONE_TIPSEL; + /* By definition the non-root connected bones have no root point drawn, + * so a root selection needs to be delivered to the parent tip. */ + + if (selmask & BONE_SELECTED) { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + + /* Bone is in a chain. */ + switch (params->sel_op) { + case SEL_OP_ADD: { + /* Select this bone. */ + ebone->flag |= BONE_TIPSEL; + ebone->parent->flag |= BONE_TIPSEL; + break; + } + case SEL_OP_SUB: { + /* Deselect this bone. */ + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* Only deselect parent tip if it is not selected. */ + if (!(ebone->parent->flag & BONE_SELECTED)) { + ebone->parent->flag &= ~BONE_TIPSEL; + } + break; + } + case SEL_OP_XOR: { + /* Toggle inverts this bone's selection. */ + if (ebone->flag & BONE_SELECTED) { + /* Deselect this bone. */ + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* Only deselect parent tip if it is not selected. */ + if (!(ebone->parent->flag & BONE_SELECTED)) { + ebone->parent->flag &= ~BONE_TIPSEL; + } + } + else { + /* Select this bone. */ + ebone->flag |= BONE_TIPSEL; + ebone->parent->flag |= BONE_TIPSEL; + } + break; + } + case SEL_OP_SET: { + /* Select this bone. */ + ebone->flag |= BONE_TIPSEL; + ebone->parent->flag |= BONE_TIPSEL; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } - } - else { - /* Select this bone. */ - ebone->flag |= BONE_TIPSEL; - ebone->parent->flag |= BONE_TIPSEL; } } else { - /* Select this bone. */ - ebone->flag |= BONE_TIPSEL; - ebone->parent->flag |= BONE_TIPSEL; + switch (params->sel_op) { + case SEL_OP_ADD: { + ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + break; + } + case SEL_OP_SUB: { + ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + break; + } + case SEL_OP_XOR: { + /* Toggle inverts this bone's selection. */ + if (ebone->flag & BONE_SELECTED) { + ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + } + else { + ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } + break; + } + case SEL_OP_SET: { + ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; + } + } } } else { - if (extend) { - ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - else if (deselect) { - ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); - } - else if (toggle) { - /* Toggle inverts this bone's selection. */ - if (ebone->flag & BONE_SELECTED) { - ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + switch (params->sel_op) { + case SEL_OP_ADD: { + ebone->flag |= selmask; + break; } - else { - ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + case SEL_OP_SUB: { + ebone->flag &= ~selmask; + break; + } + case SEL_OP_XOR: { + if (ebone->flag & selmask) { + ebone->flag &= ~selmask; + } + else { + ebone->flag |= selmask; + } + break; + } + case SEL_OP_SET: { + ebone->flag |= selmask; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } - } - else { - ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); } } - } - else { - if (extend) { - ebone->flag |= selmask; - } - else if (deselect) { - ebone->flag &= ~selmask; - } - else if (toggle && (ebone->flag & selmask)) { - ebone->flag &= ~selmask; - } - else { - ebone->flag |= selmask; + + ED_armature_edit_sync_selection(arm->edbo); + + /* Then now check for active status. */ + if (ED_armature_ebone_selectflag_get(ebone)) { + arm->act_edbone = ebone; } - } - ED_armature_edit_sync_selection(arm->edbo); + if (view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } - /* Then now check for active status. */ - if (ED_armature_ebone_selectflag_get(ebone)) { - arm->act_edbone = ebone; + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + changed = true; } - if (view_layer->basact != basact) { - ED_object_base_activate(C, basact); + if (changed) { + ED_outliner_select_sync_from_edit_bone_tag(C); } - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - return true; + return changed || found; } -bool ED_armature_edit_select_pick( - bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +bool ED_armature_edit_select_pick(bContext *C, + const int mval[2], + const struct SelectPick_Params *params) + { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; @@ -1084,7 +1137,7 @@ bool ED_armature_edit_select_pick( vc.mval[1] = mval[1]; nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask); - return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, extend, deselect, toggle); + return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, params); } /** \} */ diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 12238280b06..8790a10f3e5 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -121,43 +121,27 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) } } -void ED_armature_pose_select_pick_bone(ViewLayer *view_layer, +bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer, View3D *v3d, Object *ob, Bone *bone, - const bool extend, - const bool deselect, - const bool toggle) + const struct SelectPick_Params *params) { - if (!ob || !ob->pose) { - return; - } - - Object *ob_act = OBACT(view_layer); - BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL); - - /* If the bone cannot be affected, don't do anything. */ - if (bone == NULL || (bone->flag & BONE_UNSELECTABLE)) { - return; - } - bArmature *arm = ob->data; + bool found = false; + bool changed = false; - /* Since we do unified select, we don't shift+select a bone if the - * armature object was not active yet. - * NOTE(campbell): special exception for armature mode so we can do multi-select - * we could check for multi-select explicitly but think its fine to - * always give predictable behavior in weight paint mode. */ - if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) { - /* When we are entering into posemode via toggle-select, - * from another active object - always select the bone. */ - if (!extend && !deselect && toggle) { - /* Re-select the bone again later in this function. */ - bone->flag &= ~BONE_SELECTED; + if (ob || ob->pose) { + if (bone && ((bone->flag & BONE_UNSELECTABLE) == 0)) { + found = true; } } - if (!extend && !deselect && !toggle) { - { + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (bone->flag & BONE_SELECTED)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ /* Don't use 'BKE_object_pose_base_array_get_unique' * because we may be selecting from object mode. */ FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) { @@ -169,56 +153,94 @@ void ED_armature_pose_select_pick_bone(ViewLayer *view_layer, } } FOREACH_VISIBLE_BASE_END; + changed = true; } - bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = bone; } - else { - if (extend) { - bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = bone; - } - else if (deselect) { - bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + + if (found) { + Object *ob_act = OBACT(view_layer); + BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL); + + /* If the bone cannot be affected, don't do anything. */ + bArmature *arm = ob->data; + + /* Since we do unified select, we don't shift+select a bone if the + * armature object was not active yet. + * NOTE(campbell): special exception for armature mode so we can do multi-select + * we could check for multi-select explicitly but think its fine to + * always give predictable behavior in weight paint mode. */ + if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) { + /* When we are entering into posemode via toggle-select, + * from another active object - always select the bone. */ + if (params->sel_op == SEL_OP_SET) { + /* Re-select the bone again later in this function. */ + bone->flag &= ~BONE_SELECTED; + } } - else if (toggle) { - if (bone->flag & BONE_SELECTED) { - /* If not active, we make it active. */ - if (bone != arm->act_bone) { - arm->act_bone = bone; + + switch (params->sel_op) { + case SEL_OP_ADD: { + bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = bone; + break; + } + case SEL_OP_SUB: { + bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + break; + } + case SEL_OP_XOR: { + if (bone->flag & BONE_SELECTED) { + /* If not active, we make it active. */ + if (bone != arm->act_bone) { + arm->act_bone = bone; + } + else { + bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } } else { - bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = bone; } + break; } - else { + case SEL_OP_SET: { bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); arm->act_bone = bone; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } } - } - if (ob_act) { - /* In weightpaint we select the associated vertex group too. */ - if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) { - if (bone == arm->act_bone) { - ED_vgroup_select_by_name(ob_act, bone->name); - DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY); + if (ob_act) { + /* In weightpaint we select the associated vertex group too. */ + if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) { + if (bone == arm->act_bone) { + ED_vgroup_select_by_name(ob_act, bone->name); + DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY); + } } - } - /* If there are some dependencies for visualizing armature state - * (e.g. Mask Modifier in 'Armature' mode), force update. - */ - else if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* NOTE: ob not ob_act here is intentional - it's the source of the - * bones being selected [T37247] + /* If there are some dependencies for visualizing armature state + * (e.g. Mask Modifier in 'Armature' mode), force update. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + else if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* NOTE: ob not ob_act here is intentional - it's the source of the + * bones being selected [T37247] + */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + + /* Tag armature for copy-on-write update (since act_bone is in armature not object). */ + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); } - /* Tag armature for copy-on-write update (since act_bone is in armature not object). */ - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + changed = true; } + + return changed || found; } bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, @@ -226,9 +248,7 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, Base *base, const struct GPUSelectResult *buffer, const short hits, - bool extend, - bool deselect, - bool toggle, + const struct SelectPick_Params *params, bool do_nearest) { Object *ob = base->object; @@ -243,9 +263,7 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, nearBone = ED_armature_pick_bone_from_selectbuffer( &base, 1, buffer, hits, 1, do_nearest, &base_dummy); - ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, extend, deselect, toggle); - - return nearBone != NULL; + return ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, params); } void ED_armature_pose_select_in_wpaint_mode(ViewLayer *view_layer, Base *base_select) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index fb349b78d71..cf04cdba859 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -896,7 +896,7 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) strcpy(mode_str, TIP_("Breakdown")); break; case POSESLIDE_BLEND: - strcpy(mode_str, TIP_("Blend To Neighbor")); + strcpy(mode_str, TIP_("Blend to Neighbor")); break; default: @@ -1722,7 +1722,7 @@ static int pose_slide_blend_to_neighbors_exec(bContext *C, wmOperator *op) void POSE_OT_blend_to_neighbors(wmOperatorType *ot) { /* Identifiers. */ - ot->name = "Blend To Neighbor"; + ot->name = "Blend to Neighbor"; ot->idname = "POSE_OT_blend_to_neighbor"; ot->description = "Blend from current position to previous or next keyframe"; diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 90d4ef60598..f0b0218d7e0 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -1100,7 +1100,7 @@ static void pchan_clear_rot(bPoseChannel *pchan) copy_v3_v3(pchan->eul, eul); } } - } /* Duplicated in source/blender/editors/object/object_transform.c */ + } /* Duplicated in source/blender/editors/object/object_transform.cc */ else { if (pchan->rotmode == ROT_MODE_QUAT) { unit_qt(pchan->quat); diff --git a/source/blender/editors/asset/intern/asset_indexer.cc b/source/blender/editors/asset/intern/asset_indexer.cc index 49c4002eee8..3cc3638c299 100644 --- a/source/blender/editors/asset/intern/asset_indexer.cc +++ b/source/blender/editors/asset/intern/asset_indexer.cc @@ -39,7 +39,6 @@ using namespace blender::bke; using namespace blender::bke::idprop; /** - * \file asset_indexer.cc * \brief Indexer for asset libraries. * * Indexes are stored per input file. Each index can contain zero to multiple asset entries. @@ -494,15 +493,15 @@ struct AssetLibraryIndex { return; } struct direntry *dir_entries = nullptr; - int num_entries = BLI_filelist_dir_contents(index_path, &dir_entries); - for (int i = 0; i < num_entries; i++) { + const int dir_entries_num = BLI_filelist_dir_contents(index_path, &dir_entries); + for (int i = 0; i < dir_entries_num; i++) { struct direntry *entry = &dir_entries[i]; if (BLI_str_endswith(entry->relname, ".index.json")) { unused_file_indices.add_as(std::string(entry->path)); } } - BLI_filelist_free(dir_entries, num_entries); + BLI_filelist_free(dir_entries, dir_entries_num); } void mark_as_used(const std::string &filename) diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h index c3ea36fcda5..8bfabe23ea1 100644 --- a/source/blender/editors/curve/curve_intern.h +++ b/source/blender/editors/curve/curve_intern.h @@ -181,6 +181,7 @@ void SURFACE_OT_primitive_nurbs_surface_sphere_add(struct wmOperatorType *ot); void SURFACE_OT_primitive_nurbs_surface_torus_add(struct wmOperatorType *ot); /* editcurve_query.c */ + bool ED_curve_pick_vert(struct ViewContext *vc, short sel, struct Nurb **r_nurb, diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 2dcddd01670..5ff63e767f6 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -44,6 +44,7 @@ #include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_transform.h" #include "ED_transform_snap_object_context.h" #include "ED_types.h" @@ -4722,8 +4723,9 @@ void CURVE_OT_make_segment(wmOperatorType *ot) /** \name Pick Select from 3D View * \{ */ -bool ED_curve_editnurb_select_pick( - bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +bool ED_curve_editnurb_select_pick(bContext *C, + const int mval[2], + const struct SelectPick_Params *params) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; @@ -4732,18 +4734,21 @@ bool ED_curve_editnurb_select_pick( BPoint *bp = NULL; Base *basact = NULL; short hand; + bool changed = false; view3d_operator_needs_opengl(C); ED_view3d_viewcontext_init(C, &vc, depsgraph); copy_v2_v2_int(vc.mval, mval); - if (ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact)) { - Object *obedit = basact->object; - Curve *cu = obedit->data; - ListBase *editnurb = object_editcurve_get(obedit); - const void *vert = BKE_curve_vert_active_get(cu); + bool found = ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact); - if (!extend && !deselect && !toggle) { + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && + (((bezt ? (&bezt->f1)[hand] : bp->f1) & SELECT) != 0)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( vc.view_layer, vc.v3d, &objects_len); @@ -4756,105 +4761,123 @@ bool ED_curve_editnurb_select_pick( WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); } MEM_freeN(objects); + changed = true; } + } - if (extend) { - if (bezt) { - if (hand == 1) { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); - } - else { - if (hand == 0) { - bezt->f1 |= SELECT; + if (found) { + Object *obedit = basact->object; + Curve *cu = obedit->data; + ListBase *editnurb = object_editcurve_get(obedit); + const void *vert = BKE_curve_vert_active_get(cu); + + switch (params->sel_op) { + case SEL_OP_ADD: { + if (bezt) { + if (hand == 1) { + select_beztriple(bezt, SELECT, SELECT, HIDDEN); } else { - bezt->f3 |= SELECT; - } - } - BKE_curve_nurb_vert_active_set(cu, nu, bezt); - } - else { - select_bpoint(bp, SELECT, SELECT, HIDDEN); - BKE_curve_nurb_vert_active_set(cu, nu, bp); - } - } - else if (deselect) { - if (bezt) { - if (hand == 1) { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); - if (bezt == vert) { - cu->actvert = CU_ACT_NONE; + if (hand == 0) { + bezt->f1 |= SELECT; + } + else { + bezt->f3 |= SELECT; + } } - } - else if (hand == 0) { - bezt->f1 &= ~SELECT; + BKE_curve_nurb_vert_active_set(cu, nu, bezt); } else { - bezt->f3 &= ~SELECT; - } - } - else { - select_bpoint(bp, DESELECT, SELECT, HIDDEN); - if (bp == vert) { - cu->actvert = CU_ACT_NONE; + select_bpoint(bp, SELECT, SELECT, HIDDEN); + BKE_curve_nurb_vert_active_set(cu, nu, bp); } + break; } - } - else if (toggle) { - if (bezt) { - if (hand == 1) { - if (bezt->f2 & SELECT) { + case SEL_OP_SUB: { + if (bezt) { + if (hand == 1) { select_beztriple(bezt, DESELECT, SELECT, HIDDEN); if (bezt == vert) { cu->actvert = CU_ACT_NONE; } } + else if (hand == 0) { + bezt->f1 &= ~SELECT; + } else { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); - BKE_curve_nurb_vert_active_set(cu, nu, bezt); + bezt->f3 &= ~SELECT; } } - else if (hand == 0) { - bezt->f1 ^= SELECT; - } else { - bezt->f3 ^= SELECT; - } - } - else { - if (bp->f1 & SELECT) { select_bpoint(bp, DESELECT, SELECT, HIDDEN); if (bp == vert) { cu->actvert = CU_ACT_NONE; } } + break; + } + case SEL_OP_XOR: { + if (bezt) { + if (hand == 1) { + if (bezt->f2 & SELECT) { + select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + if (bezt == vert) { + cu->actvert = CU_ACT_NONE; + } + } + else { + select_beztriple(bezt, SELECT, SELECT, HIDDEN); + BKE_curve_nurb_vert_active_set(cu, nu, bezt); + } + } + else if (hand == 0) { + bezt->f1 ^= SELECT; + } + else { + bezt->f3 ^= SELECT; + } + } else { - select_bpoint(bp, SELECT, SELECT, HIDDEN); - BKE_curve_nurb_vert_active_set(cu, nu, bp); + if (bp->f1 & SELECT) { + select_bpoint(bp, DESELECT, SELECT, HIDDEN); + if (bp == vert) { + cu->actvert = CU_ACT_NONE; + } + } + else { + select_bpoint(bp, SELECT, SELECT, HIDDEN); + BKE_curve_nurb_vert_active_set(cu, nu, bp); + } } + break; } - } - else { - BKE_nurbList_flag_set(editnurb, SELECT, false); + case SEL_OP_SET: { + BKE_nurbList_flag_set(editnurb, SELECT, false); - if (bezt) { + if (bezt) { - if (hand == 1) { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); - } - else { - if (hand == 0) { - bezt->f1 |= SELECT; + if (hand == 1) { + select_beztriple(bezt, SELECT, SELECT, HIDDEN); } else { - bezt->f3 |= SELECT; + if (hand == 0) { + bezt->f1 |= SELECT; + } + else { + bezt->f3 |= SELECT; + } } + BKE_curve_nurb_vert_active_set(cu, nu, bezt); + } + else { + select_bpoint(bp, SELECT, SELECT, HIDDEN); + BKE_curve_nurb_vert_active_set(cu, nu, bp); } - BKE_curve_nurb_vert_active_set(cu, nu, bezt); + break; } - else { - select_bpoint(bp, SELECT, SELECT, HIDDEN); - BKE_curve_nurb_vert_active_set(cu, nu, bp); + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } } @@ -4876,10 +4899,10 @@ bool ED_curve_editnurb_select_pick( DEG_id_tag_update(obedit->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - return true; + changed = true; } - return false; + return changed || found; } /** \} */ diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 02c7f3856e8..611dbb2e80c 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -526,16 +526,16 @@ static bool font_paste_utf8(bContext *C, const char *str, const size_t str_len) /** \name Paste From File Operator * \{ */ -static int paste_from_file(bContext *C, ReportList *reports, const char *filename) +static int paste_from_file(bContext *C, ReportList *reports, const char *filepath) { Object *obedit = CTX_data_edit_object(C); char *strp; size_t filelen; int retval; - strp = BLI_file_read_text_as_mem(filename, 1, &filelen); + strp = BLI_file_read_text_as_mem(filepath, 1, &filelen); if (strp == NULL) { - BKE_reportf(reports, RPT_ERROR, "Failed to open file '%s'", filename); + BKE_reportf(reports, RPT_ERROR, "Failed to open file '%s'", filepath); return OPERATOR_CANCELLED; } strp[filelen] = 0; @@ -545,7 +545,7 @@ static int paste_from_file(bContext *C, ReportList *reports, const char *filenam retval = OPERATOR_FINISHED; } else { - BKE_reportf(reports, RPT_ERROR, "File too long %s", filename); + BKE_reportf(reports, RPT_ERROR, "File too long %s", filepath); retval = OPERATOR_CANCELLED; } @@ -556,12 +556,12 @@ static int paste_from_file(bContext *C, ReportList *reports, const char *filenam static int paste_from_file_exec(bContext *C, wmOperator *op) { - char *path; + char *filepath; int retval; - path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0, NULL); - retval = paste_from_file(C, op->reports, path); - MEM_freeN(path); + filepath = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0, NULL); + retval = paste_from_file(C, op->reports, filepath); + MEM_freeN(filepath); return retval; } @@ -2091,7 +2091,7 @@ static int font_open_exec(bContext *C, wmOperator *op) static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { VFont *vfont = NULL; - const char *path; + const char *filepath; PointerRNA idptr; PropertyPointerRNA *pprop; @@ -2106,13 +2106,13 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) vfont = (VFont *)idptr.owner_id; } - path = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->filepath : U.fontdir; + filepath = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->filepath : U.fontdir; if (RNA_struct_property_is_set(op->ptr, "filepath")) { return font_open_exec(C, op); } - RNA_string_set(op->ptr, "filepath", path); + RNA_string_set(op->ptr, "filepath", filepath); WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; @@ -2184,7 +2184,10 @@ void FONT_OT_unlink(wmOperatorType *ot) } bool ED_curve_editfont_select_pick( - bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) + bContext *C, + const int mval[2], + /* NOTE: `params->deselect_all` is ignored as only one text-box is active at once. */ + const struct SelectPick_Params *params) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *obedit = CTX_data_edit_object(C); @@ -2203,9 +2206,7 @@ bool ED_curve_editfont_select_pick( ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); /* currently only select active */ - (void)extend; - (void)deselect; - (void)toggle; + (void)params; for (i_iter = 0; i_iter < cu->totbox; i_iter++) { int i = (i_iter + i_actbox) % cu->totbox; @@ -2257,6 +2258,8 @@ bool ED_curve_editfont_select_pick( if (cu->actbox != actbox_select) { cu->actbox = actbox_select; WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + /* TODO: support #ID_RECALC_SELECT. */ + DEG_id_tag_update(obedit->data, ID_RECALC_COPY_ON_WRITE); } return true; } diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 9cde23451dc..17108619a4d 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -21,7 +21,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi float *radius_data = (float *)CustomData_add_layer_named( &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); - MutableSpan<float> radii{radius_data, curves.points_size()}; + MutableSpan<float> radii{radius_data, curves.points_num()}; for (const int i : offsets.index_range()) { offsets[i] = points_per_curve * i; @@ -30,7 +30,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi RandomNumberGenerator rng; for (const int i : curves.curves_range()) { - const IndexRange curve_range = curves.range_for_curve(i); + const IndexRange curve_range = curves.points_for_curve(i); MutableSpan<float3> curve_positions = positions.slice(curve_range); MutableSpan<float> curve_radii = radii.slice(curve_range); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index edbdc1ee96f..d58bbec01cd 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -595,9 +595,9 @@ set(ICON_NAMES axis_top layer_used layer_active - outliner_ob_hair - outliner_data_hair - hair_data + outliner_ob_curves + outliner_data_curves + curves_data outliner_ob_pointcloud outliner_data_pointcloud pointcloud_data @@ -652,7 +652,7 @@ set(ICON_NAMES matsphere matcube monkey - hair + curves aliased antialiased mat_sphere_sky @@ -770,6 +770,11 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curve.extrude_move ops.curve.radius ops.curve.vertex_random + ops.curves.sculpt_add + ops.curves.sculpt_comb + ops.curves.sculpt_cut + ops.curves.sculpt_delete + ops.curves.sculpt_grow ops.generic.cursor ops.generic.select ops.generic.select_box diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 77cd5d9221f..6225a68f53c 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -36,10 +36,6 @@ namespace blender::ed::geometry { -using fn::CPPType; -using fn::GArray; -using fn::GVArray; - /*********************** Attribute Operators ************************/ static bool geometry_attributes_poll(bContext *C) diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index d4518f21586..4f9468cc9c4 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -207,7 +207,7 @@ typedef struct tGpTimingData { int seed; /* Data set from points, used to compute final timing FCurve */ - int num_points, cur_point; + int points_num, cur_point; /* Distances */ float *dists; @@ -229,29 +229,29 @@ typedef struct tGpTimingData { /* Init point buffers for timing data. * Note this assumes we only grow those arrays! */ -static void gpencil_timing_data_set_nbr(tGpTimingData *gtd, const int nbr) +static void gpencil_timing_data_set_num(tGpTimingData *gtd, const int num) { float *tmp; - BLI_assert(nbr > gtd->num_points); + BLI_assert(num > gtd->points_num); /* distances */ tmp = gtd->dists; - gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__); + gtd->dists = MEM_callocN(sizeof(float) * num, __func__); if (tmp) { - memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points); + memcpy(gtd->dists, tmp, sizeof(float) * gtd->points_num); MEM_freeN(tmp); } /* times */ tmp = gtd->times; - gtd->times = MEM_callocN(sizeof(float) * nbr, __func__); + gtd->times = MEM_callocN(sizeof(float) * num, __func__); if (tmp) { - memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points); + memcpy(gtd->times, tmp, sizeof(float) * gtd->points_num); MEM_freeN(tmp); } - gtd->num_points = nbr; + gtd->points_num = num; } /* add stroke point to timing buffers */ @@ -297,15 +297,15 @@ static void gpencil_timing_data_add_point(tGpTimingData *gtd, static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd, RNG *rng, const int idx, - const int nbr_gaps, - int *nbr_done_gaps, + const int gaps_count, + int *gaps_done_count, const float tot_gaps_time, const float delta_time, float *next_delta_time) { int j; - for (j = idx + 1; j < gtd->num_points; j++) { + for (j = idx + 1; j < gtd->points_num; j++) { if (gtd->times[j] < 0) { gtd->times[j] = -gtd->times[j]; if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { @@ -316,7 +316,7 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd, /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range, * and which sum to exactly tot_gaps_time... */ - int rem_gaps = nbr_gaps - (*nbr_done_gaps); + int rem_gaps = gaps_count - (*gaps_done_count); if (rem_gaps < 2) { /* Last gap, just give remaining time! */ *next_delta_time = tot_gaps_time; @@ -327,7 +327,7 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd, /* This code ensures that if the first gaps * have been shorter than average gap_duration, next gaps * will tend to be longer (i.e. try to recover the lateness), and vice-versa! */ - delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps)); + delta = delta_time - (gtd->gap_duration * (*gaps_done_count)); /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */ min = -gtd->gap_randomness - delta; @@ -343,7 +343,7 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd, *next_delta_time += gtd->gap_duration; } } - (*nbr_done_gaps)++; + (*gaps_done_count)++; break; } } @@ -353,14 +353,14 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd, static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, RNG *rng, - int *nbr_gaps, + int *gaps_count, float *r_tot_gaps_time) { float delta_time = 0.0f; - for (int i = 0; i < gtd->num_points; i++) { + for (int i = 0; i < gtd->points_num; i++) { if (gtd->times[i] < 0 && i) { - (*nbr_gaps)++; + (*gaps_count)++; gtd->times[i] = -gtd->times[i] - delta_time; delta_time += gtd->times[i] - gtd->times[i - 1]; gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */ @@ -371,7 +371,7 @@ static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, } gtd->tot_time -= delta_time; - *r_tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; + *r_tot_gaps_time = (float)(*gaps_count) * gtd->gap_duration; gtd->tot_time += *r_tot_gaps_time; if (gtd->gap_randomness > 0.0f) { BLI_rng_srandom(rng, gtd->seed); @@ -387,7 +387,7 @@ static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports, tGpTimingData *gtd, RNG *rng, const float time_range, - const int nbr_gaps, + const int gaps_count, const float tot_gaps_time) { /* Use actual recorded timing! */ @@ -399,20 +399,20 @@ static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports, /* CustomGaps specific */ float delta_time = 0.0f, next_delta_time = 0.0f; - int nbr_done_gaps = 0; + int gaps_done_count = 0; /* This is a bit tricky, as: * - We can't add arbitrarily close points on FCurve (in time). * - We *must* have all "caps" points of all strokes in FCurve, as much as possible! */ - for (int i = 0; i < gtd->num_points; i++) { + for (int i = 0; i < gtd->points_num; i++) { /* If new stroke... */ if (i > end_stroke_idx) { start_stroke_idx = i; delta_time = next_delta_time; /* find end of that new stroke */ end_stroke_idx = gpencil_find_end_of_stroke_idx( - gtd, rng, i, nbr_gaps, &nbr_done_gaps, tot_gaps_time, delta_time, &next_delta_time); + gtd, rng, i, gaps_count, &gaps_done_count, tot_gaps_time, delta_time, &next_delta_time); /* This one should *never* be negative! */ end_stroke_time = time_start + ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range); @@ -502,7 +502,7 @@ static void gpencil_stroke_path_animation(bContext *C, FCurve *fcu; PointerRNA ptr; PropertyRNA *prop = NULL; - int nbr_gaps = 0; + int gaps_count = 0; if (gtd->mode == GP_STROKECONVERT_TIMING_NONE) { return; @@ -571,7 +571,7 @@ static void gpencil_stroke_path_animation(bContext *C, /* Pre-process gaps, in case we don't want to keep their original timing */ if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { - gpencil_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time); + gpencil_stroke_path_animation_preprocess_gaps(gtd, rng, &gaps_count, &tot_gaps_time); } if (gtd->realtime) { @@ -582,7 +582,7 @@ static void gpencil_stroke_path_animation(bContext *C, } gpencil_stroke_path_animation_add_keyframes( - reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time); + reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, gaps_count, tot_gaps_time); BLI_rng_free(rng); } @@ -684,7 +684,7 @@ static void gpencil_stroke_to_path(bContext *C, } if (do_gtd) { - gpencil_timing_data_set_nbr(gtd, nu->pntsu); + gpencil_timing_data_set_num(gtd, nu->pntsu); } /* If needed, make the link between both strokes with two zero-radius additional points */ @@ -929,7 +929,7 @@ static void gpencil_stroke_to_bezier(bContext *C, } if (do_gtd) { - gpencil_timing_data_set_nbr(gtd, nu->pntsu); + gpencil_timing_data_set_num(gtd, nu->pntsu); } tot = gps->totpoints; @@ -1536,7 +1536,7 @@ static int gpencil_convert_layer_exec(bContext *C, wmOperator *op) gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness"); gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration); gtd.seed = RNA_int_get(op->ptr, "seed"); - gtd.num_points = gtd.cur_point = 0; + gtd.points_num = gtd.cur_point = 0; gtd.dists = gtd.times = NULL; gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f; gtd.inittime = 0.0; diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 069493025dc..45a2247c65e 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1387,6 +1387,15 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) current_check_co[1] = boundary_co[1] + offset[offset_idx][1]; int image_idx = ibuf->x * current_check_co[1] + current_check_co[0]; + /* Check if the index is inside the image. If the index is outside is + * because the algorithm is unable to find the outline of the figure. This is + * possible for negative filling when click inside a figure instead of + * clicking outside. + * If the index is out of range, finish the filling. */ + if (image_idx > imagesize - 1) { + start_found = false; + break; + } get_pixel(ibuf, image_idx, rgba); /* find next boundary pixel */ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 2f5f82e332c..84efc875be7 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -27,6 +27,7 @@ struct MeshDeformModifierData; struct Object; struct ReportList; struct Scene; +struct SelectPick_Params; struct UndoType; struct View3D; struct ViewLayer; @@ -164,18 +165,20 @@ bool ED_armature_edit_deselect_all_visible(struct Object *obedit); bool ED_armature_edit_deselect_all_multi_ex(struct Base **bases, uint bases_len); bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint bases_len); bool ED_armature_edit_deselect_all_visible_multi(struct bContext *C); +/** + * \return True when pick finds an element or the selection changed. + */ bool ED_armature_edit_select_pick_bone(struct bContext *C, struct Base *basact, struct EditBone *ebone, int selmask, - bool extend, - bool deselect, - bool toggle); + const struct SelectPick_Params *params); /** * Bone selection picking for armature edit-mode in the view3d. */ -bool ED_armature_edit_select_pick( - struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +bool ED_armature_edit_select_pick(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params); /** * Perform a selection operation on elements which have been 'touched', * use for lasso & border select but can be used elsewhere too. @@ -305,25 +308,26 @@ void ED_pose_recalculate_paths(struct bContext *C, /* pose_select.c */ -void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer, +/** + * \return True when pick finds an element or the selection changed. + */ +bool ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer, struct View3D *v3d, struct Object *ob, struct Bone *bone, - bool extend, - bool deselect, - bool toggle); + const struct SelectPick_Params *params); /** * Called for mode-less pose selection. * assumes the active object is still on old situation. + * + * \return True when pick finds an element or the selection changed. */ bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer, struct View3D *v3d, struct Base *base, const struct GPUSelectResult *buffer, short hits, - bool extend, - bool deselect, - bool toggle, + const struct SelectPick_Params *params, bool do_nearest); /** * While in weight-paint mode, a single pose may be active as well. diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h index c97f97a2ddc..6097e7c69d9 100644 --- a/source/blender/editors/include/ED_curve.h +++ b/source/blender/editors/include/ED_curve.h @@ -19,6 +19,7 @@ struct EditNurb; struct Main; struct Nurb; struct Object; +struct SelectPick_Params; struct Text; struct UndoType; struct View3D; @@ -46,8 +47,12 @@ void ED_curve_editnurb_load(struct Main *bmain, struct Object *obedit); void ED_curve_editnurb_make(struct Object *obedit); void ED_curve_editnurb_free(struct Object *obedit); -bool ED_curve_editnurb_select_pick( - struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +/** + * \return True when pick finds an element or the selection changed. + */ +bool ED_curve_editnurb_select_pick(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params); struct Nurb *ED_curve_add_nurbs_primitive( struct bContext *C, struct Object *obedit, float mat[4][4], int type, int newob); @@ -100,10 +105,13 @@ int ED_curve_updateAnimPaths(struct Main *bmain, struct Curve *cu); bool ED_curve_active_center(struct Curve *cu, float center[3]); /** - * TextBox selection + * Text box selection. + * + * \return True when pick finds an element or the selection changed. */ -bool ED_curve_editfont_select_pick( - struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +bool ED_curve_editfont_select_pick(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params); /* editfont_undo.c */ diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 90e8dbad272..3a0bb9738ae 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -382,6 +382,7 @@ void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc); bool delete_fcurve_keys(struct FCurve *fcu); void clear_fcurve_keys(struct FCurve *fcu); void duplicate_fcurve_keys(struct FCurve *fcu); +float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr); typedef struct FCurveSegment { struct FCurveSegment *next, *prev; @@ -404,6 +405,7 @@ void blend_to_neighbor_fcurve_segment(struct FCurve *fcu, float factor); void breakdown_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); bool decimate_fcurve(struct bAnimListElem *ale, float remove_ratio, float error_sq_max); +void blend_to_default_fcurve(struct PointerRNA *id_ptr, struct FCurve *fcu, float factor); /** * Use a weighted moving-means method to reduce intensity of fluctuations. */ diff --git a/source/blender/editors/include/ED_lattice.h b/source/blender/editors/include/ED_lattice.h index eddf69e1cb6..1b9311cbacf 100644 --- a/source/blender/editors/include/ED_lattice.h +++ b/source/blender/editors/include/ED_lattice.h @@ -13,6 +13,7 @@ extern "C" { struct Base; struct Object; +struct SelectPick_Params; struct UndoType; struct wmKeyConfig; @@ -24,8 +25,12 @@ void ED_keymap_lattice(struct wmKeyConfig *keyconf); /* editlattice_select.c */ bool ED_lattice_flags_set(struct Object *obedit, int flag); -bool ED_lattice_select_pick( - struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +/** + * \return True when pick finds an element or the selection changed. + */ +bool ED_lattice_select_pick(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params); bool ED_lattice_deselect_all_multi_ex(struct Base **bases, uint bases_len); bool ED_lattice_deselect_all_multi(struct bContext *C); diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h index bc01f76f20d..7039d6d9fc7 100644 --- a/source/blender/editors/include/ED_mask.h +++ b/source/blender/editors/include/ED_mask.h @@ -63,7 +63,6 @@ bool ED_mask_selected_minmax(const struct bContext *C, /* mask_draw.c */ -void ED_mask_draw(const struct bContext *C, char draw_flag, char draw_type); /** * Sets up the opengl context. * width, height are to match the values from #ED_mask_get_size(). diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h index e0c921ea0db..7f2c4dff311 100644 --- a/source/blender/editors/include/ED_mball.h +++ b/source/blender/editors/include/ED_mball.h @@ -12,7 +12,9 @@ extern "C" { #endif struct Base; +struct MetaElem; struct Object; +struct SelectPick_Params; struct UndoType; struct bContext; struct wmKeyConfig; @@ -31,11 +33,19 @@ struct MetaElem *ED_mball_add_primitive(struct bContext *C, float dia, int type); +struct Base *ED_mball_base_and_elem_from_select_buffer(struct Base **bases, + uint bases_len, + const uint select_id, + struct MetaElem **r_ml); + /** - * Select MetaElement with mouse click (user can select radius circle or stiffness circle). + * Select meta-element with mouse click (user can select radius circle or stiffness circle). + * + * \return True when pick finds an element or the selection changed. */ -bool ED_mball_select_pick( - struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +bool ED_mball_select_pick(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params); bool ED_mball_deselect_all_multi_ex(struct Base **bases, uint bases_len); bool ED_mball_deselect_all_multi(struct bContext *C); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 68e46dfa0e5..03ca4cd062b 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -31,6 +31,7 @@ struct Mesh; struct Object; struct ReportList; struct Scene; +struct SelectPick_Params; struct UndoType; struct UvMapVert; struct UvVertMap; @@ -268,8 +269,9 @@ bool EDBM_unified_findnearest_from_raycast(struct ViewContext *vc, struct BMEdge **r_eed, struct BMFace **r_efa); -bool EDBM_select_pick( - struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +bool EDBM_select_pick(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params); /** * When switching select mode, makes sure selection is consistent for editing @@ -387,12 +389,13 @@ void ED_keymap_mesh(struct wmKeyConfig *keyconf); * use in object mode when selecting faces (while painting). */ void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag); +/** + * \return True when pick finds an element or the selection changed. + */ bool paintface_mouse_select(struct bContext *C, - struct Object *ob, const int mval[2], - bool extend, - bool deselect, - bool toggle); + const struct SelectPick_Params *params, + struct Object *ob); bool paintface_deselect_all_visible(struct bContext *C, struct Object *ob, int action, diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h index a4797ff167c..553aa444891 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -16,6 +16,7 @@ struct PTCacheEdit; struct ParticleEditSettings; struct ParticleSystem; struct Scene; +struct SelectPick_Params; struct UndoType; struct ViewLayer; struct bContext; @@ -54,8 +55,9 @@ void PE_update_object(struct Depsgraph *depsgraph, /* selection tools */ -bool PE_mouse_particles( - struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +bool PE_mouse_particles(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params); bool PE_box_select(struct bContext *C, const struct rcti *rect, int sel_op); bool PE_circle_select(struct bContext *C, struct wmGenericUserData *wm_userdata, diff --git a/source/blender/editors/include/ED_select_utils.h b/source/blender/editors/include/ED_select_utils.h index 26d8d0a3d8c..b9ef5c283aa 100644 --- a/source/blender/editors/include/ED_select_utils.h +++ b/source/blender/editors/include/ED_select_utils.h @@ -70,6 +70,31 @@ bool ED_select_similar_compare_float_tree(const struct KDTree_1d *tree, */ eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first); +/** Argument passed to picking functions. */ +struct SelectPick_Params { + /** + * - #SEL_OP_ADD named "extend" from operators. + * - #SEL_OP_SUB named "deselect" from operators. + * - #SEL_OP_XOR named "toggle" from operators. + * - #SEL_OP_AND (never used for picking). + * - #SEL_OP_SET use when "extend", "deselect" and "toggle" are all disabled. + */ + eSelectOp sel_op; + /** Deselect all, even when there is nothing found at the cursor location. */ + bool deselect_all; + /** + * When selecting an element that is already selected, do nothing (passthrough). + * don't even make it active. + * Use to implement tweaking to move the selection without first de-selecting. + */ + bool select_passthrough; +}; + +/** + * Utility to get #eSelectPickMode from booleans for convenience. + */ +eSelectOp ED_select_op_from_booleans(bool extend, bool deselect, bool toggle); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index d28700c64ee..d8415064d2f 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -157,6 +157,8 @@ enum { UI_BLOCK_POPOVER_ONCE = 1 << 22, /** Always show key-maps, even for non-menus. */ UI_BLOCK_SHOW_SHORTCUT_ALWAYS = 1 << 23, + /** Don't show library override state for buttons in this block. */ + UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE = 1 << 24, /** The block is only used during the search process and will not be drawn. * Currently just for the case of a closed panel's sub-panel (and its sub-panels). */ UI_BLOCK_SEARCH_ONLY = 1 << 25, @@ -1610,6 +1612,14 @@ uiBut *uiDefAutoButR(uiBlock *block, int y, int width, int height); +void uiDefAutoButsArrayR(uiBlock *block, + PointerRNA *ptr, + PropertyRNA *prop, + const int icon, + const int x, + const int y, + const int tot_width, + const int height); /** * \a check_prop callback filters functions to avoid drawing certain properties, * in cases where PROP_HIDDEN flag can't be used for a property. diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 3619a7ce317..e83a8761e12 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -4238,28 +4238,28 @@ static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *bu int totitems = 0; int categories = 0; - int nbr_entries_nosepr = 0; + int entries_nosepr_count = 0; for (const EnumPropertyItem *item = item_array; item->identifier; item++, totitems++) { if (!item->identifier[0]) { /* inconsistent, but menus with categories do not look good flipped */ if (item->name) { block->flag |= UI_BLOCK_NO_FLIP; categories++; - nbr_entries_nosepr++; + entries_nosepr_count++; } - /* We do not want simple separators in nbr_entries_nosepr count */ + /* We do not want simple separators in `entries_nosepr_count`. */ continue; } - nbr_entries_nosepr++; + entries_nosepr_count++; } /* Columns and row estimation. Ignore simple separators here. */ - int columns = (nbr_entries_nosepr + 20) / 20; + int columns = (entries_nosepr_count + 20) / 20; if (columns < 1) { columns = 1; } if (columns > 8) { - columns = (nbr_entries_nosepr + 25) / 25; + columns = (entries_nosepr_count + 25) / 25; } int rows = totitems / columns; diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index decd8c03d70..c02024bc82d 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -546,13 +546,13 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), #undef HISTOGRAM_TOT_GRID_LINES -static void waveform_draw_one(float *waveform, int nbr, const float col[3]) +static void waveform_draw_one(float *waveform, int waveform_num, const float col[3]) { GPUVertFormat format = {0}; const uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(vbo, nbr); + GPU_vertbuf_data_alloc(vbo, waveform_num); GPU_vertbuf_attr_fill(vbo, pos_id, waveform); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 8677b1ed78a..8935df7b581 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -7969,7 +7969,16 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } if (but->flag & UI_BUT_DISABLED) { - return WM_UI_HANDLER_BREAK; + /* It's important to continue here instead of breaking since breaking causes the event to be + * considered "handled", preventing further click/drag events from being generated. + * + * An example of where this is needed is dragging node-sockets, where dragging a node-socket + * could exit the button before the drag threshold was reached, disable the button then break + * handling of the #MOUSEMOVE event preventing the socket being dragged entirely, see: T96255. + * + * Region level event handling is responsible for preventing events being passed + * through to parts of the UI that are logically behind this button, see: T92364. */ + return WM_UI_HANDLER_CONTINUE; } switch (but->type) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 694ae78ca9e..a7a2409ef17 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1275,7 +1275,8 @@ void ui_layout_remove_but(uiLayout *layout, const uiBut *but); */ bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but); /** - * \note May reallocate \a but, so the possibly new address is returned. + * \note May reallocate \a but, so the possibly new address is returned. May also override the + * #UI_BUT_DISABLED flag depending on if a search pointer-property pair was provided/found. */ uiBut *ui_but_add_search(uiBut *but, PointerRNA *ptr, diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 17f0ae1f2d4..cbc21bd481f 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -2751,6 +2751,10 @@ uiBut *ui_but_add_search( ui_rna_collection_search_arg_free_fn, NULL, NULL); + /* If this is called multiple times for the same button, an earlier call may have taken the + * else branch below so the button was disabled. Now we have a searchprop, so it can be enabled + * again. */ + but->flag &= ~UI_BUT_DISABLED; } else if (but->type == UI_BTYPE_SEARCH_MENU) { /* In case we fail to find proper searchprop, diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 54bd1a2cebb..0f4f0ef48ff 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1976,6 +1976,9 @@ static void UI_OT_drop_name(wmOperatorType *ot) static bool ui_list_focused_poll(bContext *C) { const ARegion *region = CTX_wm_region(C); + if (!region) { + return false; + } const wmWindow *win = CTX_wm_window(C); const uiList *list = UI_list_find_mouse_over(region, win->eventstate); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index ad7c71e3be9..0e417fda911 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6261,16 +6261,13 @@ void uiTemplateColormanagedViewSettings(uiLayout *layout, ColorManagedViewSettings *view_settings = view_transform_ptr.data; uiLayout *col = uiLayoutColumn(layout, false); - - uiLayout *row = uiLayoutRow(col, false); - uiItemR(row, &view_transform_ptr, "view_transform", 0, IFACE_("View"), ICON_NONE); + uiItemR(col, &view_transform_ptr, "view_transform", 0, IFACE_("View"), ICON_NONE); + uiItemR(col, &view_transform_ptr, "look", 0, IFACE_("Look"), ICON_NONE); col = uiLayoutColumn(layout, false); uiItemR(col, &view_transform_ptr, "exposure", 0, NULL, ICON_NONE); uiItemR(col, &view_transform_ptr, "gamma", 0, NULL, ICON_NONE); - uiItemR(col, &view_transform_ptr, "look", 0, IFACE_("Look"), ICON_NONE); - col = uiLayoutColumn(layout, false); uiItemR(col, &view_transform_ptr, "use_curve_mapping", 0, NULL, ICON_NONE); if (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) { diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 814bd956096..728d42a9353 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -320,6 +320,7 @@ uiBut *uiDefAutoButR(uiBlock *block, -1, -1, NULL); + ui_but_add_search(but, ptr, prop, NULL, NULL); break; } case PROP_COLLECTION: { @@ -338,6 +339,29 @@ uiBut *uiDefAutoButR(uiBlock *block, return but; } +void uiDefAutoButsArrayR(uiBlock *block, + PointerRNA *ptr, + PropertyRNA *prop, + const int icon, + const int x, + const int y, + const int tot_width, + const int height) +{ + const int len = RNA_property_array_length(ptr, prop); + if (len == 0) { + return; + } + + const int item_width = tot_width / len; + + UI_block_align_begin(block); + for (int i = 0; i < len; i++) { + uiDefAutoButR(block, ptr, prop, i, "", icon, x + i * item_width, y, item_width, height); + } + UI_block_align_end(block); +} + eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout, PointerRNA *ptr, bool (*check_prop)(PointerRNA *ptr, diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 35cf952b5ce..b33cab3cbc6 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4963,6 +4963,9 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu } } #endif + if (but->block->flag & UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE) { + state &= ~UI_BUT_OVERRIDDEN; + } const float zoom = 1.0f / but->block->aspect; wt->state(wt, state, drawflag, but->emboss); diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index 1d5c9d717d0..dc62212bf53 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -221,14 +221,6 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) export_settings.limit_precision = limit_precision != 0; export_settings.keep_bind_info = keep_bind_info != 0; - int includeFilter = OB_REL_NONE; - if (export_settings.include_armatures) { - includeFilter |= OB_REL_MOD_ARMATURE; - } - if (export_settings.include_children) { - includeFilter |= OB_REL_CHILDREN_RECURSIVE; - } - export_count = collada_export(C, &export_settings); if (export_count == 0) { diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index f253f63946b..df15191916a 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -293,11 +293,8 @@ void WM_OT_obj_export(struct wmOperatorType *ot) 0.01, 1000.0f); /* File Writer options. */ - RNA_def_boolean(ot->srna, - "apply_modifiers", - true, - "Apply Modifiers", - "Apply modifiers to exported meshes"); + RNA_def_boolean( + ot->srna, "apply_modifiers", true, "Apply Modifiers", "Apply modifiers to exported meshes"); RNA_def_enum(ot->srna, "export_eval_mode", io_obj_export_evaluation_mode, @@ -345,7 +342,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "export_material_groups", false, "Export Material Groups", - "Append mesh name and material name to object name, separated by a '_'"); + "Generate an OBJ group for each part of a geometry using a different material"); RNA_def_boolean( ot->srna, "export_vertex_groups", diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c index d1635078126..ea99c6e23ff 100644 --- a/source/blender/editors/lattice/editlattice_select.c +++ b/source/blender/editors/lattice/editlattice_select.c @@ -556,15 +556,18 @@ void LATTICE_OT_select_ungrouped(wmOperatorType *ot) * Gets called via generic mouse select operator. * \{ */ -static void findnearestLattvert__doClosest(void *userData, BPoint *bp, const float screen_co[2]) +struct NearestLatticeVert_UserData { + BPoint *bp; + float dist; + /** When true, the existing selection gets a disadvantage. */ + bool select; + float mval_fl[2]; + bool is_changed; +}; + +static void findnearestLattvert__doClosest(void *user_data, BPoint *bp, const float screen_co[2]) { - struct { - BPoint *bp; - float dist; - int select; - float mval_fl[2]; - bool is_changed; - } *data = userData; + struct NearestLatticeVert_UserData *data = user_data; float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); if ((bp->f1 & SELECT) && data->select) { @@ -578,21 +581,12 @@ static void findnearestLattvert__doClosest(void *userData, BPoint *bp, const flo } } -static BPoint *findnearestLattvert(ViewContext *vc, int sel, Base **r_base) +static BPoint *findnearestLattvert(ViewContext *vc, bool select, Base **r_base) { - /* (sel == 1): selected gets a disadvantage */ - /* in nurb and bezt or bp the nearest is written */ - /* return 0 1 2: handlepunt */ - struct { - BPoint *bp; - float dist; - int select; - float mval_fl[2]; - bool is_changed; - } data = {NULL}; + struct NearestLatticeVert_UserData data = {NULL}; data.dist = ED_view3d_select_dist_px(); - data.select = sel; + data.select = select; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; @@ -616,24 +610,27 @@ static BPoint *findnearestLattvert(ViewContext *vc, int sel, Base **r_base) return data.bp; } -bool ED_lattice_select_pick( - bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +bool ED_lattice_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; BPoint *bp = NULL; Base *basact = NULL; + bool changed = false; ED_view3d_viewcontext_init(C, &vc, depsgraph); vc.mval[0] = mval[0]; vc.mval[1] = mval[1]; bp = findnearestLattvert(&vc, true, &basact); - if (bp) { - ED_view3d_viewcontext_init_object(&vc, basact->object); - Lattice *lt = ((Lattice *)vc.obedit->data)->editlatt->latt; + bool found = (bp != NULL); - if (!extend && !deselect && !toggle) { + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (bp->f1 & SELECT)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( vc.view_layer, vc.v3d, &objects_len); @@ -645,20 +642,36 @@ bool ED_lattice_select_pick( } } MEM_freeN(objects); + changed = true; } + } - if (extend) { - bp->f1 |= SELECT; - } - else if (deselect) { - bp->f1 &= ~SELECT; - } - else if (toggle) { - bp->f1 ^= SELECT; /* swap */ - } - else { - ED_lattice_flags_set(vc.obedit, 0); - bp->f1 |= SELECT; + if (found) { + ED_view3d_viewcontext_init_object(&vc, basact->object); + Lattice *lt = ((Lattice *)vc.obedit->data)->editlatt->latt; + + switch (params->sel_op) { + case SEL_OP_ADD: { + bp->f1 |= SELECT; + break; + } + case SEL_OP_SUB: { + bp->f1 &= ~SELECT; + break; + } + case SEL_OP_XOR: { + bp->f1 ^= SELECT; /* swap */ + break; + } + case SEL_OP_SET: { + ED_lattice_flags_set(vc.obedit, 0); + bp->f1 |= SELECT; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; + } } if (bp->f1 & SELECT) { @@ -675,10 +688,10 @@ bool ED_lattice_select_pick( DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - return true; + changed = true; } - return false; + return changed || found; } /** \} */ diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 8fe3b74f9c9..60232dee109 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -638,21 +638,6 @@ static void draw_mask_layers(const bContext *C, GPU_blend(GPU_BLEND_NONE); } -void ED_mask_draw(const bContext *C, const char draw_flag, const char draw_type) -{ - ScrArea *area = CTX_wm_area(C); - Mask *mask = CTX_data_edit_mask(C); - int width, height; - - if (!mask) { - return; - } - - ED_mask_get_size(area, &width, &height); - - draw_mask_layers(C, mask, draw_flag, draw_type, width, height); -} - static float *mask_rasterize(Mask *mask, const int width, const int height) { MaskRasterHandle *handle; diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index 8a1e12c76c5..a5c6adaa43e 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -366,59 +366,77 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) return ok; } -bool paintface_mouse_select( - struct bContext *C, Object *ob, const int mval[2], bool extend, bool deselect, bool toggle) +bool paintface_mouse_select(struct bContext *C, + const int mval[2], + const struct SelectPick_Params *params, + Object *ob) { Mesh *me; - MPoly *mpoly_sel; + MPoly *mpoly_sel = NULL; uint index; + bool changed = false; + bool found = false; /* Get the face under the cursor */ me = BKE_mesh_from_object(ob); - if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { - return false; - } - - if (index >= me->totpoly) { - return false; - } - - mpoly_sel = me->mpoly + index; - if (mpoly_sel->flag & ME_HIDE) { - return false; + if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { + if (index < me->totpoly) { + mpoly_sel = me->mpoly + index; + if ((mpoly_sel->flag & ME_HIDE) == 0) { + found = true; + } + } } - /* clear flags */ - if (!extend && !deselect && !toggle) { - paintface_deselect_all_visible(C, ob, SEL_DESELECT, false); + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (mpoly_sel->flag & ME_FACE_SEL)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ + changed |= paintface_deselect_all_visible(C, ob, SEL_DESELECT, false); + } } - me->act_face = (int)index; + if (found) { + me->act_face = (int)index; - if (extend) { - mpoly_sel->flag |= ME_FACE_SEL; - } - else if (deselect) { - mpoly_sel->flag &= ~ME_FACE_SEL; - } - else if (toggle) { - if (mpoly_sel->flag & ME_FACE_SEL) { - mpoly_sel->flag &= ~ME_FACE_SEL; - } - else { - mpoly_sel->flag |= ME_FACE_SEL; + switch (params->sel_op) { + case SEL_OP_ADD: { + mpoly_sel->flag |= ME_FACE_SEL; + break; + } + case SEL_OP_SUB: { + mpoly_sel->flag &= ~ME_FACE_SEL; + break; + } + case SEL_OP_XOR: { + if (mpoly_sel->flag & ME_FACE_SEL) { + mpoly_sel->flag &= ~ME_FACE_SEL; + } + else { + mpoly_sel->flag |= ME_FACE_SEL; + } + break; + } + case SEL_OP_SET: { + mpoly_sel->flag |= ME_FACE_SEL; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; + } } - } - else { - mpoly_sel->flag |= ME_FACE_SEL; - } - /* image window redraw */ + /* image window redraw */ - paintface_flush_flags(C, ob, SELECT); - ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */ - return true; + paintface_flush_flags(C, ob, SELECT); + ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */ + changed = true; + } + return changed || found; } void paintvert_flush_flags(Object *ob) diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index faf449a77aa..d3fc83eedd7 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -108,6 +108,7 @@ static int geometry_extract_apply(bContext *C, new_mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); BMEditMesh *em = BKE_editmesh_create(bm); diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index dbda9fa7746..f2e7150e791 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -28,6 +28,7 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -700,7 +701,10 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE /* TODO(dfelinto): right now we try to find the closest element twice. * The ideal is to refactor EDBM_select_pick so it doesn't * have to pick the nearest vert/edge/face again. */ - EDBM_select_pick(C, event->mval, true, false, false); + const struct SelectPick_Params params = { + .sel_op = SEL_OP_ADD, + }; + EDBM_select_pick(C, event->mval, ¶ms); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c index 6b3a796ab0d..85426acb905 100644 --- a/source/blender/editors/mesh/editmesh_rip_edge.c +++ b/source/blender/editors/mesh/editmesh_rip_edge.c @@ -125,7 +125,7 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve #ifdef USE_TRICKY_EXTEND /* first check if we can select the edge to split based on selection-only */ - int tot_sel = 0, tot = 0; + int tot_sel = 0; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { @@ -133,7 +133,6 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve e_best = e; tot_sel += 1; } - tot += 1; } } if (tot_sel != 1) { diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 8784b1a90d9..9c8c5c45cb7 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -2016,7 +2016,7 @@ void MESH_OT_select_interior_faces(wmOperatorType *ot) * Gets called via generic mouse select operator. * \{ */ -bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +bool EDBM_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params) { ViewContext vc; @@ -2033,100 +2033,153 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len); - bool ok = false; - - if (unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa)) { - Base *basact = bases[base_index_active]; - ED_view3d_viewcontext_init_object(&vc, basact->object); + bool changed = false; + bool found = unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa); - /* Deselect everything */ - if (extend == false && deselect == false && toggle == false) { + if (params->sel_op == SEL_OP_SET) { + BMElem *ele = efa ? (BMElem *)efa : (eed ? (BMElem *)eed : (BMElem *)eve); + if ((found && params->select_passthrough) && BM_elem_flag_test(ele, BM_ELEM_SELECT)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ for (uint base_index = 0; base_index < bases_len; base_index++) { Base *base_iter = bases[base_index]; Object *ob_iter = base_iter->object; EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT); - if (basact->object != ob_iter) { - DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); - } + DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); } + changed = true; } + } + + if (found) { + Base *basact = bases[base_index_active]; + ED_view3d_viewcontext_init_object(&vc, basact->object); if (efa) { - if (extend) { - /* set the last selected face */ - BM_mesh_active_face_set(vc.em->bm, efa); - - /* Work-around: deselect first, so we can guarantee it will */ - /* be active even if it was already selected */ - BM_select_history_remove(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, false); - BM_select_history_store(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, true); - } - else if (deselect) { - BM_select_history_remove(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, false); - } - else { - /* set the last selected face */ - BM_mesh_active_face_set(vc.em->bm, efa); + switch (params->sel_op) { + case SEL_OP_ADD: { + BM_mesh_active_face_set(vc.em->bm, efa); - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + /* Work-around: deselect first, so we can guarantee it will + * be active even if it was already selected. */ + BM_select_history_remove(vc.em->bm, efa); + BM_face_select_set(vc.em->bm, efa, false); BM_select_history_store(vc.em->bm, efa); BM_face_select_set(vc.em->bm, efa, true); + break; } - else if (toggle) { + case SEL_OP_SUB: { BM_select_history_remove(vc.em->bm, efa); BM_face_select_set(vc.em->bm, efa, false); + break; + } + case SEL_OP_XOR: { + BM_mesh_active_face_set(vc.em->bm, efa); + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + BM_select_history_store(vc.em->bm, efa); + BM_face_select_set(vc.em->bm, efa, true); + } + else { + BM_select_history_remove(vc.em->bm, efa); + BM_face_select_set(vc.em->bm, efa, false); + } + break; + } + case SEL_OP_SET: { + BM_mesh_active_face_set(vc.em->bm, efa); + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + BM_select_history_store(vc.em->bm, efa); + BM_face_select_set(vc.em->bm, efa, true); + } + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } } } else if (eed) { - if (extend) { - /* Work-around: deselect first, so we can guarantee it will */ - /* be active even if it was already selected */ - BM_select_history_remove(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, false); - BM_select_history_store(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, true); - } - else if (deselect) { - BM_select_history_remove(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, false); - } - else { - if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + + switch (params->sel_op) { + case SEL_OP_ADD: { + /* Work-around: deselect first, so we can guarantee it will + * be active even if it was already selected. */ + BM_select_history_remove(vc.em->bm, eed); + BM_edge_select_set(vc.em->bm, eed, false); BM_select_history_store(vc.em->bm, eed); BM_edge_select_set(vc.em->bm, eed, true); + break; } - else if (toggle) { + case SEL_OP_SUB: { BM_select_history_remove(vc.em->bm, eed); BM_edge_select_set(vc.em->bm, eed, false); + break; + } + case SEL_OP_XOR: { + if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + BM_select_history_store(vc.em->bm, eed); + BM_edge_select_set(vc.em->bm, eed, true); + } + else { + BM_select_history_remove(vc.em->bm, eed); + BM_edge_select_set(vc.em->bm, eed, false); + } + break; + } + case SEL_OP_SET: { + if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + BM_select_history_store(vc.em->bm, eed); + BM_edge_select_set(vc.em->bm, eed, true); + } + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } } } else if (eve) { - if (extend) { - /* Work-around: deselect first, so we can guarantee it will */ - /* be active even if it was already selected */ - BM_select_history_remove(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, false); - BM_select_history_store(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, true); - } - else if (deselect) { - BM_select_history_remove(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, false); - } - else { - if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + switch (params->sel_op) { + case SEL_OP_ADD: { + /* Work-around: deselect first, so we can guarantee it will + * be active even if it was already selected. */ + BM_select_history_remove(vc.em->bm, eve); + BM_vert_select_set(vc.em->bm, eve, false); BM_select_history_store(vc.em->bm, eve); BM_vert_select_set(vc.em->bm, eve, true); + break; } - else if (toggle) { + case SEL_OP_SUB: { BM_select_history_remove(vc.em->bm, eve); BM_vert_select_set(vc.em->bm, eve, false); + break; + } + case SEL_OP_XOR: { + if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + BM_select_history_store(vc.em->bm, eve); + BM_vert_select_set(vc.em->bm, eve, true); + } + else { + BM_select_history_remove(vc.em->bm, eve); + BM_vert_select_set(vc.em->bm, eve, false); + } + break; + } + case SEL_OP_SET: { + if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + BM_select_history_store(vc.em->bm, eve); + BM_vert_select_set(vc.em->bm, eve, true); + } + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } } } @@ -2168,12 +2221,12 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - ok = true; + changed = true; } MEM_freeN(bases); - return ok; + return changed; } /** \} */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 54cc3efe986..2181b652d16 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -57,6 +57,7 @@ #include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_transform.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -2301,7 +2302,7 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op) BMIter iter; const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw"); - int tot_rotate_all = 0, tot_failed_all = 0; + int tot_failed_all = 0; bool no_selected_edges = true, invalid_selected_edges = true; ViewLayer *view_layer = CTX_data_view_layer(C); @@ -2359,7 +2360,6 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op) const int tot_rotate = BMO_slot_buffer_len(bmop.slots_out, "edges.out"); const int tot_failed = tot - tot_rotate; - tot_rotate_all += tot_rotate; tot_failed_all += tot_failed; if (tot_failed != 0) { @@ -8540,7 +8540,10 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * case EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED: new_mode = EDBM_CLNOR_POINTTO_MODE_COORDINATES; view3d_operator_needs_opengl(C); - if (EDBM_select_pick(C, event->mval, false, false, false)) { + const struct SelectPick_Params params = { + .sel_op = SEL_OP_SET, + }; + if (EDBM_select_pick(C, event->mval, ¶ms)) { /* Point to newly selected active. */ ED_object_calc_active_center_for_editmode(obedit, false, target); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 9c7d712a739..ecc5f8f8ef5 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -673,6 +673,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em) (&(struct BMeshFromMeshParams){ /* Handled with tessellation. */ .calc_face_normal = false, + .calc_vert_normal = false, .active_shapekey = um->shapenr, })); diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 136d0d46c68..06a649e5b6c 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -736,14 +736,43 @@ void MBALL_OT_reveal_metaelems(wmOperatorType *ot) /** \name Select Pick Utility * \{ */ -bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +Base *ED_mball_base_and_elem_from_select_buffer(Base **bases, + uint bases_len, + const uint select_id, + MetaElem **r_ml) +{ + const uint hit_object = select_id & 0xFFFF; + Base *base = NULL; + MetaElem *ml = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + if (bases[base_index]->object->runtime.select_id == hit_object) { + base = bases[base_index]; + break; + } + } + if (base != NULL) { + const uint hit_elem = (select_id & ~MBALLSEL_ANY) >> 16; + MetaBall *mb = base->object->data; + ml = BLI_findlink(mb->editelems, hit_elem); + } + *r_ml = ml; + return base; +} + +static bool ed_mball_findnearest_metaelem(bContext *C, + const int mval[2], + bool use_cycle, + Base **r_base, + MetaElem **r_ml, + uint *r_selmask) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - static MetaElem *startelem = NULL; ViewContext vc; int a, hits; GPUSelectResult buffer[MAXPICKELEMS]; rcti rect; + bool found = false; ED_view3d_viewcontext_init(C, &vc, depsgraph); @@ -753,131 +782,140 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese buffer, ARRAY_SIZE(buffer), &rect, - VIEW3D_SELECT_PICK_NEAREST, + use_cycle ? VIEW3D_SELECT_PICK_ALL : VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP); - FOREACH_BASE_IN_EDIT_MODE_BEGIN (vc.view_layer, vc.v3d, base) { - ED_view3d_viewcontext_init_object(&vc, base->object); - MetaBall *mb = (MetaBall *)base->object->data; - MetaElem *ml, *ml_act = NULL; + if (hits == 0) { + return false; + } - /* does startelem exist? */ - ml = mb->editelems->first; - while (ml) { - if (ml == startelem) { - break; + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len); + + int hit_cycle_offset = 0; + if (use_cycle) { + /* When cycling, use the hit directly after the current active meta-element (when set). */ + const int base_index = vc.obact->runtime.select_id; + MetaBall *mb = (MetaBall *)vc.obact->data; + MetaElem *ml = mb->lastelem; + if (ml && (ml->flag & SELECT)) { + const int ml_index = BLI_findindex(mb->editelems, ml); + BLI_assert(ml_index != -1); + + /* Count backwards in case the active meta-element has multiple entries, + * ensure this steps onto the next meta-element. */ + a = hits; + while (a--) { + const int select_id = buffer[a].id; + if (select_id == -1) { + continue; + } + + if (((select_id & 0xFFFF) == base_index) && + ((select_id & ~MBALLSEL_ANY) >> 16 == ml_index)) { + hit_cycle_offset = a + 1; + break; + } } - ml = ml->next; } + } - if (ml == NULL) { - startelem = mb->editelems->first; + for (a = 0; a < hits; a++) { + const int index = (hit_cycle_offset == 0) ? a : ((a + hit_cycle_offset) % hits); + const uint select_id = buffer[index].id; + if (select_id == -1) { + continue; } - if (hits > 0) { - int metaelem_id = 0; - ml = startelem; - while (ml) { - for (a = 0; a < hits; a++) { - const int hitresult = buffer[a].id; - if (hitresult == -1) { - continue; - } + MetaElem *ml; + Base *base = ED_mball_base_and_elem_from_select_buffer(bases, bases_len, select_id, &ml); + if (ml == NULL) { + continue; + } + *r_base = base; + *r_ml = ml; + *r_selmask = select_id & MBALLSEL_ANY; + found = true; + break; + } - const uint hit_object = hitresult & 0xFFFF; - if (vc.obedit->runtime.select_id != hit_object) { - continue; - } + MEM_freeN(bases); - if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) { - continue; - } + return found; +} - if (hitresult & MBALLSEL_RADIUS) { - ml->flag |= MB_SCALE_RAD; - ml_act = ml; - break; - } +bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params) +{ + Base *base = NULL; + MetaElem *ml = NULL; + uint selmask = 0; - if (hitresult & MBALLSEL_STIFF) { - ml->flag &= ~MB_SCALE_RAD; - ml_act = ml; - break; - } - } + bool changed = false; - if (ml_act) { - break; - } - ml = ml->next; - if (ml == NULL) { - ml = mb->editelems->first; - } - if (ml == startelem) { - break; - } + bool found = ed_mball_findnearest_metaelem(C, mval, true, &base, &ml, &selmask); - metaelem_id += 0x10000; - } + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (ml->flag & SELECT)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ + changed |= ED_mball_deselect_all_multi(C); + } + } - /* When some metaelem was found, then it is necessary to select or deselect it. */ - if (ml_act) { - if (!extend && !deselect && !toggle) { - uint objects_len; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - vc.view_layer, vc.v3d, &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob_iter = objects[ob_index]; - - if (ob_iter == base->object) { - continue; - } - - BKE_mball_deselect_all((MetaBall *)ob_iter->data); - DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); - } - MEM_freeN(objects); - } + if (found) { + if (selmask & MBALLSEL_RADIUS) { + ml->flag |= MB_SCALE_RAD; + } + else if (selmask & MBALLSEL_STIFF) { + ml->flag &= ~MB_SCALE_RAD; + } - if (extend) { - ml_act->flag |= SELECT; - } - else if (deselect) { - ml_act->flag &= ~SELECT; - } - else if (toggle) { - if (ml_act->flag & SELECT) { - ml_act->flag &= ~SELECT; - } - else { - ml_act->flag |= SELECT; - } + switch (params->sel_op) { + case SEL_OP_ADD: { + ml->flag |= SELECT; + break; + } + case SEL_OP_SUB: { + ml->flag &= ~SELECT; + break; + } + case SEL_OP_XOR: { + if (ml->flag & SELECT) { + ml->flag &= ~SELECT; } else { - /* Deselect all existing metaelems */ - BKE_mball_deselect_all(mb); - - /* Select only metaelem clicked on */ - ml_act->flag |= SELECT; + ml->flag |= SELECT; } + break; + } + case SEL_OP_SET: { + /* Deselect has already been performed. */ + ml->flag |= SELECT; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; + } + } - mb->lastelem = ml_act; - - DEG_id_tag_update(&mb->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb); + ViewLayer *view_layer = CTX_data_view_layer(C); + MetaBall *mb = (MetaBall *)base->object->data; + mb->lastelem = ml; - if (vc.view_layer->basact != base) { - ED_object_base_activate(C, base); - } + DEG_id_tag_update(&mb->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb); - return true; - } + if (view_layer->basact != base) { + ED_object_base_activate(C, base); } + + changed = true; } - FOREACH_BASE_IN_EDIT_MODE_END; - return false; + return changed || found; } /** \} */ diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 6f8763fa2bb..02feccc211a 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -51,7 +51,7 @@ set(SRC object_select.c object_shader_fx.c object_shapekey.c - object_transform.c + object_transform.cc object_utils.c object_vgroup.c object_volume.c diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 3529574a3a4..efcb47f76ab 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -23,6 +23,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index 9b62823ea8a..d0a6a5d44c0 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -403,7 +403,7 @@ void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot) /* properties */ prop = RNA_def_enum(ot->srna, "type", - rna_enum_object_modifier_type_items, + rna_enum_object_greasepencil_modifier_type_items, eGpencilModifierType_Thick, "Type", ""); diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 60a49fa6945..490c495dad5 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -28,7 +28,7 @@ enum eObject_Hook_Add_Mode { /* internal exports only */ -/* object_transform.c */ +/* object_transform.cc */ void OBJECT_OT_location_clear(struct wmOperatorType *ot); void OBJECT_OT_rotation_clear(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index b0c8646dd04..7fedc2c6265 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -2219,7 +2219,7 @@ static int multires_unsubdivide_exec(bContext *C, wmOperator *op) int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, 1, true); if (new_levels == 0) { - BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild a lower level"); + BKE_report(op->reports, RPT_ERROR, "No valid subdivisions found to rebuild a lower level"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.cc index 9e82abf4d07..24425b5a991 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.cc @@ -5,8 +5,8 @@ * \ingroup edobj */ -#include <stdlib.h> -#include <string.h> +#include <cstdlib> +#include <cstring> #include "DNA_anim_types.h" #include "DNA_armature_types.h" @@ -19,10 +19,12 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BLI_array.h" +#include "BLI_array.hh" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_math_vector.hh" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_armature.h" #include "BKE_context.h" @@ -64,6 +66,10 @@ #include "object_intern.h" +using blender::Array; +using blender::float2; +using blender::Vector; + /* -------------------------------------------------------------------- */ /** \name Clear Transformation Utilities * \{ */ @@ -283,25 +289,20 @@ static int object_clear_transform_generic_exec(bContext *C, Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - /* May be NULL. */ + /* May be null. */ View3D *v3d = CTX_wm_view3d(C); KeyingSet *ks; const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta"); - BLI_assert(!ELEM(NULL, clear_func, default_ksName)); + BLI_assert(!ELEM(nullptr, clear_func, default_ksName)); - Object **objects = NULL; - uint objects_len = 0; - { - BLI_array_declare(objects); - FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) { - BLI_array_append(objects, ob); - } - FOREACH_SELECTED_EDITABLE_OBJECT_END; - objects_len = BLI_array_len(objects); + Vector<Object *> objects; + FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) { + objects.append(ob); } + FOREACH_SELECTED_EDITABLE_OBJECT_END; - if (objects == NULL) { + if (objects.is_empty()) { return OPERATOR_CANCELLED; } @@ -310,14 +311,14 @@ static int object_clear_transform_generic_exec(bContext *C, SCE_XFORM_SKIP_CHILDREN); const bool use_transform_data_origin = (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN); - struct XFormObjectSkipChild_Container *xcs = NULL; - struct XFormObjectData_Container *xds = NULL; + struct XFormObjectSkipChild_Container *xcs = nullptr; + struct XFormObjectData_Container *xds = nullptr; if (use_transform_skip_children) { BKE_scene_graph_evaluated_ensure(depsgraph, bmain); xcs = ED_object_xform_skip_child_container_create(); ED_object_xform_skip_child_container_item_ensure_from_array( - xcs, view_layer, objects, objects_len); + xcs, view_layer, objects.data(), objects.size()); } if (use_transform_data_origin) { BKE_scene_graph_evaluated_ensure(depsgraph, bmain); @@ -327,9 +328,7 @@ static int object_clear_transform_generic_exec(bContext *C, /* get KeyingSet to use */ ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = objects[ob_index]; - + for (Object *ob : objects) { if (use_transform_data_origin) { ED_object_data_xform_container_item_ensure(xds, ob); } @@ -342,7 +341,6 @@ static int object_clear_transform_generic_exec(bContext *C, /* tag for updates */ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } - MEM_freeN(objects); if (use_transform_skip_children) { ED_object_xform_skip_child_container_update_all(xcs, bmain, depsgraph); @@ -355,7 +353,7 @@ static int object_clear_transform_generic_exec(bContext *C, } /* this is needed so children are also updated */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr); return OPERATOR_FINISHED; } @@ -488,7 +486,7 @@ static int object_origin_clear_exec(bContext *C, wmOperator *UNUSED(op)) } CTX_DATA_END; - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr); return OPERATOR_FINISHED; } @@ -519,12 +517,11 @@ void OBJECT_OT_origin_clear(wmOperatorType *ot) static void ignore_parent_tx(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { Object workob; - Object *ob_child; Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); /* a change was made, adjust the children to compensate */ - for (ob_child = bmain->objects.first; ob_child; ob_child = ob_child->id.next) { + LISTBASE_FOREACH (Object *, ob_child, &bmain->objects) { if (ob_child->parent == ob) { Object *ob_child_eval = DEG_get_evaluated_object(depsgraph, ob_child); BKE_object_apply_mat4(ob_child_eval, ob_child_eval->obmat, true, false); @@ -549,7 +546,7 @@ static void append_sorted_object_parent_hierarchy(Object *root_object, Object **sorted_objects, int *object_index) { - if (!ELEM(object->parent, NULL, root_object)) { + if (!ELEM(object->parent, nullptr, root_object)) { append_sorted_object_parent_hierarchy( root_object, object->parent, sorted_objects, object_index); } @@ -560,7 +557,7 @@ static void append_sorted_object_parent_hierarchy(Object *root_object, } } -static Object **sorted_selected_editable_objects(bContext *C, int *r_num_objects) +static Array<Object *> sorted_selected_editable_objects(bContext *C) { Main *bmain = CTX_data_main(C); @@ -573,23 +570,20 @@ static Object **sorted_selected_editable_objects(bContext *C, int *r_num_objects } CTX_DATA_END; if (num_objects == 0) { - *r_num_objects = 0; - return NULL; + return {}; } /* Append all the objects. */ - Object **sorted_objects = MEM_malloc_arrayN(num_objects, sizeof(Object *), "sorted objects"); + Array<Object *> sorted_objects(num_objects); int object_index = 0; CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) { if ((object->id.tag & LIB_TAG_DOIT) == 0) { continue; } - append_sorted_object_parent_hierarchy(object, object, sorted_objects, &object_index); + append_sorted_object_parent_hierarchy(object, object, sorted_objects.data(), &object_index); } CTX_DATA_END; - *r_num_objects = num_objects; - return sorted_objects; } @@ -617,11 +611,11 @@ static int apply_objects_internal(bContext *C, OB_SURF, OB_FONT, OB_GPENCIL)) { - ID *obdata = ob->data; + ID *obdata = static_cast<ID *>(ob->data); if (ID_REAL_USERS(obdata) > 1) { BKE_reportf(reports, RPT_ERROR, - "Cannot apply to a multi user: Object \"%s\", %s \"%s\", aborting", + R"(Cannot apply to a multi user: Object "%s", %s "%s", aborting)", ob->id.name + 2, BKE_idtype_idcode_to_name(GS(obdata->name)), obdata->name + 2); @@ -631,7 +625,7 @@ static int apply_objects_internal(bContext *C, if (ID_IS_LINKED(obdata)) { BKE_reportf(reports, RPT_ERROR, - "Cannot apply to library data: Object \"%s\", %s \"%s\", aborting", + R"(Cannot apply to library data: Object "%s", %s "%s", aborting)", ob->id.name + 2, BKE_idtype_idcode_to_name(GS(obdata->name)), obdata->name + 2); @@ -640,16 +634,14 @@ static int apply_objects_internal(bContext *C, } if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { - ID *obdata = ob->data; - Curve *cu; - - cu = ob->data; + ID *obdata = static_cast<ID *>(ob->data); + Curve *cu = static_cast<Curve *>(ob->data); if (((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) { BKE_reportf( reports, RPT_ERROR, - "Rotation/Location can't apply to a 2D curve: Object \"%s\", %s \"%s\", aborting", + R"(Rotation/Location can't apply to a 2D curve: Object "%s", %s "%s", aborting)", ob->id.name + 2, BKE_idtype_idcode_to_name(GS(obdata->name)), obdata->name + 2); @@ -658,7 +650,7 @@ static int apply_objects_internal(bContext *C, if (cu->key) { BKE_reportf(reports, RPT_ERROR, - "Can't apply to a curve with shape-keys: Object \"%s\", %s \"%s\", aborting", + R"(Can't apply to a curve with shape-keys: Object "%s", %s "%s", aborting)", ob->id.name + 2, BKE_idtype_idcode_to_name(GS(obdata->name)), obdata->name + 2); @@ -675,7 +667,7 @@ static int apply_objects_internal(bContext *C, } if (ob->type == OB_GPENCIL) { - bGPdata *gpd = ob->data; + bGPdata *gpd = static_cast<bGPdata *>(ob->data); if (gpd) { if (gpd->layers.first) { /* Unsupported configuration */ @@ -684,7 +676,7 @@ static int apply_objects_internal(bContext *C, LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* Parented layers aren't supported as we can't easily re-evaluate * the scene to sample parent movement */ - if (gpl->parent == NULL) { + if (gpl->parent == nullptr) { has_unparented_layers = true; break; } @@ -706,7 +698,7 @@ static int apply_objects_internal(bContext *C, BKE_reportf( reports, RPT_ERROR, - "Can't apply to GP data-block with no layers: Object \"%s\", %s \"%s\", aborting", + R"(Can't apply to GP data-block with no layers: Object "%s", %s "%s", aborting)", ob->id.name + 2, BKE_idtype_idcode_to_name(ID_GD), gpd->id.name + 2); @@ -715,7 +707,7 @@ static int apply_objects_internal(bContext *C, } if (ob->type == OB_LAMP) { - Light *la = ob->data; + Light *la = static_cast<Light *>(ob->data); if (la->type == LA_AREA) { if (apply_rot || apply_loc) { BKE_reportf(reports, @@ -736,15 +728,12 @@ static int apply_objects_internal(bContext *C, changed = false; /* now execute */ - int num_objects; - Object **objects = sorted_selected_editable_objects(C, &num_objects); - if (objects == NULL) { + Array<Object *> objects = sorted_selected_editable_objects(C); + if (objects.is_empty()) { return OPERATOR_CANCELLED; } - for (int object_index = 0; object_index < num_objects; object_index++) { - Object *ob = objects[object_index]; - + for (Object *ob : objects) { /* calculate rotation/scale matrix */ if (apply_scale && apply_rot) { BKE_object_to_mat3(ob, rsmat); @@ -786,7 +775,7 @@ static int apply_objects_internal(bContext *C, /* apply to object data */ if (ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (apply_scale) { multiresModifier_scale_disp(depsgraph, scene, ob); @@ -799,25 +788,25 @@ static int apply_objects_internal(bContext *C, BKE_mesh_normals_tag_dirty(me); } else if (ob->type == OB_ARMATURE) { - bArmature *arm = ob->data; + bArmature *arm = static_cast<bArmature *>(ob->data); BKE_armature_transform(arm, mat, do_props); } else if (ob->type == OB_LATTICE) { - Lattice *lt = ob->data; + Lattice *lt = static_cast<Lattice *>(ob->data); BKE_lattice_transform(lt, mat, true); } else if (ob->type == OB_MBALL) { - MetaBall *mb = ob->data; + MetaBall *mb = static_cast<MetaBall *>(ob->data); BKE_mball_transform(mb, mat, do_props); } else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { - Curve *cu = ob->data; + Curve *cu = static_cast<Curve *>(ob->data); scale = mat3_to_scale(rsmat); BKE_curve_transform_ex(cu, mat, true, do_props, scale); } else if (ob->type == OB_FONT) { - Curve *cu = ob->data; + Curve *cu = static_cast<Curve *>(ob->data); scale = mat3_to_scale(rsmat); @@ -834,7 +823,7 @@ static int apply_objects_internal(bContext *C, } } else if (ob->type == OB_GPENCIL) { - bGPdata *gpd = ob->data; + bGPdata *gpd = static_cast<bGPdata *>(ob->data); BKE_gpencil_transform(gpd, mat); } else if (ob->type == OB_CAMERA) { @@ -870,7 +859,7 @@ static int apply_objects_internal(bContext *C, } } else if (ob->type == OB_LAMP) { - Light *la = ob->data; + Light *la = static_cast<Light *>(ob->data); if (la->type != LA_AREA) { continue; } @@ -911,7 +900,8 @@ static int apply_objects_internal(bContext *C, BKE_object_where_is_calc(depsgraph, scene, ob_eval); if (ob->type == OB_ARMATURE) { /* needed for bone parents */ - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data), + static_cast<bArmature *>(ob->data)); BKE_pose_where_is(depsgraph, scene, ob_eval); } @@ -922,14 +912,12 @@ static int apply_objects_internal(bContext *C, changed = true; } - MEM_freeN(objects); - if (!changed) { BKE_report(reports, RPT_WARNING, "Objects have no data to transform"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr); return OPERATOR_FINISHED; } @@ -956,7 +944,7 @@ static int visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr); return OPERATOR_FINISHED; } @@ -1034,7 +1022,6 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) Object *obact = CTX_data_active_object(C); Object *obedit = CTX_data_edit_object(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Object *tob; float cent[3], cent_neg[3], centn[3]; const float *cursor = scene->cursor.location; int centermode = RNA_enum_get(op->ptr, "type"); @@ -1068,7 +1055,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) if (obedit) { if (obedit->type == OB_MESH) { - Mesh *me = obedit->data; + Mesh *me = static_cast<Mesh *>(obedit->data); BMEditMesh *em = me->edit_mesh; BMVert *eve; BMIter iter; @@ -1107,25 +1094,24 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } } - int num_objects; - Object **objects = sorted_selected_editable_objects(C, &num_objects); - if (objects == NULL) { + Array<Object *> objects = sorted_selected_editable_objects(C); + if (objects.is_empty()) { return OPERATOR_CANCELLED; } /* reset flags */ - for (int object_index = 0; object_index < num_objects; object_index++) { + for (int object_index = 0; object_index < objects.size(); object_index++) { Object *ob = objects[object_index]; ob->flag &= ~OB_DONE; /* move active first */ if (ob == obact) { - memmove(&objects[1], objects, object_index * sizeof(Object *)); + memmove(&objects[1], objects.data(), object_index * sizeof(Object *)); objects[0] = ob; } } - for (tob = bmain->objects.first; tob; tob = tob->id.next) { + LISTBASE_FOREACH (Object *, tob, &bmain->objects) { if (tob->data) { ((ID *)tob->data)->tag &= ~LIB_TAG_DOIT; } @@ -1134,8 +1120,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } } - for (int object_index = 0; object_index < num_objects; object_index++) { - Object *ob = objects[object_index]; + for (Object *ob : objects) { if (ob->flag & OB_DONE) { continue; } @@ -1149,8 +1134,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) mul_m4_v3(ob->imat, cent); } - if (ob->data == NULL) { - /* special support for dupligroups */ + if (ob->data == nullptr) { + /* Special support for instanced collections. */ if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection && (ob->instance_collection->id.tag & LIB_TAG_DOIT) == 0) { if (ID_IS_LINKED(ob->instance_collection)) { @@ -1182,8 +1167,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) tot_lib_error++; } else if (ob->type == OB_MESH) { - if (obedit == NULL) { - Mesh *me = ob->data; + if (obedit == nullptr) { + Mesh *me = static_cast<Mesh *>(ob->data); if (centermode == ORIGIN_TO_CURSOR) { /* done */ @@ -1202,7 +1187,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } negate_v3_v3(cent_neg, cent); - BKE_mesh_translate(me, cent_neg, 1); + BKE_mesh_translate(me, cent_neg, true); tot_change++; me->id.tag |= LIB_TAG_DOIT; @@ -1210,7 +1195,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } } else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { - Curve *cu = ob->data; + Curve *cu = static_cast<Curve *>(ob->data); if (centermode == ORIGIN_TO_CURSOR) { /* done */ @@ -1228,7 +1213,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } negate_v3_v3(cent_neg, cent); - BKE_curve_translate(cu, cent_neg, 1); + BKE_curve_translate(cu, cent_neg, true); tot_change++; cu->id.tag |= LIB_TAG_DOIT; @@ -1244,9 +1229,9 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) else if (ob->type == OB_FONT) { /* Get from bounding-box. */ - Curve *cu = ob->data; + Curve *cu = static_cast<Curve *>(ob->data); - if (ob->runtime.bb == NULL && (centermode != ORIGIN_TO_CURSOR)) { + if (ob->runtime.bb == nullptr && (centermode != ORIGIN_TO_CURSOR)) { /* Do nothing. */ } else { @@ -1270,7 +1255,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } } else if (ob->type == OB_ARMATURE) { - bArmature *arm = ob->data; + bArmature *arm = static_cast<bArmature *>(ob->data); if (ID_REAL_USERS(arm) > 1) { #if 0 @@ -1291,7 +1276,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); BKE_object_transform_copy(ob_eval, ob); - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data), + static_cast<bArmature *>(ob->data)); BKE_object_where_is_calc(depsgraph, scene, ob_eval); BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */ @@ -1303,7 +1289,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } } else if (ob->type == OB_MBALL) { - MetaBall *mb = ob->data; + MetaBall *mb = static_cast<MetaBall *>(ob->data); if (centermode == ORIGIN_TO_CURSOR) { /* done */ @@ -1330,7 +1316,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } } else if (ob->type == OB_LATTICE) { - Lattice *lt = ob->data; + Lattice *lt = static_cast<Lattice *>(ob->data); if (centermode == ORIGIN_TO_CURSOR) { /* done */ @@ -1343,14 +1329,14 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } negate_v3_v3(cent_neg, cent); - BKE_lattice_translate(lt, cent_neg, 1); + BKE_lattice_translate(lt, cent_neg, true); tot_change++; lt->id.tag |= LIB_TAG_DOIT; do_inverse_offset = true; } else if (ob->type == OB_GPENCIL) { - bGPdata *gpd = ob->data; + bGPdata *gpd = static_cast<bGPdata *>(ob->data); float gpcenter[3]; if (gpd) { if (centermode == ORIGIN_TO_GEOMETRY) { @@ -1394,7 +1380,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } /* Apply transform to edit-curve. */ - if (gps->editcurve != NULL) { + if (gps->editcurve != nullptr) { for (i = 0; i < gps->editcurve->tot_curve_points; i++) { BezTriple *bezt = &gps->editcurve->curve_points[i].bezt; for (int j = 0; j < 3; j++) { @@ -1444,7 +1430,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) BKE_object_where_is_calc(depsgraph, scene, ob_eval); if (ob->type == OB_ARMATURE) { /* needed for bone parents */ - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data), + static_cast<bArmature *>(ob->data)); BKE_pose_where_is(depsgraph, scene, ob_eval); } @@ -1455,9 +1442,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) //{ /* use existing context looper */ - for (int other_object_index = 0; other_object_index < num_objects; other_object_index++) { - Object *ob_other = objects[other_object_index]; - + for (Object *ob_other : objects) { if ((ob_other->flag & OB_DONE) == 0 && ((ob->data && (ob->data == ob_other->data)) || (ob->instance_collection == ob_other->instance_collection && @@ -1473,7 +1458,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) BKE_object_where_is_calc(depsgraph, scene, ob_other_eval); if (ob_other->type == OB_ARMATURE) { /* needed for bone parents */ - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data), + static_cast<bArmature *>(ob->data)); BKE_pose_where_is(depsgraph, scene, ob_other_eval); } ignore_parent_tx(bmain, depsgraph, scene, ob_other); @@ -1482,9 +1468,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) // CTX_DATA_END; } } - MEM_freeN(objects); - for (tob = bmain->objects.first; tob; tob = tob->id.next) { + LISTBASE_FOREACH (Object *, tob, &bmain->objects) { if (tob->data && (((ID *)tob->data)->tag & LIB_TAG_DOIT)) { BKE_object_batch_cache_dirty_tag(tob); DEG_id_tag_update(&tob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); @@ -1497,7 +1482,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } if (tot_change) { - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr); } /* Warn if any errors occurred */ @@ -1550,13 +1535,13 @@ void OBJECT_OT_origin_set(wmOperatorType *ot) "Origin to Center of Mass (Volume)", "Calculate the center of mass from the volume (must be manifold geometry with consistent " "normals)"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static const EnumPropertyItem prop_set_bounds_types[] = { {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", 0, "Median Center", ""}, {V3D_AROUND_CENTER_BOUNDS, "BOUNDS", 0, "Bounds Center", ""}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; /* identifiers */ @@ -1622,26 +1607,23 @@ struct XFormAxisData { bool is_normal_valid; } prev; - struct XFormAxisItem *object_data; - uint object_data_len; + Vector<XFormAxisItem> object_data; bool is_translate; int init_event; }; #ifdef USE_FAKE_DEPTH_INIT -static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *xfd, - const int mval[2]) +static void object_transform_axis_target_calc_depth_init(XFormAxisData *xfd, const int mval[2]) { - struct XFormAxisItem *item = xfd->object_data; float view_co_a[3], view_co_b[3]; - const float mval_fl[2] = {UNPACK2(mval)}; + const float2 mval_fl = {static_cast<float>(mval[0]), static_cast<float>(mval[1])}; ED_view3d_win_to_ray(xfd->vc.region, mval_fl, view_co_a, view_co_b); add_v3_v3(view_co_b, view_co_a); float center[3] = {0.0f}; int center_tot = 0; - for (int i = 0; i < xfd->object_data_len; i++, item++) { - const Object *ob = item->ob; + for (XFormAxisItem &item : xfd->object_data) { + const Object *ob = item.ob; const float *ob_co_a = ob->obmat[3]; float ob_co_b[3]; add_v3_v3v3(ob_co_b, ob->obmat[3], ob->obmat[2]); @@ -1664,7 +1646,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x static bool object_is_target_compat(const Object *ob) { if (ob->type == OB_LAMP) { - const Light *la = ob->data; + const Light *la = static_cast<Light *>(ob->data); if (ELEM(la->type, LA_SUN, LA_SPOT, LA_AREA)) { return true; } @@ -1680,8 +1662,7 @@ static bool object_is_target_compat(const Object *ob) static void object_transform_axis_target_free_data(wmOperator *op) { - struct XFormAxisData *xfd = op->customdata; - struct XFormAxisItem *item = xfd->object_data; + XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata); #ifdef USE_RENDER_OVERRIDE if (xfd->depths) { @@ -1689,12 +1670,11 @@ static void object_transform_axis_target_free_data(wmOperator *op) } #endif - for (int i = 0; i < xfd->object_data_len; i++, item++) { - MEM_freeN(item->obtfm); + for (XFormAxisItem &item : xfd->object_data) { + MEM_freeN(item.obtfm); } - MEM_freeN(xfd->object_data); - MEM_freeN(xfd); - op->customdata = NULL; + MEM_delete(xfd); + op->customdata = nullptr; } /* We may want to expose as alternative to: BKE_object_apply_rotation */ @@ -1755,12 +1735,11 @@ static bool object_orient_to_location(Object *ob, static void object_transform_axis_target_cancel(bContext *C, wmOperator *op) { - struct XFormAxisData *xfd = op->customdata; - struct XFormAxisItem *item = xfd->object_data; - for (int i = 0; i < xfd->object_data_len; i++, item++) { - BKE_object_tfm_restore(item->ob, item->obtfm); - DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob); + XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata); + for (XFormAxisItem &item : xfd->object_data) { + BKE_object_tfm_restore(item.ob, item.obtfm); + DEG_id_tag_update(&item.ob->id, ID_RECALC_TRANSFORM); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item.ob); } object_transform_axis_target_free_data(op); @@ -1772,7 +1751,7 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons ViewContext vc; ED_view3d_viewcontext_init(C, &vc, depsgraph); - if (vc.obact == NULL || !object_is_target_compat(vc.obact)) { + if (vc.obact == nullptr || !object_is_target_compat(vc.obact)) { /* Falls back to texture space transform. */ return OPERATOR_PASS_THROUGH; } @@ -1782,22 +1761,23 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons vc.v3d->flag2 |= V3D_HIDE_OVERLAYS; #endif - ViewDepths *depths = NULL; - ED_view3d_depth_override(vc.depsgraph, vc.region, vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, &depths); + ViewDepths *depths = nullptr; + ED_view3d_depth_override( + vc.depsgraph, vc.region, vc.v3d, nullptr, V3D_DEPTH_NO_GPENCIL, &depths); #ifdef USE_RENDER_OVERRIDE vc.v3d->flag2 = flag2_prev; #endif - if (depths == NULL) { + if (depths == nullptr) { BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane"); return OPERATOR_CANCELLED; } ED_region_tag_redraw(vc.region); - struct XFormAxisData *xfd; - xfd = op->customdata = MEM_callocN(sizeof(struct XFormAxisData), __func__); + XFormAxisData *xfd = MEM_new<XFormAxisData>(__func__); + op->customdata = xfd; /* Don't change this at runtime. */ xfd->vc = vc; @@ -1812,41 +1792,25 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons xfd->init_event = WM_userdef_event_type_from_keymap_type(event->type); - { - struct XFormAxisItem *object_data = NULL; - BLI_array_declare(object_data); - - struct XFormAxisItem *item = BLI_array_append_ret(object_data); - item->ob = xfd->vc.obact; - - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if ((ob != xfd->vc.obact) && object_is_target_compat(ob)) { - item = BLI_array_append_ret(object_data); - item->ob = ob; - } - } - CTX_DATA_END; + xfd->object_data.append({}); + xfd->object_data.last().ob = xfd->vc.obact; - xfd->object_data = object_data; - xfd->object_data_len = BLI_array_len(object_data); - - if (xfd->object_data_len != BLI_array_len(object_data)) { - xfd->object_data = MEM_reallocN(xfd->object_data, - xfd->object_data_len * sizeof(*xfd->object_data)); + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + if ((ob != xfd->vc.obact) && object_is_target_compat(ob)) { + xfd->object_data.append({}); + xfd->object_data.last().ob = ob; } } + CTX_DATA_END; - { - struct XFormAxisItem *item = xfd->object_data; - for (int i = 0; i < xfd->object_data_len; i++, item++) { - item->obtfm = BKE_object_tfm_backup(item->ob); - BKE_object_rot_to_mat3(item->ob, item->rot_mat, true); + for (XFormAxisItem &item : xfd->object_data) { + item.obtfm = BKE_object_tfm_backup(item.ob); + BKE_object_rot_to_mat3(item.ob, item.rot_mat, true); - /* Detect negative scale matrix. */ - float full_mat3[3][3]; - BKE_object_to_mat3(item->ob, full_mat3); - item->is_z_flip = dot_v3v3(item->rot_mat[2], full_mat3[2]) < 0.0f; - } + /* Detect negative scale matrix. */ + float full_mat3[3][3]; + BKE_object_to_mat3(item.ob, full_mat3); + item.is_z_flip = dot_v3v3(item.rot_mat[2], full_mat3[2]) < 0.0f; } WM_event_add_modal_handler(C, op); @@ -1856,7 +1820,7 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const wmEvent *event) { - struct XFormAxisData *xfd = op->customdata; + XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata); ARegion *region = xfd->vc.region; view3d_operator_needs_opengl(C); @@ -1922,36 +1886,35 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const { #ifdef USE_RELATIVE_ROTATION - if (is_translate_init && xfd->object_data_len > 1) { + if (is_translate_init && xfd->object_data.size() > 1) { float xform_rot_offset_inv_first[3][3]; - struct XFormAxisItem *item = xfd->object_data; - for (int i = 0; i < xfd->object_data_len; i++, item++) { - copy_m3_m4(item->xform_rot_offset, item->ob->obmat); - normalize_m3(item->xform_rot_offset); + for (const int i : xfd->object_data.index_range()) { + XFormAxisItem &item = xfd->object_data[i]; + copy_m3_m4(item.xform_rot_offset, item.ob->obmat); + normalize_m3(item.xform_rot_offset); if (i == 0) { invert_m3_m3(xform_rot_offset_inv_first, xfd->object_data[0].xform_rot_offset); } else { - mul_m3_m3m3(item->xform_rot_offset, - item->xform_rot_offset, - xform_rot_offset_inv_first); + mul_m3_m3m3( + item.xform_rot_offset, item.xform_rot_offset, xform_rot_offset_inv_first); } } } #endif - struct XFormAxisItem *item = xfd->object_data; - for (int i = 0; i < xfd->object_data_len; i++, item++) { + for (const int i : xfd->object_data.index_range()) { + XFormAxisItem &item = xfd->object_data[i]; if (is_translate_init) { float ob_axis[3]; - item->xform_dist = len_v3v3(item->ob->obmat[3], location_world); - normalize_v3_v3(ob_axis, item->ob->obmat[2]); + item.xform_dist = len_v3v3(item.ob->obmat[3], location_world); + normalize_v3_v3(ob_axis, item.ob->obmat[2]); /* Scale to avoid adding distance when moving between surfaces. */ if (normal_found) { float scale = fabsf(dot_v3v3(ob_axis, normal)); - item->xform_dist *= scale; + item.xform_dist *= scale; } } @@ -1961,13 +1924,13 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const copy_v3_v3(target_normal, normal); } else { - normalize_v3_v3(target_normal, item->ob->obmat[2]); + normalize_v3_v3(target_normal, item.ob->obmat[2]); } #ifdef USE_RELATIVE_ROTATION if (normal_found) { if (i != 0) { - mul_m3_v3(item->xform_rot_offset, target_normal); + mul_m3_v3(item.xform_rot_offset, target_normal); } } #endif @@ -1975,17 +1938,17 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const float loc[3]; copy_v3_v3(loc, location_world); - madd_v3_v3fl(loc, target_normal, item->xform_dist); - object_apply_location(item->ob, loc); + madd_v3_v3fl(loc, target_normal, item.xform_dist); + object_apply_location(item.ob, loc); /* so orient behaves as expected */ - copy_v3_v3(item->ob->obmat[3], loc); + copy_v3_v3(item.ob->obmat[3], loc); } object_orient_to_location( - item->ob, item->rot_mat, item->rot_mat[2], location_world, item->is_z_flip); + item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip); - DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob); + DEG_id_tag_update(&item.ob->id, ID_RECALC_TRANSFORM); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item.ob); } if (normal_found) { copy_v3_v3(xfd->prev.normal, normal); @@ -1994,15 +1957,11 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const } } else { - struct XFormAxisItem *item = xfd->object_data; - for (int i = 0; i < xfd->object_data_len; i++, item++) { - if (object_orient_to_location(item->ob, - item->rot_mat, - item->rot_mat[2], - location_world, - item->is_z_flip)) { - DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob); + for (XFormAxisItem &item : xfd->object_data) { + if (object_orient_to_location( + item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip)) { + DEG_id_tag_update(&item.ob->id, ID_RECALC_TRANSFORM); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item.ob); } } xfd->prev.is_normal_valid = false; diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c index 5bc062eb177..e8ceb97ed7a 100644 --- a/source/blender/editors/physics/dynamicpaint_ops.c +++ b/source/blender/editors/physics/dynamicpaint_ops.c @@ -399,27 +399,27 @@ static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job) * Save output images */ { - char filename[FILE_MAX]; + char filepath[FILE_MAX]; /* primary output layer */ if (surface->flags & MOD_DPAINT_OUT1) { /* set filepath */ BLI_join_dirfile( - filename, sizeof(filename), surface->image_output_path, surface->output_name); - BLI_path_frame(filename, frame, 4); + filepath, sizeof(filepath), surface->image_output_path, surface->output_name); + BLI_path_frame(filepath, frame, 4); /* save image */ - dynamicPaint_outputSurfaceImage(surface, filename, 0); + dynamicPaint_outputSurfaceImage(surface, filepath, 0); } /* secondary output */ if (surface->flags & MOD_DPAINT_OUT2 && surface->type == MOD_DPAINT_SURFACE_T_PAINT) { /* set filepath */ BLI_join_dirfile( - filename, sizeof(filename), surface->image_output_path, surface->output_name2); - BLI_path_frame(filename, frame, 4); + filepath, sizeof(filepath), surface->image_output_path, surface->output_name2); + BLI_path_frame(filepath, frame, 4); /* save image */ - dynamicPaint_outputSurfaceImage(surface, filename, 1); + dynamicPaint_outputSurfaceImage(surface, filepath, 1); } } } diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index fc815ebe682..4a639e227f7 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -487,6 +487,8 @@ typedef struct PEData { int select_action; int select_toggle_action; bool is_changed; + + void *user_data; } PEData; static void PE_set_data(bContext *C, PEData *data) @@ -1721,46 +1723,6 @@ static void select_keys(PEData *data, point->flag |= PEP_EDIT_RECALC; } -static void extend_key_select(PEData *data, int point_index, int key_index, bool UNUSED(is_inside)) -{ - PTCacheEdit *edit = data->edit; - PTCacheEditPoint *point = edit->points + point_index; - PTCacheEditKey *key = point->keys + key_index; - - if ((key->flag & PEK_SELECT) == 0) { - key->flag |= PEK_SELECT; - point->flag |= PEP_EDIT_RECALC; - data->is_changed = true; - } -} - -static void deselect_key_select(PEData *data, - int point_index, - int key_index, - bool UNUSED(is_inside)) -{ - PTCacheEdit *edit = data->edit; - PTCacheEditPoint *point = edit->points + point_index; - PTCacheEditKey *key = point->keys + key_index; - - if ((key->flag & PEK_SELECT) != 0) { - key->flag &= ~PEK_SELECT; - point->flag |= PEP_EDIT_RECALC; - data->is_changed = true; - } -} - -static void toggle_key_select(PEData *data, int point_index, int key_index, bool UNUSED(is_inside)) -{ - PTCacheEdit *edit = data->edit; - PTCacheEditPoint *point = edit->points + point_index; - PTCacheEditKey *key = point->keys + key_index; - - key->flag ^= PEK_SELECT; - point->flag |= PEP_EDIT_RECALC; - data->is_changed = true; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -1862,13 +1824,50 @@ void PARTICLE_OT_select_all(wmOperatorType *ot) /** \name Pick Select Operator * \{ */ -bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +struct NearestParticleData { + PTCacheEditPoint *point; + PTCacheEditKey *key; +}; + +static void nearest_key_fn(PEData *data, int point_index, int key_index, bool UNUSED(is_inside)) +{ + PTCacheEdit *edit = data->edit; + PTCacheEditPoint *point = edit->points + point_index; + PTCacheEditKey *key = point->keys + key_index; + + struct NearestParticleData *user_data = data->user_data; + user_data->point = point; + user_data->key = key; + data->is_changed = true; +} + +static bool pe_nearest_point_and_key(bContext *C, + const int mval[2], + PTCacheEditPoint **r_point, + PTCacheEditKey **r_key) +{ + struct NearestParticleData user_data = {NULL}; + + PEData data; + PE_set_view3d_data(C, &data); + data.mval = mval; + data.rad = ED_view3d_select_dist_px(); + + data.user_data = &user_data; + for_mouse_hit_keys(&data, nearest_key_fn, PSEL_NEAREST); + bool found = data.is_changed; + PE_data_free(&data); + + *r_point = user_data.point; + *r_key = user_data.key; + return found; +} + +bool PE_mouse_particles(bContext *C, const int mval[2], const struct SelectPick_Params *params) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - POINT_P; - KEY_K; PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob); @@ -1876,39 +1875,67 @@ bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool desele return false; } - if (!extend && !deselect && !toggle) { - LOOP_VISIBLE_POINTS { - LOOP_SELECTED_KEYS { - key->flag &= ~PEK_SELECT; - point->flag |= PEP_EDIT_RECALC; - } - } - } + PTCacheEditPoint *point; + PTCacheEditKey *key; - PEData data; - PE_set_view3d_data(C, &data); - data.mval = mval; - data.rad = ED_view3d_select_dist_px(); + bool changed = false; + bool found = pe_nearest_point_and_key(C, mval, &point, &key); - /* 1 = nearest only */ - if (extend) { - for_mouse_hit_keys(&data, extend_key_select, PSEL_NEAREST); - } - else if (deselect) { - for_mouse_hit_keys(&data, deselect_key_select, PSEL_NEAREST); - } - else { - for_mouse_hit_keys(&data, toggle_key_select, PSEL_NEAREST); + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (key->flag & PEK_SELECT)) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ + changed |= PE_deselect_all_visible_ex(edit); + } } - if (data.is_changed) { - PE_update_selection(data.depsgraph, scene, ob, 1); - WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob); + if (found) { + switch (params->sel_op) { + case SEL_OP_ADD: { + if ((key->flag & PEK_SELECT) == 0) { + key->flag |= PEK_SELECT; + point->flag |= PEP_EDIT_RECALC; + changed = true; + } + break; + } + case SEL_OP_SUB: { + if ((key->flag & PEK_SELECT) != 0) { + key->flag &= ~PEK_SELECT; + point->flag |= PEP_EDIT_RECALC; + changed = true; + } + break; + } + case SEL_OP_XOR: { + key->flag ^= PEK_SELECT; + point->flag |= PEP_EDIT_RECALC; + changed = true; + break; + } + case SEL_OP_SET: { + if ((key->flag & PEK_SELECT) == 0) { + key->flag |= PEK_SELECT; + point->flag |= PEP_EDIT_RECALC; + changed = true; + } + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; + } + } } - PE_data_free(&data); + if (changed) { + PE_update_selection(depsgraph, scene, ob, 1); + WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob); + } - return true; + return changed || found; } /** \} */ diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc index 33b68dfb47b..e5d2a765ca1 100644 --- a/source/blender/editors/render/render_internal.cc +++ b/source/blender/editors/render/render_internal.cc @@ -31,6 +31,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" @@ -101,7 +102,7 @@ struct RenderJob { /* called inside thread! */ static bool image_buffer_calc_tile_rect(const RenderResult *rr, const ImBuf *ibuf, - volatile rcti *renrect, + rcti *renrect, rcti *r_ibuf_rect, int *r_offset_x, int *r_offset_y) @@ -355,7 +356,14 @@ static int screen_render_exec(bContext *C, wmOperator *op) scene->r.frame_step); } else { - RE_RenderFrame(re, mainp, scene, single_layer, camera_override, scene->r.cfra, is_write_still); + RE_RenderFrame(re, + mainp, + scene, + single_layer, + camera_override, + scene->r.cfra, + scene->r.subframe, + is_write_still); } RE_SetReports(re, nullptr); @@ -549,7 +557,7 @@ static void render_image_update_pass_and_layer(RenderJob *rj, RenderResult *rr, } } -static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect) +static void image_rect_update(void *rjv, RenderResult *rr, rcti *renrect) { RenderJob *rj = static_cast<RenderJob *>(rjv); Image *ima = rj->image; @@ -655,6 +663,7 @@ static void render_startjob(void *rjv, short *stop, short *do_update, float *pro rj->single_layer, rj->camera_override, rj->scene->r.cfra, + rj->scene->r.subframe, rj->write_still); } diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index be66e87f2e5..fbdc1086874 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -34,6 +34,8 @@ #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" +#include "BKE_image_save.h" #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_report.h" @@ -312,13 +314,13 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R imb_freerectfloatImBuf(out); } BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y)); - RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id); + RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id); IMB_freeImBuf(out); } else if (gpd) { /* If there are no strips, Grease Pencil still needs a buffer to draw on */ ImBuf *out = IMB_allocImBuf(oglrender->sizex, oglrender->sizey, 32, IB_rect); - RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id); + RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id); IMB_freeImBuf(out); } @@ -414,7 +416,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) { BKE_image_stamp_buf(scene, camera, nullptr, rect, rectf, rr->rectx, rr->recty, 4); } - RE_render_result_rect_from_ibuf(rr, &scene->r, ibuf_result, oglrender->view_id); + RE_render_result_rect_from_ibuf(rr, ibuf_result, oglrender->view_id); IMB_freeImBuf(ibuf_result); } } @@ -439,7 +441,7 @@ static void screen_opengl_render_write(OGLRender *oglrender) /* write images as individual images or stereo */ BKE_render_result_stamp_info(scene, scene->camera, rr, false); - ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, false, name); + ok = BKE_image_render_write(oglrender->reports, rr, scene, false, name); RE_ReleaseResultImage(oglrender->re); @@ -1070,7 +1072,7 @@ static void write_result_func(TaskPool *__restrict pool, void *task_data_v) nullptr); BKE_render_result_stamp_info(scene, scene->camera, rr, false); - ok = RE_WriteRenderViewsImage(nullptr, rr, scene, true, name); + ok = BKE_image_render_write(nullptr, rr, scene, true, name); if (!ok) { BKE_reportf(&reports, RPT_ERROR, "Write error: cannot save %s", name); } diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index cfb88cd7868..ef0f0b6225c 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -459,11 +459,11 @@ static Scene *preview_prepare_scene( if (sce) { ViewLayer *view_layer = static_cast<ViewLayer *>(sce->view_layers.first); - /* Only enable the combined renderpass */ + /* Only enable the combined render-pass. */ view_layer->passflag = SCE_PASS_COMBINED; view_layer->eevee.render_passes = 0; - /* this flag tells render to not execute depsgraph or ipos etc */ + /* This flag tells render to not execute depsgraph or F-Curves etc. */ sce->r.scemode |= R_BUTS_PREVIEW; BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine)); @@ -987,9 +987,7 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview * \{ */ /* inside thread, called by renderer, sets job update value */ -static void shader_preview_update(void *spv, - RenderResult *UNUSED(rr), - volatile struct rcti *UNUSED(rect)) +static void shader_preview_update(void *spv, RenderResult *UNUSED(rr), struct rcti *UNUSED(rect)) { ShaderPreview *sp = static_cast<ShaderPreview *>(spv); @@ -1681,19 +1679,19 @@ class PreviewLoadJob { PreviewLoadJob(); ~PreviewLoadJob(); - static PreviewLoadJob &ensure_job(wmWindowManager *, wmWindow *); - static void load_jobless(PreviewImage *, eIconSizes); + static PreviewLoadJob &ensure_job(wmWindowManager *wm, wmWindow *win); + static void load_jobless(PreviewImage *preview, eIconSizes icon_size); - void push_load_request(PreviewImage *, eIconSizes); + void push_load_request(PreviewImage *preview, eIconSizes icon_size); private: - static void run_fn(void *, short *, short *, float *); - static void update_fn(void *); - static void end_fn(void *); - static void free_fn(void *); + static void run_fn(void *customdata, short *stop, short *do_update, float *progress); + static void update_fn(void *customdata); + static void end_fn(void *customdata); + static void free_fn(void *customdata); /** Mark a single requested preview as being done, remove the request. */ - static void finish_request(RequestedPreview &); + static void finish_request(RequestedPreview &request); }; PreviewLoadJob::PreviewLoadJob() : todo_queue_(BLI_thread_queue_init()) diff --git a/source/blender/editors/scene/CMakeLists.txt b/source/blender/editors/scene/CMakeLists.txt index 7f687212066..12043ac2957 100644 --- a/source/blender/editors/scene/CMakeLists.txt +++ b/source/blender/editors/scene/CMakeLists.txt @@ -8,8 +8,8 @@ set(INC ../../depsgraph ../../makesdna ../../makesrna - ../../windowmanager ../../sequencer + ../../windowmanager ) set(INC_SYS diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index ff77f9910fb..5464d0a347d 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -24,6 +24,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_screen.h" @@ -71,7 +72,7 @@ static int screenshot_data_create(bContext *C, wmOperator *op, ScrArea *area) scd->crop = area->totrct; } - BKE_imformat_defaults(&scd->im_format); + BKE_image_format_init(&scd->im_format, false); op->customdata = scd; diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index ccbdb3c4145..c422c8c2033 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -13,8 +13,8 @@ set(INC ../../gpu ../../imbuf ../../makesdna - ../../nodes ../../makesrna + ../../nodes ../../render ../../windowmanager ../../../../intern/atomic @@ -27,15 +27,20 @@ set(INC ) set(SRC + curves_sculpt_3d_brush.cc + curves_sculpt_add.cc + curves_sculpt_comb.cc + curves_sculpt_delete.cc curves_sculpt_ops.cc + curves_sculpt_snake_hook.cc paint_cursor.c paint_curve.c paint_curve_undo.c paint_hide.c paint_image.cc - paint_image_ops_paint.cc paint_image_2d.c paint_image_2d_curve_mask.cc + paint_image_ops_paint.cc paint_image_proj.c paint_mask.c paint_ops.c @@ -72,6 +77,7 @@ set(SRC sculpt_uv.c curves_sculpt_intern.h + curves_sculpt_intern.hh paint_intern.h sculpt_intern.h ) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc new file mode 100644 index 00000000000..945bb09c0c6 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_curves.hh" + +#include "ED_view3d.h" + +#include "UI_interface.h" + +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_task.hh" + +/** + * The code below uses a prefix naming convention to indicate the coordinate space: + * cu: Local space of the curves object that is being edited. + * su: Local space of the surface object. + * wo: World space. + * re: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +struct BrushPositionCandidate { + /** 3D position of the brush. */ + float3 position_cu; + /** Squared distance from the mouse position in screen space. */ + float distance_sq_re = FLT_MAX; + /** Measure for how far away the candidate is from the camera. */ + float depth_sq_cu = FLT_MAX; +}; + +/** + * Determine the 3D position of a brush based on curve segments under a screen position. + */ +static std::optional<float3> find_curves_brush_position(const CurvesGeometry &curves, + const float3 &ray_start_cu, + const float3 &ray_end_cu, + const float brush_radius_re, + ARegion ®ion, + RegionView3D &rv3d, + Object &object) +{ + /* This value might have to be adjusted based on user feedback. */ + const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f); + const float brush_inner_radius_sq_re = pow2f(brush_inner_radius_re); + + float4x4 projection; + ED_view3d_ob_project_mat_get(&rv3d, &object, projection.values); + + float2 brush_pos_re; + ED_view3d_project_float_v2_m4(®ion, ray_start_cu, brush_pos_re, projection.values); + + const float max_depth_sq_cu = math::distance_squared(ray_start_cu, ray_end_cu); + + /* Contains the logic that checks if `b` is a better candidate than `a`. */ + auto is_better_candidate = [&](const BrushPositionCandidate &a, + const BrushPositionCandidate &b) { + if (b.distance_sq_re <= brush_inner_radius_sq_re) { + if (a.distance_sq_re > brush_inner_radius_sq_re) { + /* New candidate is in inner radius while old one is not. */ + return true; + } + if (b.depth_sq_cu < a.depth_sq_cu) { + /* Both candidates are in inner radius, but new one is closer to the camera. */ + return true; + } + } + else if (b.distance_sq_re < a.distance_sq_re) { + /* Both candidates are outside of inner radius, but new on is closer to the brush center. */ + return true; + } + return false; + }; + + auto update_if_better = [&](BrushPositionCandidate &a, const BrushPositionCandidate &b) { + if (is_better_candidate(a, b)) { + a = b; + } + }; + + const Span<float3> positions = curves.positions(); + + BrushPositionCandidate best_candidate = threading::parallel_reduce( + curves.curves_range(), + 128, + BrushPositionCandidate(), + [&](IndexRange curves_range, const BrushPositionCandidate &init) { + BrushPositionCandidate best_candidate = init; + + for (const int curve_i : curves_range) { + const IndexRange points = curves.points_for_curve(curve_i); + const int tot_segments = points.size() - 1; + + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1_cu = positions[points[segment_i]]; + const float3 &p2_cu = positions[points[segment_i] + 1]; + + float2 p1_re, p2_re; + ED_view3d_project_float_v2_m4(®ion, p1_cu, p1_re, projection.values); + ED_view3d_project_float_v2_m4(®ion, p2_cu, p2_re, projection.values); + + float2 closest_re; + const float lambda = closest_to_line_segment_v2( + closest_re, brush_pos_re, p1_re, p2_re); + + const float3 closest_cu = math::interpolate(p1_cu, p2_cu, lambda); + const float depth_sq_cu = math::distance_squared(ray_start_cu, closest_cu); + if (depth_sq_cu > max_depth_sq_cu) { + continue; + } + + const float distance_sq_re = math::distance_squared(brush_pos_re, closest_re); + + BrushPositionCandidate candidate; + candidate.position_cu = closest_cu; + candidate.depth_sq_cu = depth_sq_cu; + candidate.distance_sq_re = distance_sq_re; + + update_if_better(best_candidate, candidate); + } + } + return best_candidate; + }, + [&](const BrushPositionCandidate &a, const BrushPositionCandidate &b) { + return is_better_candidate(a, b) ? b : a; + }); + + if (best_candidate.distance_sq_re == FLT_MAX) { + /* Nothing found. */ + return std::nullopt; + } + + return best_candidate.position_cu; +} + +std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, + Object &curves_object, + const float2 &brush_pos_re, + const float brush_radius_re) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); + ARegion *region = CTX_wm_region(&C); + View3D *v3d = CTX_wm_view3d(&C); + RegionView3D *rv3d = CTX_wm_region_view3d(&C); + + Curves &curves_id = *static_cast<Curves *>(curves_object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + Object *surface_object = curves_id.surface; + + float3 center_ray_start_wo, center_ray_end_wo; + ED_view3d_win_to_segment_clipped( + depsgraph, region, v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); + + /* Shorten ray when the surface object is hit. */ + if (surface_object != nullptr) { + const float4x4 surface_to_world_mat = surface_object->obmat; + const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); + + Mesh &surface = *static_cast<Mesh *>(surface_object->data); + BVHTreeFromMesh surface_bvh; + BKE_bvhtree_from_mesh_get(&surface_bvh, &surface, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); + + const float3 center_ray_start_su = world_to_surface_mat * center_ray_start_wo; + float3 center_ray_end_su = world_to_surface_mat * center_ray_end_wo; + const float3 center_ray_direction_su = math::normalize(center_ray_end_su - + center_ray_start_su); + + BVHTreeRayHit center_ray_hit; + center_ray_hit.dist = FLT_MAX; + center_ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh.tree, + center_ray_start_su, + center_ray_direction_su, + 0.0f, + ¢er_ray_hit, + surface_bvh.raycast_callback, + &surface_bvh); + if (center_ray_hit.index >= 0) { + const float3 hit_position_su = center_ray_hit.co; + if (math::distance(center_ray_start_su, center_ray_end_su) > + math::distance(center_ray_start_su, hit_position_su)) { + center_ray_end_su = hit_position_su; + center_ray_end_wo = surface_to_world_mat * center_ray_end_su; + } + } + } + + const float4x4 curves_to_world_mat = curves_object.obmat; + const float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); + + const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo; + const float3 center_ray_end_cu = world_to_curves_mat * center_ray_end_wo; + + const std::optional<float3> brush_position_optional_cu = find_curves_brush_position( + curves, + center_ray_start_cu, + center_ray_end_cu, + brush_radius_re, + *region, + *rv3d, + curves_object); + if (!brush_position_optional_cu.has_value()) { + /* Nothing found. */ + return std::nullopt; + } + const float3 brush_position_cu = *brush_position_optional_cu; + + /* Determine the 3D brush radius. */ + float3 radius_ray_start_wo, radius_ray_end_wo; + ED_view3d_win_to_segment_clipped(depsgraph, + region, + v3d, + brush_pos_re + float2(brush_radius_re, 0.0f), + radius_ray_start_wo, + radius_ray_end_wo, + true); + const float3 radius_ray_start_cu = world_to_curves_mat * radius_ray_start_wo; + const float3 radius_ray_end_cu = world_to_curves_mat * radius_ray_end_wo; + + CurvesBrush3D brush_3d; + brush_3d.position_cu = brush_position_cu; + brush_3d.radius_cu = dist_to_line_v3(brush_position_cu, radius_ray_start_cu, radius_ray_end_cu); + return brush_3d; +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc new file mode 100644 index 00000000000..809511d0106 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -0,0 +1,792 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float4x4.hh" +#include "BLI_kdtree.h" +#include "BLI_rand.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_paint.h" +#include "BKE_spline.hh" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +/** + * The code below uses a prefix naming convention to indicate the coordinate space: + * cu: Local space of the curves object that is being edited. + * su: Local space of the surface object. + * wo: World space. + * re: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +using bke::CurvesGeometry; + +class AddOperation : public CurvesSculptStrokeOperation { + private: + /** Used when some data should be interpolated from existing curves. */ + KDTree_3d *curve_roots_kdtree_ = nullptr; + + friend struct AddOperationExecutor; + + public: + ~AddOperation() override + { + if (curve_roots_kdtree_ != nullptr) { + BLI_kdtree_3d_free(curve_roots_kdtree_); + } + } + + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; +}; + +static void initialize_straight_curve_positions(const float3 &p1, + const float3 &p2, + MutableSpan<float3> r_positions) +{ + const float step = 1.0f / (float)(r_positions.size() - 1); + for (const int i : r_positions.index_range()) { + r_positions[i] = math::interpolate(p1, p2, i * step); + } +} + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct AddOperationExecutor { + AddOperation *self_ = nullptr; + Depsgraph *depsgraph_ = nullptr; + Scene *scene_ = nullptr; + Object *object_ = nullptr; + ARegion *region_ = nullptr; + View3D *v3d_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + Object *surface_ob_ = nullptr; + Mesh *surface_ = nullptr; + Span<MLoopTri> surface_looptris_; + Span<float3> corner_normals_su_; + + CurvesSculpt *curves_sculpt_ = nullptr; + Brush *brush_ = nullptr; + + float brush_radius_re_; + float2 brush_pos_re_; + + bool use_front_face_; + bool interpolate_length_; + bool interpolate_shape_; + bool use_interpolation_; + float new_curve_length_; + int add_amount_; + int points_per_curve_ = 8; + + /** Various matrices to convert between coordinate spaces. */ + float4x4 curves_to_world_mat_; + float4x4 world_to_curves_mat_; + float4x4 world_to_surface_mat_; + float4x4 surface_to_world_mat_; + float4x4 surface_to_curves_mat_; + float4x4 surface_to_curves_normal_mat_; + + BVHTreeFromMesh surface_bvh_; + + int tot_old_curves_; + int tot_old_points_; + + struct AddedPoints { + Vector<float3> positions_cu; + Vector<float3> bary_coords; + Vector<int> looptri_indices; + }; + + void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) + { + self_ = &self; + depsgraph_ = CTX_data_depsgraph_pointer(C); + scene_ = CTX_data_scene(C); + object_ = CTX_data_active_object(C); + region_ = CTX_wm_region(C); + v3d_ = CTX_wm_view3d(C); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + + if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + return; + } + + curves_to_world_mat_ = object_->obmat; + world_to_curves_mat_ = curves_to_world_mat_.inverted(); + + surface_ob_ = curves_id_->surface; + surface_ = static_cast<Mesh *>(surface_ob_->data); + surface_to_world_mat_ = surface_ob_->obmat; + world_to_surface_mat_ = surface_to_world_mat_.inverted(); + surface_to_curves_mat_ = world_to_curves_mat_ * surface_to_world_mat_; + surface_to_curves_normal_mat_ = surface_to_curves_mat_.inverted().transposed(); + + if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_); + } + corner_normals_su_ = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), + surface_->totloop}; + + curves_sculpt_ = scene_->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_pos_re_ = stroke_extension.mouse_position; + + use_front_face_ = brush_->flag & BRUSH_FRONTFACE; + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + add_amount_ = std::max(0, brush_->curves_sculpt_settings->add_amount); + interpolate_length_ = curves_sculpt_->flag & CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; + interpolate_shape_ = curves_sculpt_->flag & CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; + use_interpolation_ = interpolate_length_ || interpolate_shape_; + new_curve_length_ = curves_sculpt_->curve_length; + + tot_old_curves_ = curves_->curves_num(); + tot_old_points_ = curves_->points_num(); + + if (add_amount_ == 0) { + return; + } + + RandomNumberGenerator rng{(uint32_t)(PIL_check_seconds_timer() * 1000000.0f)}; + + BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); + + surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), + BKE_mesh_runtime_looptri_len(surface_)}; + + /* Sample points on the surface using one of multiple strategies. */ + AddedPoints added_points; + if (add_amount_ == 1) { + this->sample_in_center(added_points); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->sample_projected(rng, added_points); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->sample_spherical(rng, added_points); + } + else { + BLI_assert_unreachable(); + } + + if (added_points.bary_coords.is_empty()) { + /* No new points have been added. */ + return; + } + + if (use_interpolation_) { + this->ensure_curve_roots_kdtree(); + } + + const int tot_added_curves = added_points.bary_coords.size(); + const int tot_added_points = tot_added_curves * points_per_curve_; + + curves_->resize(curves_->points_num() + tot_added_points, + curves_->curves_num() + tot_added_curves); + + threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, + [&]() { this->initialize_attributes(added_points); }); + + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region_); + } + + float3 get_bary_coords(const Mesh &mesh, const MLoopTri &looptri, const float3 position) const + { + const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co; + const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co; + const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co; + float3 bary_coords; + interp_weights_tri_v3(bary_coords, v0, v1, v2, position); + return bary_coords; + } + + /** + * Sample a single point exactly at the mouse position. + */ + void sample_in_center(AddedPoints &r_added_points) + { + float3 ray_start_wo, ray_end_wo; + ED_view3d_win_to_segment_clipped( + depsgraph_, region_, v3d_, brush_pos_re_, ray_start_wo, ray_end_wo, true); + const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; + const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); + + BVHTreeRayHit ray_hit; + ray_hit.dist = FLT_MAX; + ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh_.tree, + ray_start_su, + ray_direction_su, + 0.0f, + &ray_hit, + surface_bvh_.raycast_callback, + &surface_bvh_); + + if (ray_hit.index == -1) { + return; + } + + const int looptri_index = ray_hit.index; + const float3 brush_pos_su = ray_hit.co; + const float3 bary_coords = this->get_bary_coords( + *surface_, surface_looptris_[looptri_index], brush_pos_su); + + const float3 brush_pos_cu = surface_to_curves_mat_ * brush_pos_su; + + r_added_points.positions_cu.append(brush_pos_cu); + r_added_points.bary_coords.append(bary_coords); + r_added_points.looptri_indices.append(looptri_index); + } + + /** + * Sample points by shooting rays within the brush radius in the 3D view. + */ + void sample_projected(RandomNumberGenerator &rng, AddedPoints &r_added_points) + { + const int max_iterations = std::max(100'000, add_amount_ * 10); + int current_iteration = 0; + while (r_added_points.bary_coords.size() < add_amount_) { + if (current_iteration++ >= max_iterations) { + break; + } + + const float r = brush_radius_re_ * std::sqrt(rng.get_float()); + const float angle = rng.get_float() * 2.0f * M_PI; + const float2 pos_re = brush_pos_re_ + r * float2(std::cos(angle), std::sin(angle)); + + float3 ray_start_wo, ray_end_wo; + ED_view3d_win_to_segment_clipped( + depsgraph_, region_, v3d_, pos_re, ray_start_wo, ray_end_wo, true); + const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; + const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); + + BVHTreeRayHit ray_hit; + ray_hit.dist = FLT_MAX; + ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh_.tree, + ray_start_su, + ray_direction_su, + 0.0f, + &ray_hit, + surface_bvh_.raycast_callback, + &surface_bvh_); + + if (ray_hit.index == -1) { + continue; + } + + if (use_front_face_) { + const float3 normal_su = ray_hit.no; + if (math::dot(ray_direction_su, normal_su) >= 0.0f) { + continue; + } + } + + const int looptri_index = ray_hit.index; + const float3 pos_su = ray_hit.co; + + const float3 bary_coords = this->get_bary_coords( + *surface_, surface_looptris_[looptri_index], pos_su); + + const float3 pos_cu = surface_to_curves_mat_ * pos_su; + + r_added_points.positions_cu.append(pos_cu); + r_added_points.bary_coords.append(bary_coords); + r_added_points.looptri_indices.append(looptri_index); + } + } + + /** + * Sample points in a 3D sphere around the surface position that the mouse hovers over. + */ + void sample_spherical(RandomNumberGenerator &rng, AddedPoints &r_added_points) + { + /* Find ray that starts in the center of the brush. */ + float3 brush_ray_start_wo, brush_ray_end_wo; + ED_view3d_win_to_segment_clipped( + depsgraph_, region_, v3d_, brush_pos_re_, brush_ray_start_wo, brush_ray_end_wo, true); + const float3 brush_ray_start_su = world_to_surface_mat_ * brush_ray_start_wo; + const float3 brush_ray_end_su = world_to_surface_mat_ * brush_ray_end_wo; + const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); + + /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius + * in 3D. */ + float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo; + ED_view3d_win_to_segment_clipped(depsgraph_, + region_, + v3d_, + brush_pos_re_ + float2(brush_radius_re_, 0), + brush_radius_ray_start_wo, + brush_radius_ray_end_wo, + true); + const float3 brush_radius_ray_start_su = world_to_surface_mat_ * brush_radius_ray_start_wo; + const float3 brush_radius_ray_end_su = world_to_surface_mat_ * brush_radius_ray_end_wo; + + BVHTreeRayHit ray_hit; + ray_hit.dist = FLT_MAX; + ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh_.tree, + brush_ray_start_su, + brush_ray_direction_su, + 0.0f, + &ray_hit, + surface_bvh_.raycast_callback, + &surface_bvh_); + + if (ray_hit.index == -1) { + return; + } + + /* Compute brush radius. */ + const float3 brush_pos_su = ray_hit.co; + const float brush_radius_su = dist_to_line_v3( + brush_pos_su, brush_radius_ray_start_su, brush_radius_ray_end_su); + const float brush_radius_sq_su = pow2f(brush_radius_su); + + /* Find surface triangles within brush radius. */ + Vector<int> looptri_indices; + if (use_front_face_) { + BLI_bvhtree_range_query_cpp( + *surface_bvh_.tree, + brush_pos_su, + brush_radius_su, + [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { + const MLoopTri &looptri = surface_looptris_[index]; + const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; + const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; + const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; + float3 normal_su; + normal_tri_v3(normal_su, v0_su, v1_su, v2_su); + if (math::dot(normal_su, brush_ray_direction_su) >= 0.0f) { + return; + } + looptri_indices.append(index); + }); + } + else { + BLI_bvhtree_range_query_cpp( + *surface_bvh_.tree, + brush_pos_su, + brush_radius_su, + [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { + looptri_indices.append(index); + }); + } + + /* Density used for sampling points. This does not have to be exact, because the loop below + * automatically runs until enough samples have been found. If too many samples are found, some + * will be discarded afterwards. */ + const float brush_plane_area_su = M_PI * brush_radius_sq_su; + const float approximate_density_su = add_amount_ / brush_plane_area_su; + + /* Used for switching between two triangle sampling strategies. */ + const float area_threshold = brush_plane_area_su; + + /* Usually one or two iterations should be enough. */ + const int max_iterations = 5; + int current_iteration = 0; + + while (r_added_points.bary_coords.size() < add_amount_) { + if (current_iteration++ >= max_iterations) { + break; + } + + for (const int looptri_index : looptri_indices) { + const MLoopTri &looptri = surface_looptris_[looptri_index]; + + const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; + const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; + const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; + + const float looptri_area_su = area_tri_v3(v0_su, v1_su, v2_su); + + if (looptri_area_su < area_threshold) { + /* The triangle is small compared to the brush radius. Sample by generating random + * barycentric coordinates. */ + const int amount = rng.round_probabilistic(approximate_density_su * looptri_area_su); + for ([[maybe_unused]] const int i : IndexRange(amount)) { + const float3 bary_coord = rng.get_barycentric_coordinates(); + const float3 point_pos_su = attribute_math::mix3(bary_coord, v0_su, v1_su, v2_su); + const float distance_to_brush_sq_su = math::distance_squared(point_pos_su, + brush_pos_su); + if (distance_to_brush_sq_su > brush_radius_sq_su) { + continue; + } + + r_added_points.bary_coords.append(bary_coord); + r_added_points.looptri_indices.append(looptri_index); + r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su); + } + } + else { + /* The triangle is large compared to the brush radius. Sample by generating random points + * on the triangle plane within the brush radius. */ + float3 normal_su; + normal_tri_v3(normal_su, v0_su, v1_su, v2_su); + + float3 brush_pos_proj_su = brush_pos_su; + project_v3_plane(brush_pos_proj_su, normal_su, v0_su); + + const float proj_distance_sq_su = math::distance_squared(brush_pos_proj_su, + brush_pos_su); + const float brush_radius_factor_sq = 1.0f - + std::min(1.0f, + proj_distance_sq_su / brush_radius_sq_su); + const float radius_proj_sq_su = brush_radius_sq_su * brush_radius_factor_sq; + const float radius_proj_su = std::sqrt(radius_proj_sq_su); + const float circle_area_su = M_PI * radius_proj_su; + + const int amount = rng.round_probabilistic(approximate_density_su * circle_area_su); + + const float3 axis_1_su = math::normalize(v1_su - v0_su) * radius_proj_su; + const float3 axis_2_su = math::normalize(math::cross( + axis_1_su, math::cross(axis_1_su, v2_su - v0_su))) * + radius_proj_su; + + for ([[maybe_unused]] const int i : IndexRange(amount)) { + const float r = std::sqrt(rng.get_float()); + const float angle = rng.get_float() * 2.0f * M_PI; + const float x = r * std::cos(angle); + const float y = r * std::sin(angle); + const float3 point_pos_su = brush_pos_proj_su + axis_1_su * x + axis_2_su * y; + if (!isect_point_tri_prism_v3(point_pos_su, v0_su, v1_su, v2_su)) { + /* Sampled point is not in the triangle. */ + continue; + } + + float3 bary_coord; + interp_weights_tri_v3(bary_coord, v0_su, v1_su, v2_su, point_pos_su); + + r_added_points.bary_coords.append(bary_coord); + r_added_points.looptri_indices.append(looptri_index); + r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su); + } + } + } + } + + /* Remove samples when there are too many. */ + while (r_added_points.bary_coords.size() > add_amount_) { + const int index_to_remove = rng.get_int32(r_added_points.bary_coords.size()); + r_added_points.bary_coords.remove_and_reorder(index_to_remove); + r_added_points.looptri_indices.remove_and_reorder(index_to_remove); + r_added_points.positions_cu.remove_and_reorder(index_to_remove); + } + } + + void ensure_curve_roots_kdtree() + { + if (self_->curve_roots_kdtree_ == nullptr) { + self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num()); + for (const int curve_i : curves_->curves_range()) { + const int root_point_i = curves_->offsets()[curve_i]; + const float3 &root_pos_cu = curves_->positions()[root_point_i]; + BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu); + } + BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); + } + } + + void initialize_curve_offsets(const int tot_added_curves) + { + MutableSpan<int> offsets = curves_->offsets(); + threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { + for (const int i : range) { + const int curve_i = tot_old_curves_ + i; + offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; + } + }); + } + + struct NeighborInfo { + /* Curve index of the neighbor. */ + int index; + /* The weights of all neighbors of a new curve add up to 1. */ + float weight; + }; + static constexpr int max_neighbors = 5; + using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + + void initialize_attributes(const AddedPoints &added_points) + { + Array<NeighborsVector> neighbors_per_curve; + if (use_interpolation_) { + neighbors_per_curve = this->find_curve_neighbors(added_points); + } + + Array<float> new_lengths_cu(added_points.bary_coords.size()); + if (interpolate_length_) { + this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); + } + else { + new_lengths_cu.fill(new_curve_length_); + } + + Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points); + this->initialize_surface_attachment(added_points); + + if (interpolate_shape_) { + this->initialize_position_with_interpolation( + added_points, neighbors_per_curve, new_normals_su, new_lengths_cu); + } + else { + this->initialize_position_without_interpolation( + added_points, new_lengths_cu, new_normals_su); + } + } + + Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points) + { + const int tot_added_curves = added_points.bary_coords.size(); + Array<NeighborsVector> neighbors_per_curve(tot_added_curves); + threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) { + for (const int i : range) { + const float3 root_cu = added_points.positions_cu[i]; + std::array<KDTreeNearest_3d, max_neighbors> nearest_n; + const int found_neighbors = BLI_kdtree_3d_find_nearest_n( + self_->curve_roots_kdtree_, root_cu, nearest_n.data(), max_neighbors); + float tot_weight = 0.0f; + for (const int neighbor_i : IndexRange(found_neighbors)) { + KDTreeNearest_3d &nearest = nearest_n[neighbor_i]; + const float weight = 1.0f / std::max(nearest.dist, 0.00001f); + tot_weight += weight; + neighbors_per_curve[i].append({nearest.index, weight}); + } + /* Normalize weights. */ + for (NeighborInfo &neighbor : neighbors_per_curve[i]) { + neighbor.weight /= tot_weight; + } + } + }); + return neighbors_per_curve; + } + + void interpolate_lengths(const Span<NeighborsVector> neighbors_per_curve, + MutableSpan<float> r_lengths) + { + const Span<float3> positions_cu = curves_->positions(); + + threading::parallel_for(r_lengths.index_range(), 128, [&](const IndexRange range) { + for (const int added_curve_i : range) { + const Span<NeighborInfo> neighbors = neighbors_per_curve[added_curve_i]; + float length_sum = 0.0f; + for (const NeighborInfo &neighbor : neighbors) { + const IndexRange neighbor_points = curves_->points_for_curve(neighbor.index); + float neighbor_length = 0.0f; + const int tot_segments = neighbor_points.size() - 1; + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1 = positions_cu[neighbor_points[segment_i]]; + const float3 &p2 = positions_cu[neighbor_points[segment_i] + 1]; + neighbor_length += math::distance(p1, p2); + } + length_sum += neighbor.weight * neighbor_length; + } + const float length = neighbors.is_empty() ? new_curve_length_ : length_sum; + r_lengths[added_curve_i] = length; + } + }); + } + + float3 compute_point_normal_su(const int looptri_index, const float3 &bary_coord) + { + const MLoopTri &looptri = surface_looptris_[looptri_index]; + const int l0 = looptri.tri[0]; + const int l1 = looptri.tri[1]; + const int l2 = looptri.tri[2]; + + const float3 &l0_normal_su = corner_normals_su_[l0]; + const float3 &l1_normal_su = corner_normals_su_[l1]; + const float3 &l2_normal_su = corner_normals_su_[l2]; + + const float3 normal_su = math::normalize( + attribute_math::mix3(bary_coord, l0_normal_su, l1_normal_su, l2_normal_su)); + return normal_su; + } + + Array<float3> compute_normals_for_added_curves_su(const AddedPoints &added_points) + { + Array<float3> normals_su(added_points.bary_coords.size()); + threading::parallel_for(normals_su.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int looptri_index = added_points.looptri_indices[i]; + const float3 &bary_coord = added_points.bary_coords[i]; + normals_su[i] = this->compute_point_normal_su(looptri_index, bary_coord); + } + }); + return normals_su; + } + + void initialize_surface_attachment(const AddedPoints &added_points) + { + MutableSpan<int> surface_triangle_indices = curves_->surface_triangle_indices(); + MutableSpan<float2> surface_triangle_coords = curves_->surface_triangle_coords(); + threading::parallel_for( + added_points.bary_coords.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + const int curve_i = tot_old_curves_ + i; + surface_triangle_indices[curve_i] = added_points.looptri_indices[i]; + surface_triangle_coords[curve_i] = float2(added_points.bary_coords[i]); + } + }); + } + + /** + * Initialize new curves so that they are just a straight line in the normal direction. + */ + void initialize_position_without_interpolation(const AddedPoints &added_points, + const Span<float> lengths_cu, + const MutableSpan<float3> normals_su) + { + MutableSpan<float3> positions_cu = curves_->positions(); + + threading::parallel_for( + added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int first_point_i = tot_old_points_ + i * points_per_curve_; + const float3 &root_cu = added_points.positions_cu[i]; + const float length = lengths_cu[i]; + const float3 &normal_su = normals_su[i]; + const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); + const float3 tip_cu = root_cu + length * normal_cu; + + initialize_straight_curve_positions( + root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + } + }); + } + + /** + * Use neighboring curves to determine the shape. + */ + void initialize_position_with_interpolation(const AddedPoints &added_points, + const Span<NeighborsVector> neighbors_per_curve, + const Span<float3> new_normals_su, + const Span<float> new_lengths_cu) + { + MutableSpan<float3> positions_cu = curves_->positions(); + const Span<int> surface_triangle_indices = curves_->surface_triangle_indices(); + const Span<float2> surface_triangle_coords = curves_->surface_triangle_coords(); + + threading::parallel_for( + added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; + + const float length_cu = new_lengths_cu[i]; + const float3 &normal_su = new_normals_su[i]; + const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); + + const float3 &root_cu = added_points.positions_cu[i]; + const int first_point_i = tot_old_points_ + i * points_per_curve_; + + if (neighbors.is_empty()) { + /* If there are no neighbors, just make a straight line. */ + const float3 tip_cu = root_cu + length_cu * normal_cu; + initialize_straight_curve_positions( + root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + continue; + } + + positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); + + for (const NeighborInfo &neighbor : neighbors) { + const int neighbor_curve_i = neighbor.index; + const int neighbor_looptri_index = surface_triangle_indices[neighbor_curve_i]; + + float3 neighbor_bary_coord{surface_triangle_coords[neighbor_curve_i]}; + neighbor_bary_coord.z = 1.0f - neighbor_bary_coord.x - neighbor_bary_coord.y; + + const float3 neighbor_normal_su = this->compute_point_normal_su( + neighbor_looptri_index, neighbor_bary_coord); + const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat_ * + neighbor_normal_su); + + /* The rotation matrix used to transform relative coordinates of the neighbor curve + * to the new curve. */ + float normal_rotation_cu[3][3]; + rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu); + + const IndexRange neighbor_points = curves_->points_for_curve(neighbor_curve_i); + const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; + + /* Use a temporary #PolySpline, because that's the easiest way to resample an + * existing curve right now. Resampling is necessary if the length of the new curve + * does not match the length of the neighbors or the number of handle points is + * different. */ + PolySpline neighbor_spline; + neighbor_spline.resize(neighbor_points.size()); + neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); + neighbor_spline.mark_cache_invalid(); + + const float neighbor_length_cu = neighbor_spline.length(); + const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); + + const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; + for (const int j : IndexRange(points_per_curve_)) { + const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( + j * resample_factor); + const float index_factor = lookup.evaluated_index + lookup.factor; + float3 p; + neighbor_spline.sample_with_index_factors<float3>( + neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); + const float3 relative_coord = p - neighbor_root_cu; + float3 rotated_relative_coord = relative_coord; + mul_m3_v3(normal_rotation_cu, rotated_relative_coord); + positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; + } + } + } + }); + } +}; + +void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +{ + AddOperationExecutor executor; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation() +{ + return std::make_unique<AddOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc new file mode 100644 index 00000000000..d062fe32cfe --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -0,0 +1,377 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float4x4.hh" +#include "BLI_index_mask_ops.hh" +#include "BLI_kdtree.h" +#include "BLI_rand.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_paint.h" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "UI_interface.h" + +/** + * The code below uses a prefix naming convention to indicate the coordinate space: + * cu: Local space of the curves object that is being edited. + * su: Local space of the surface object. + * wo: World space. + * re: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +using blender::bke::CurvesGeometry; +using threading::EnumerableThreadSpecific; + +/** + * Moves individual points under the brush and does a length preservation step afterwards. + */ +class CombOperation : public CurvesSculptStrokeOperation { + private: + /** Last mouse position. */ + float2 brush_pos_last_re_; + + /** Only used when a 3D brush is used. */ + CurvesBrush3D brush_3d_; + + /** Length of each segment indexed by the index of the first point in the segment. */ + Array<float> segment_lengths_cu_; + + friend struct CombOperationExecutor; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct CombOperationExecutor { + CombOperation *self_ = nullptr; + bContext *C_ = nullptr; + Depsgraph *depsgraph_ = nullptr; + Scene *scene_ = nullptr; + Object *object_ = nullptr; + ARegion *region_ = nullptr; + View3D *v3d_ = nullptr; + RegionView3D *rv3d_ = nullptr; + + CurvesSculpt *curves_sculpt_ = nullptr; + Brush *brush_ = nullptr; + float brush_radius_re_; + float brush_strength_; + + eBrushFalloffShape falloff_shape_; + + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + const Object *surface_ob_ = nullptr; + const Mesh *surface_ = nullptr; + Span<MLoopTri> surface_looptris_; + + float2 brush_pos_prev_re_; + float2 brush_pos_re_; + float2 brush_pos_diff_re_; + float brush_pos_diff_length_re_; + + float4x4 curves_to_world_mat_; + float4x4 world_to_curves_mat_; + float4x4 surface_to_world_mat_; + float4x4 world_to_surface_mat_; + + BVHTreeFromMesh surface_bvh_; + + void execute(CombOperation &self, bContext *C, const StrokeExtension &stroke_extension) + { + self_ = &self; + + BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; }); + + C_ = C; + depsgraph_ = CTX_data_depsgraph_pointer(C); + scene_ = CTX_data_scene(C); + object_ = CTX_data_active_object(C); + region_ = CTX_wm_region(C); + v3d_ = CTX_wm_view3d(C); + rv3d_ = CTX_wm_region_view3d(C); + + curves_sculpt_ = scene_->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + + curves_to_world_mat_ = object_->obmat; + world_to_curves_mat_ = curves_to_world_mat_.inverted(); + + falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + + brush_pos_prev_re_ = self_->brush_pos_last_re_; + brush_pos_re_ = stroke_extension.mouse_position; + brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; + brush_pos_diff_length_re_ = math::length(brush_pos_diff_re_); + + surface_ob_ = curves_id_->surface; + if (surface_ob_ != nullptr) { + surface_ = static_cast<const Mesh *>(surface_ob_->data); + surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), + BKE_mesh_runtime_looptri_len(surface_)}; + surface_to_world_mat_ = surface_ob_->obmat; + world_to_surface_mat_ = surface_to_world_mat_.inverted(); + BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); + } + + BLI_SCOPED_DEFER([&]() { + if (surface_ob_ != nullptr) { + free_bvhtree_from_mesh(&surface_bvh_); + } + }); + + if (stroke_extension.is_first) { + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + this->initialize_spherical_brush_reference_point(); + } + this->initialize_segment_lengths(); + /* Combing does nothing when there is no mouse movement, so return directly. */ + return; + } + + EnumerableThreadSpecific<Vector<int>> changed_curves; + + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { + this->comb_projected(changed_curves); + } + else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + this->comb_spherical(changed_curves); + } + else { + BLI_assert_unreachable(); + } + + this->restore_segment_lengths(changed_curves); + + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region_); + } + + /** + * Do combing in screen space. + */ + void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + { + MutableSpan<float3> positions_cu = curves_->positions(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + const float brush_radius_sq_re = pow2f(brush_radius_re_); + + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + Vector<int> &local_changed_curves = r_changed_curves.local(); + for (const int curve_i : curves_range) { + bool curve_changed = false; + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 old_pos_cu = positions_cu[point_i]; + + /* Find the position of the point in screen space. */ + float2 old_pos_re; + ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values); + + const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2( + old_pos_re, brush_pos_prev_re_, brush_pos_re_); + if (distance_to_brush_sq_re > brush_radius_sq_re) { + /* Ignore the point because it's too far away. */ + continue; + } + + const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re); + /* A falloff that is based on how far away the point is from the stroke. */ + const float radius_falloff = BKE_brush_curve_strength( + brush_, distance_to_brush_re, brush_radius_re_); + /* Combine the falloff and brush strength. */ + const float weight = brush_strength_ * radius_falloff; + + /* Offset the old point position in screen space and transform it back into 3D space. */ + const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; + float3 new_position_wo; + ED_view3d_win_to_3d( + v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); + const float3 new_position_cu = world_to_curves_mat_ * new_position_wo; + positions_cu[point_i] = new_position_cu; + + curve_changed = true; + } + if (curve_changed) { + local_changed_curves.append(curve_i); + } + } + }); + } + + /** + * Do combing in 3D space. + */ + void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + { + MutableSpan<float3> positions_cu = curves_->positions(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + float3 brush_start_wo, brush_end_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_prev_re_, + brush_start_wo); + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_end_wo); + const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; + const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + + const float3 brush_diff_cu = brush_end_cu - brush_start_cu; + + const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + Vector<int> &local_changed_curves = r_changed_curves.local(); + for (const int curve_i : curves_range) { + bool curve_changed = false; + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 pos_old_cu = positions_cu[point_i]; + + /* Compute distance to the brush. */ + const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( + pos_old_cu, brush_start_cu, brush_end_cu); + if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + /* Ignore the point because it's too far away. */ + continue; + } + + const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu); + + /* A falloff that is based on how far away the point is from the stroke. */ + const float radius_falloff = BKE_brush_curve_strength( + brush_, distance_to_brush_cu, brush_radius_cu); + /* Combine the falloff and brush strength. */ + const float weight = brush_strength_ * radius_falloff; + + /* Update the point position. */ + positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu; + curve_changed = true; + } + if (curve_changed) { + local_changed_curves.append(curve_i); + } + } + }); + } + + /** + * Sample depth under mouse by looking at curves and the surface. + */ + void initialize_spherical_brush_reference_point() + { + std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( + *C_, *object_, brush_pos_re_, brush_radius_re_); + if (brush_3d.has_value()) { + self_->brush_3d_ = *brush_3d; + } + } + + /** + * Remember the initial length of all curve segments. This allows restoring the length after + * combing. + */ + void initialize_segment_lengths() + { + const Span<float3> positions_cu = curves_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_->points_num()); + threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[point_i]; + const float3 &p2_cu = positions_cu[point_i + 1]; + const float length_cu = math::distance(p1_cu, p2_cu); + self_->segment_lengths_cu_[point_i] = length_cu; + } + } + }); + } + + /** + * Restore previously stored length for each segment in the changed curves. + */ + void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves) + { + const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; + MutableSpan<float3> positions_cu = curves_->positions(); + + threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) { + threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : changed_curves.as_span().slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(points.size() - 1)) { + const float3 &p1_cu = positions_cu[points[segment_i]]; + float3 &p2_cu = positions_cu[points[segment_i] + 1]; + const float3 direction = math::normalize(p2_cu - p1_cu); + const float expected_length_cu = expected_lengths_cu[points[segment_i]]; + p2_cu = p1_cu + direction * expected_length_cu; + } + } + }); + }); + } +}; + +void CombOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +{ + CombOperationExecutor executor; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation() +{ + return std::make_unique<CombOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc new file mode 100644 index 00000000000..ae87f414dd5 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float4x4.hh" +#include "BLI_index_mask_ops.hh" +#include "BLI_kdtree.h" +#include "BLI_rand.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_paint.h" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +namespace blender::ed::sculpt_paint { + +using blender::bke::CurvesGeometry; + +class DeleteOperation : public CurvesSculptStrokeOperation { + private: + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override + { + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast<Curves *>(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan<float3> positions = curves.positions(); + + const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position : + last_mouse_position_; + const float2 mouse_end = stroke_extension.mouse_position; + + /* Find indices of curves that have to be removed. */ + Vector<int64_t> indices; + const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( + curves.curves_range(), 512, indices, [&](const int curve_i) { + const IndexRange point_range = curves.points_for_curve(curve_i); + for (const int segment_i : IndexRange(point_range.size() - 1)) { + const float3 pos1 = positions[point_range[segment_i]]; + const float3 pos2 = positions[point_range[segment_i + 1]]; + + float2 pos1_proj, pos2_proj; + ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values); + ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values); + + const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end); + if (dist <= brush_radius) { + return true; + } + } + return false; + }); + + curves.remove_curves(curves_to_remove); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + + last_mouse_position_ = stroke_extension.mouse_position; + } +}; + +std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation() +{ + return std::make_unique<DeleteOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh new file mode 100644 index 00000000000..d021627921f --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <optional> + +#include "curves_sculpt_intern.h" + +#include "BLI_math_vector.hh" + +#include "BKE_curves.hh" + +struct ARegion; +struct RegionView3D; +struct Object; + +namespace blender::ed::sculpt_paint { + +using bke::CurvesGeometry; + +struct StrokeExtension { + bool is_first; + float2 mouse_position; +}; + +/** + * Base class for stroke based operations in curves sculpt mode. + */ +class CurvesSculptStrokeOperation { + public: + virtual ~CurvesSculptStrokeOperation() = default; + virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0; +}; + +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); + +struct CurvesBrush3D { + float3 position_cu; + float radius_cu; +}; + +/** + * Find 3d brush position based on cursor position for curves sculpting. + */ +std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, + Object &curves_object, + const float2 &brush_pos_re, + float brush_radius_re); + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 63202f3902a..382f0529daa 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -40,6 +40,7 @@ #include "PIL_time.h" #include "curves_sculpt_intern.h" +#include "curves_sculpt_intern.hh" #include "paint_intern.h" /* -------------------------------------------------------------------- */ @@ -68,318 +69,11 @@ bool CURVES_SCULPT_mode_poll_view3d(bContext *C) namespace blender::ed::sculpt_paint { using blender::bke::CurvesGeometry; -using blender::fn::CPPType; /* -------------------------------------------------------------------- */ /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ -struct StrokeExtension { - bool is_first; - float2 mouse_position; -}; - -/** - * Base class for stroke based operations in curves sculpt mode. - */ -class CurvesSculptStrokeOperation { - public: - virtual ~CurvesSculptStrokeOperation() = default; - virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0; -}; - -class DeleteOperation : public CurvesSculptStrokeOperation { - private: - float2 last_mouse_position_; - - public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override - { - Scene &scene = *CTX_data_scene(C); - Object &object = *CTX_data_active_object(C); - ARegion *region = CTX_wm_region(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - - CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; - Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); - const float brush_radius = BKE_brush_size_get(&scene, &brush); - - float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); - - Curves &curves_id = *static_cast<Curves *>(object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - MutableSpan<float3> positions = curves.positions(); - - const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position : - last_mouse_position_; - const float2 mouse_end = stroke_extension.mouse_position; - - /* Find indices of curves that have to be removed. */ - Vector<int64_t> indices; - const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( - curves.curves_range(), 512, indices, [&](const int curve_i) { - const IndexRange point_range = curves.range_for_curve(curve_i); - for (const int segment_i : IndexRange(point_range.size() - 1)) { - const float3 pos1 = positions[point_range[segment_i]]; - const float3 pos2 = positions[point_range[segment_i + 1]]; - - float2 pos1_proj, pos2_proj; - ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values); - ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values); - - const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end); - if (dist <= brush_radius) { - return true; - } - } - return false; - }); - - curves.remove_curves(curves_to_remove); - - curves.tag_positions_changed(); - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region); - - last_mouse_position_ = stroke_extension.mouse_position; - } -}; - -/** - * Moves individual points under the brush and does a length preservation step afterwards. - */ -class CombOperation : public CurvesSculptStrokeOperation { - private: - float2 last_mouse_position_; - - public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override - { - BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; }); - - if (stroke_extension.is_first) { - return; - } - - Scene &scene = *CTX_data_scene(C); - Object &object = *CTX_data_active_object(C); - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - - CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; - Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); - const float brush_radius = BKE_brush_size_get(&scene, &brush); - const float brush_strength = BKE_brush_alpha_get(&scene, &brush); - - const float4x4 ob_mat = object.obmat; - const float4x4 ob_imat = ob_mat.inverted(); - - float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); - - Curves &curves_id = *static_cast<Curves *>(object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - MutableSpan<float3> positions = curves.positions(); - - const float2 mouse_prev = last_mouse_position_; - const float2 mouse_cur = stroke_extension.mouse_position; - const float2 mouse_diff = mouse_cur - mouse_prev; - const float mouse_diff_len = math::length(mouse_diff); - - threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { - for (const int curve_i : curves_range) { - const IndexRange curve_points = curves.range_for_curve(curve_i); - /* Compute lengths of the segments. Those are used to make sure that the lengths don't - * change. */ - Vector<float, 16> segment_lengths(curve_points.size() - 1); - for (const int segment_i : IndexRange(curve_points.size() - 1)) { - const float3 &p1 = positions[curve_points[segment_i]]; - const float3 &p2 = positions[curve_points[segment_i] + 1]; - const float length = math::distance(p1, p2); - segment_lengths[segment_i] = length; - } - bool curve_changed = false; - for (const int point_i : curve_points.drop_front(1)) { - const float3 old_position = positions[point_i]; - - /* Find the position of the point in screen space. */ - float2 old_position_screen; - ED_view3d_project_float_v2_m4( - region, old_position, old_position_screen, projection.values); - - /* Project the point onto the line drawn by the mouse. Note, it's projected on the - * infinite line, not only on the line segment. */ - float2 old_position_screen_proj; - /* t is 0 when the point is closest to the previous mouse position and 1 when it's - * closest to the current mouse position. */ - const float t = closest_to_line_v2( - old_position_screen_proj, old_position_screen, mouse_prev, mouse_cur); - - /* Compute the distance to the mouse line segment. */ - const float2 old_position_screen_proj_segment = mouse_prev + - std::clamp(t, 0.0f, 1.0f) * mouse_diff; - const float distance_screen = math::distance(old_position_screen, - old_position_screen_proj_segment); - if (distance_screen > brush_radius) { - /* Ignore the point because it's too far away. */ - continue; - } - /* Compute a falloff that is based on how far along the point along the last stroke - * segment is. */ - const float t_overshoot = brush_radius / mouse_diff_len; - const float t_falloff = 1.0f - std::max(t, 0.0f) / (1.0f + t_overshoot); - /* A falloff that is based on how far away the point is from the stroke. */ - const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius); - /* Combine the different falloffs and brush strength. */ - const float weight = brush_strength * t_falloff * radius_falloff; - - /* Offset the old point position in screen space and transform it back into 3D space. */ - const float2 new_position_screen = old_position_screen + mouse_diff * weight; - float3 new_position; - ED_view3d_win_to_3d( - v3d, region, ob_mat * old_position, new_position_screen, new_position); - new_position = ob_imat * new_position; - positions[point_i] = new_position; - - curve_changed = true; - } - if (!curve_changed) { - continue; - } - /* Ensure that the length of each segment stays the same. */ - for (const int segment_i : IndexRange(curve_points.size() - 1)) { - const float3 &p1 = positions[curve_points[segment_i]]; - float3 &p2 = positions[curve_points[segment_i] + 1]; - const float3 direction = math::normalize(p2 - p1); - const float desired_length = segment_lengths[segment_i]; - p2 = p1 + direction * desired_length; - } - } - }); - - curves.tag_positions_changed(); - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region); - } -}; - -/** - * Drags the tip point of each curve and resamples the rest of the curve. - */ -class SnakeHookOperation : public CurvesSculptStrokeOperation { - private: - float2 last_mouse_position_; - - public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override - { - BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; }); - - if (stroke_extension.is_first) { - return; - } - - Scene &scene = *CTX_data_scene(C); - Object &object = *CTX_data_active_object(C); - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); - - CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; - Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); - const float brush_radius = BKE_brush_size_get(&scene, &brush); - const float brush_strength = BKE_brush_alpha_get(&scene, &brush); - - const float4x4 ob_mat = object.obmat; - const float4x4 ob_imat = ob_mat.inverted(); - - float4x4 projection; - ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); - - Curves &curves_id = *static_cast<Curves *>(object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - MutableSpan<float3> positions = curves.positions(); - - const float2 mouse_prev = last_mouse_position_; - const float2 mouse_cur = stroke_extension.mouse_position; - const float2 mouse_diff = mouse_cur - mouse_prev; - - threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { - for (const int curve_i : curves_range) { - const IndexRange curve_points = curves.range_for_curve(curve_i); - const int last_point_i = curve_points.last(); - - const float3 old_position = positions[last_point_i]; - - float2 old_position_screen; - ED_view3d_project_float_v2_m4( - region, old_position, old_position_screen, projection.values); - - const float distance_screen = math::distance(old_position_screen, mouse_prev); - if (distance_screen > brush_radius) { - continue; - } - - const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius); - const float weight = brush_strength * radius_falloff; - - const float2 new_position_screen = old_position_screen + mouse_diff * weight; - float3 new_position; - ED_view3d_win_to_3d(v3d, region, ob_mat * old_position, new_position_screen, new_position); - new_position = ob_imat * new_position; - - this->move_last_point_and_resample(positions, curve_points, new_position); - } - }); - - curves.tag_positions_changed(); - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region); - } - - void move_last_point_and_resample(MutableSpan<float3> positions, - const IndexRange curve_points, - const float3 &new_last_point_position) const - { - Vector<float> old_lengths; - old_lengths.append(0.0f); - /* Used to (1) normalize the segment sizes over time and (2) support making zero-length - * segments */ - const float extra_length = 0.001f; - for (const int segment_i : IndexRange(curve_points.size() - 1)) { - const float3 &p1 = positions[curve_points[segment_i]]; - const float3 &p2 = positions[curve_points[segment_i] + 1]; - const float length = math::distance(p1, p2); - old_lengths.append(old_lengths.last() + length + extra_length); - } - Vector<float> point_factors; - for (float &old_length : old_lengths) { - point_factors.append(old_length / old_lengths.last()); - } - - PolySpline new_spline; - new_spline.resize(curve_points.size()); - MutableSpan<float3> new_spline_positions = new_spline.positions(); - for (const int i : IndexRange(curve_points.size() - 1)) { - new_spline_positions[i] = positions[curve_points[i]]; - } - new_spline_positions.last() = new_last_point_position; - new_spline.mark_cache_invalid(); - - for (const int i : IndexRange(curve_points.size())) { - const float factor = point_factors[i]; - const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - new_spline.sample_with_index_factors<float3>( - new_spline_positions, {&index_factor, 1}, {&p, 1}); - positions[curve_points[i]] = p; - } - } -}; - /** * Resamples the curves to a shorter length. */ @@ -423,7 +117,7 @@ class ShrinkOperation : public CurvesSculptStrokeOperation { threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { for (const int curve_i : curves_range) { - const IndexRange curve_points = curves.range_for_curve(curve_i); + const IndexRange curve_points = curves.points_for_curve(curve_i); const int last_point_i = curve_points.last(); const float3 old_tip_position = positions[last_point_i]; @@ -492,7 +186,7 @@ class ShrinkOperation : public CurvesSculptStrokeOperation { } }; -class AddOperation : public CurvesSculptStrokeOperation { +class DensityAddOperation : public CurvesSculptStrokeOperation { private: /** Contains the root points of the curves that existed before this operation started. */ KDTree_3d *old_kdtree_ = nullptr; @@ -513,7 +207,7 @@ class AddOperation : public CurvesSculptStrokeOperation { }; public: - ~AddOperation() override + ~DensityAddOperation() override { if (old_kdtree_ != nullptr) { BLI_kdtree_3d_free(old_kdtree_); @@ -610,7 +304,7 @@ class AddOperation : public CurvesSculptStrokeOperation { if (old_kdtree_ == nullptr && minimum_distance > 0.0f) { old_kdtree_ = this->kdtree_from_curve_roots_and_positions(curves, curves.curves_range(), {}); - old_kdtree_size_ = curves.curves_size(); + old_kdtree_size_ = curves.curves_num(); } float density; @@ -683,13 +377,6 @@ class AddOperation : public CurvesSculptStrokeOperation { return kdtree; } - int float_to_int_amount(float amount_f, RandomNumberGenerator &rng) - { - const float add_probability = fractf(amount_f); - const bool add_point = add_probability > rng.get_float(); - return (int)amount_f + (int)add_point; - } - bool is_too_close_to_existing_point(const float3 position, const float minimum_distance) const { if (old_kdtree_ == nullptr) { @@ -744,7 +431,7 @@ class AddOperation : public CurvesSculptStrokeOperation { * the triangle directly. If the triangle is larger than the brush, distribute new points * in a circle on the triangle plane. */ if (looptri_area < area_threshold) { - const int amount = this->float_to_int_amount(looptri_area * density, looptri_rng); + const int amount = looptri_rng.round_probabilistic(looptri_area * density); threading::parallel_for(IndexRange(amount), 512, [&](const IndexRange amount_range) { RandomNumberGenerator point_rng{rng_base_seed + looptri_index * 1000 + @@ -781,7 +468,7 @@ class AddOperation : public CurvesSculptStrokeOperation { const float radius_proj = std::sqrt(radius_proj_sq); const float circle_area = M_PI * radius_proj_sq; - const int amount = this->float_to_int_amount(circle_area * density, rng); + const int amount = rng.round_probabilistic(circle_area * density); const float3 axis_1 = math::normalize(v1 - v0) * radius_proj; const float3 axis_2 = math::normalize( @@ -838,7 +525,7 @@ class AddOperation : public CurvesSculptStrokeOperation { { Array<bool> elimination_mask(points.positions.size(), false); - const int curves_added_previously = curves.curves_size() - old_kdtree_size_; + const int curves_added_previously = curves.curves_num() - old_kdtree_size_; KDTree_3d *new_points_kdtree = this->kdtree_from_curve_roots_and_positions( curves, IndexRange(old_kdtree_size_, curves_added_previously), points.positions); @@ -902,14 +589,14 @@ class AddOperation : public CurvesSculptStrokeOperation { const int tot_new_curves = new_points.positions.size(); const int points_per_curve = 8; - curves.resize(curves.points_size() + tot_new_curves * points_per_curve, - curves.curves_size() + tot_new_curves); + curves.resize(curves.points_num() + tot_new_curves * points_per_curve, + curves.curves_num() + tot_new_curves); MutableSpan<int> offsets = curves.offsets(); MutableSpan<float3> positions = curves.positions(); for (const int i : IndexRange(tot_new_curves)) { - const int curve_i = curves.curves_size() - tot_new_curves + i; + const int curve_i = curves.curves_num() - tot_new_curves + i; const int first_point_i = offsets[curve_i]; offsets[curve_i + 1] = offsets[curve_i] + points_per_curve; @@ -932,13 +619,15 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); switch (brush.curves_sculpt_tool) { case CURVES_SCULPT_TOOL_COMB: - return std::make_unique<CombOperation>(); + return new_comb_operation(); case CURVES_SCULPT_TOOL_DELETE: - return std::make_unique<DeleteOperation>(); + return new_delete_operation(); case CURVES_SCULPT_TOOL_SNAKE_HOOK: - return std::make_unique<SnakeHookOperation>(); + return new_snake_hook_operation(); + case CURVES_SCULPT_TOOL_ADD: + return new_add_operation(); case CURVES_SCULPT_TOOL_TEST1: - return std::make_unique<AddOperation>(); + return std::make_unique<DensityAddOperation>(); case CURVES_SCULPT_TOOL_TEST2: return std::make_unique<ShrinkOperation>(); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc new file mode 100644 index 00000000000..682cd3b47ca --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float4x4.hh" +#include "BLI_index_mask_ops.hh" +#include "BLI_kdtree.h" +#include "BLI_rand.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_paint.h" +#include "BKE_spline.hh" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +namespace blender::ed::sculpt_paint { + +using blender::bke::CurvesGeometry; + +/** + * Drags the tip point of each curve and resamples the rest of the curve. + */ +class SnakeHookOperation : public CurvesSculptStrokeOperation { + private: + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override + { + BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; }); + + if (stroke_extension.is_first) { + return; + } + + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + const float brush_strength = BKE_brush_alpha_get(&scene, &brush); + + const float4x4 ob_mat = object.obmat; + const float4x4 ob_imat = ob_mat.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast<Curves *>(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan<float3> positions = curves.positions(); + + const float2 mouse_prev = last_mouse_position_; + const float2 mouse_cur = stroke_extension.mouse_position; + const float2 mouse_diff = mouse_cur - mouse_prev; + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange curve_points = curves.points_for_curve(curve_i); + const int last_point_i = curve_points.last(); + + const float3 old_position = positions[last_point_i]; + + float2 old_position_screen; + ED_view3d_project_float_v2_m4( + region, old_position, old_position_screen, projection.values); + + const float distance_screen = math::distance(old_position_screen, mouse_prev); + if (distance_screen > brush_radius) { + continue; + } + + const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius); + const float weight = brush_strength * radius_falloff; + + const float2 new_position_screen = old_position_screen + mouse_diff * weight; + float3 new_position; + ED_view3d_win_to_3d(v3d, region, ob_mat * old_position, new_position_screen, new_position); + new_position = ob_imat * new_position; + + this->move_last_point_and_resample(positions, curve_points, new_position); + } + }); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + } + + void move_last_point_and_resample(MutableSpan<float3> positions, + const IndexRange curve_points, + const float3 &new_last_point_position) const + { + Vector<float> old_lengths; + old_lengths.append(0.0f); + /* Used to (1) normalize the segment sizes over time and (2) support making zero-length + * segments */ + const float extra_length = 0.001f; + for (const int segment_i : IndexRange(curve_points.size() - 1)) { + const float3 &p1 = positions[curve_points[segment_i]]; + const float3 &p2 = positions[curve_points[segment_i] + 1]; + const float length = math::distance(p1, p2); + old_lengths.append(old_lengths.last() + length + extra_length); + } + Vector<float> point_factors; + for (float &old_length : old_lengths) { + point_factors.append(old_length / old_lengths.last()); + } + + PolySpline new_spline; + new_spline.resize(curve_points.size()); + MutableSpan<float3> new_spline_positions = new_spline.positions(); + for (const int i : IndexRange(curve_points.size() - 1)) { + new_spline_positions[i] = positions[curve_points[i]]; + } + new_spline_positions.last() = new_last_point_position; + new_spline.mark_cache_invalid(); + + for (const int i : IndexRange(curve_points.size())) { + const float factor = point_factors[i]; + const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor); + const float index_factor = lookup.evaluated_index + lookup.factor; + float3 p; + new_spline.sample_with_index_factors<float3>( + new_spline_positions, {&index_factor, 1}, {&p, 1}); + positions[curve_points[i]] = p; + } + } +}; + +std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation() +{ + return std::make_unique<SnakeHookOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b85d2d0aec8..5e89a4823db 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -962,6 +962,7 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext) trim_mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false); BMO_op_callf(bm, @@ -1075,7 +1076,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex const int trim_totpolys = (2 * (tot_screen_points - 2)) + (2 * tot_screen_points); trim_operation->mesh = BKE_mesh_new_nomain( trim_totverts, 0, 0, trim_totpolys * 3, trim_totpolys); - trim_operation->true_mesh_co = MEM_malloc_arrayN(trim_totverts, 3 * sizeof(float), "mesh orco"); + trim_operation->true_mesh_co = MEM_malloc_arrayN(trim_totverts, sizeof(float[3]), "mesh orco"); float depth_front = trim_operation->depth_front; float depth_back = trim_operation->depth_back; @@ -1129,7 +1130,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex /* Get the triangulation for the front/back poly. */ const int tot_tris_face = tot_screen_points - 2; - uint(*r_tris)[3] = MEM_malloc_arrayN(tot_tris_face, 3 * sizeof(uint), "tris"); + uint(*r_tris)[3] = MEM_malloc_arrayN(tot_tris_face, sizeof(uint[3]), "tris"); BLI_polyfill_calc(screen_points, tot_screen_points, 0, r_tris); /* Write the front face triangle indices. */ @@ -1214,12 +1215,14 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) trim_mesh, &((struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); BM_mesh_bm_from_me(bm, sculpt_mesh, &((struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 1705e36363e..0f7b8ad1f3d 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -792,7 +792,7 @@ static int paint_space_stroke(bContext *C, Paint *paint = BKE_paint_get_active_from_context(C); ePaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - int cnt = 0; + int count = 0; const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode); float d_world_space_position[3] = {0.0f}; @@ -855,14 +855,14 @@ static int paint_space_stroke(bContext *C, pressure = stroke->last_pressure; dpressure = final_pressure - stroke->last_pressure; - cnt++; + count++; } else { break; } } - return cnt; + return count; } /**** Public API ****/ @@ -986,6 +986,11 @@ static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke) paint_stroke_free(C, op, stroke); } +static bool curves_sculpt_brush_uses_spacing(const eBrushCurvesSculptTool tool) +{ + return ELEM(tool, CURVES_SCULPT_TOOL_ADD); +} + bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) { if ((br->flag & BRUSH_SPACE) == 0) { @@ -1000,7 +1005,8 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) return true; } - if (mode == PAINT_MODE_SCULPT_CURVES) { + if (mode == PAINT_MODE_SCULPT_CURVES && + !curves_sculpt_brush_uses_spacing(br->curves_sculpt_tool)) { return false; } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index 70f8f2127b4..8bf09ce3d05 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -532,9 +532,8 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo { const int totvert = SCULPT_vertex_count_get(ss); boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN( - totvert, 3 * sizeof(float), "pivot rotation axis"); - boundary->bend.pivot_positions = MEM_calloc_arrayN( - totvert, 3 * sizeof(float), "pivot positions"); + totvert, sizeof(float[3]), "pivot rotation axis"); + boundary->bend.pivot_positions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "pivot positions"); for (int i = 0; i < totvert; i++) { if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { @@ -567,7 +566,7 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary) { const int totvert = SCULPT_vertex_count_get(ss); - boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions"); + boundary->slide.directions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "slide directions"); for (int i = 0; i < totvert; i++) { if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { @@ -592,7 +591,7 @@ static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *b { zero_v3(boundary->twist.pivot_position); float(*poly_verts)[3] = MEM_malloc_arrayN( - boundary->num_vertices, sizeof(float) * 3, "poly verts"); + boundary->num_vertices, sizeof(float[3]), "poly verts"); for (int i = 0; i < boundary->num_vertices; i++) { add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i])); copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i])); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 0e26eb9b4b6..58da5adc5e3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -147,6 +147,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene me, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, .use_shapekey = true, .active_shapekey = ob->shapenr, })); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 5f6b8bf9b19..23bc9fbb54d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -379,6 +379,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); BMIter iter; @@ -574,6 +575,7 @@ static void sculpt_face_sets_init_flood_fill(Object *ob, mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces"); @@ -652,6 +654,7 @@ static void sculpt_face_sets_init_loop(Object *ob, const int mode) mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); BMIter iter; BMFace *f; @@ -1184,6 +1187,7 @@ static void sculpt_face_set_delete_geometry(Object *ob, mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); BM_mesh_elem_table_init(bm, BM_FACE); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 858c6c4e279..482bdf97d78 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -251,7 +251,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, if (SCULPT_stroke_is_first_brush_step(ss->cache)) { const int totvert = SCULPT_vertex_count_get(ss); ss->cache->detail_directions = MEM_malloc_arrayN( - totvert, 3 * sizeof(float), "details directions"); + totvert, sizeof(float[3]), "details directions"); for (int i = 0; i < totvert; i++) { float avg[3]; diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index f0ada312d82..d33cf70e117 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -1706,8 +1706,8 @@ static const EnumPropertyItem prop_actkeys_snap_types[] = { "NEAREST_FRAME", 0, "Selection to Nearest Frame", - "Snap selected keyframes to the nearest (whole) frame (use to fix accidental subframe " - "offsets)"}, + "Snap selected keyframes to the nearest (whole) frame " + "(use to fix accidental sub-frame offsets)"}, {ACTKEYS_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 20e9d21455f..7eba3d49616 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -170,12 +170,8 @@ static void action_main_region_draw(const bContext *C, ARegion *region) bAnimContext ac; View2D *v2d = ®ion->v2d; short marker_flag = 0; - short cfra_flag = 0; UI_view2d_view_ortho(v2d); - if (saction->flag & SACTION_DRAWTIME) { - cfra_flag |= DRAWCFRA_UNIT_SECONDS; - } /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 0d1ff71e567..e7bdbfe7c68 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -271,7 +271,6 @@ static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op)) MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; const int framenr = ED_space_clip_get_clip_frame_number(sc); - bool has_selection = false; bool changed = false; ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); @@ -281,7 +280,6 @@ static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op)) if (TRACK_VIEW_SELECTED(sc, track)) { MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr); if (marker != NULL) { - has_selection |= track->markersnr > 1; clip_delete_marker(C, clip, track, marker); changed = true; } @@ -878,24 +876,24 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); } else if (data->action == SLIDE_ACTION_TILT_SIZE) { - float start[2], end[2]; - float scale = 1.0f, angle = 0.0f; - float mval[2]; - - if (data->accurate) { - mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f; - mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f; - } - else { - mval[0] = event->mval[0]; - mval[1] = event->mval[1]; - } + const float mouse_delta[2] = {dx, dy}; + /* Vector which connects marker position with tilt/scale sliding area before sliding + * began. */ + float start[2]; sub_v2_v2v2(start, data->spos, data->old_pos); + start[0] *= data->width; + start[1] *= data->height; - ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]); + /* Vector which connects marker position with tilt/scale sliding area with the sliding + * delta applied. */ + float end[2]; + add_v2_v2v2(end, data->spos, mouse_delta); sub_v2_v2(end, data->old_pos); + end[0] *= data->width; + end[1] *= data->height; + float scale = 1.0f; if (len_squared_v2(start) != 0.0f) { scale = len_v2(end) / len_v2(start); @@ -904,7 +902,7 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) } } - angle = -angle_signed_v2v2(start, end); + const float angle = -angle_signed_v2v2(start, end); for (int a = 0; a < 4; a++) { float vec[2]; diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c index f9cbce40deb..d5223d57490 100644 --- a/source/blender/editors/space_clip/tracking_ops_track.c +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -211,6 +211,8 @@ static void track_markers_startjob( TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; int framenr = tmj->sfra; + BKE_autotrack_context_start(tmj->context); + while (framenr != tmj->efra) { if (tmj->delay > 0) { /* Tracking should happen with fixed fps. Calculate time diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index c4c6fa01025..b8c28e354da 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -79,6 +79,9 @@ if(WITH_IMAGE_HDR) add_definitions(-DWITH_HDR) endif() +if(WITH_IMAGE_WEBP) + add_definitions(-DWITH_WEBP) +endif() if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 363e19a8905..ceac53bde6b 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -851,6 +851,20 @@ static bool is_filtered_file_relpath(const FileListInternEntry *file, const File return fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) == 0; } +/** + * Apply the filter string as matching pattern on file name. + * \return true when the file should be in the result set, false if it should be filtered out. + */ +static bool is_filtered_file_name(const FileListInternEntry *file, const FileListFilter *filter) +{ + if (filter->filter_search[0] == '\0') { + return true; + } + + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ + return fnmatch(filter->filter_search, file->name, FNM_CASEFOLD) == 0; +} + /** \return true when the file should be in the result set, false if it should be filtered out. */ static bool is_filtered_file_type(const FileListInternEntry *file, const FileListFilter *filter) { @@ -890,7 +904,8 @@ static bool is_filtered_file(FileListInternEntry *file, const char *UNUSED(root), FileListFilter *filter) { - return is_filtered_file_type(file, filter) && is_filtered_file_relpath(file, filter); + return is_filtered_file_type(file, filter) && + (is_filtered_file_relpath(file, filter) || is_filtered_file_name(file, filter)); } static bool is_filtered_id_file_type(const FileListInternEntry *file, @@ -1041,10 +1056,10 @@ void filelist_tag_needs_filtering(FileList *filelist) void filelist_filter(FileList *filelist) { int num_filtered = 0; - const int num_files = filelist->filelist.nbr_entries; + const int num_files = filelist->filelist.entries_num; FileListInternEntry **filtered_tmp, *file; - if (ELEM(filelist->filelist.nbr_entries, FILEDIR_NBR_ENTRIES_UNSET, 0)) { + if (ELEM(filelist->filelist.entries_num, FILEDIR_NBR_ENTRIES_UNSET, 0)) { return; } @@ -1084,8 +1099,8 @@ void filelist_filter(FileList *filelist) memcpy(filelist->filelist_intern.filtered, filtered_tmp, sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered); - filelist->filelist.nbr_entries_filtered = num_filtered; - // printf("Filetered: %d over %d entries\n", num_filtered, filelist->filelist.nbr_entries); + filelist->filelist.entries_filtered_num = num_filtered; + // printf("Filetered: %d over %d entries\n", num_filtered, filelist->filelist.entries_num); filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size); filelist->flags &= ~FL_NEED_FILTERING; @@ -1537,8 +1552,8 @@ static void filelist_direntryarr_free(FileDirEntryArr *array) #else BLI_assert(BLI_listbase_is_empty(&array->entries)); #endif - array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; - array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET; + array->entries_num = FILEDIR_NBR_ENTRIES_UNSET; + array->entries_filtered_num = FILEDIR_NBR_ENTRIES_UNSET; } static void filelist_intern_entry_free(FileListInternEntry *entry) @@ -1859,7 +1874,7 @@ FileList *filelist_new(short type) filelist_cache_init(&p->filelist_cache, FILELIST_ENTRYCACHESIZE_DEFAULT); p->selection_state = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); - p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; + p->filelist.entries_num = FILEDIR_NBR_ENTRIES_UNSET; filelist_settype(p, type); return p; @@ -1964,9 +1979,9 @@ static void filelist_clear_main_files(FileList *filelist, const int removed_files = filelist_intern_free_main_files(&filelist->filelist_intern); - filelist->filelist.nbr_entries -= removed_files; - filelist->filelist.nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET; - BLI_assert(filelist->filelist.nbr_entries > FILEDIR_NBR_ENTRIES_UNSET); + filelist->filelist.entries_num -= removed_files; + filelist->filelist.entries_filtered_num = FILEDIR_NBR_ENTRIES_UNSET; + BLI_assert(filelist->filelist.entries_num > FILEDIR_NBR_ENTRIES_UNSET); if (do_selection && filelist->selection_state) { BLI_ghash_clear(filelist->selection_state, NULL, NULL); @@ -2152,7 +2167,7 @@ int filelist_files_ensure(FileList *filelist) filelist_filter(filelist); } - return filelist->filelist.nbr_entries_filtered; + return filelist->filelist.entries_filtered_num; } static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int index) @@ -2211,7 +2226,7 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const const size_t cache_size = cache->size; int old_index; - if ((index < 0) || (index >= filelist->filelist.nbr_entries_filtered)) { + if ((index < 0) || (index >= filelist->filelist.entries_filtered_num)) { return ret; } @@ -2259,7 +2274,7 @@ FileDirEntry *filelist_file(struct FileList *filelist, int index) int filelist_file_find_path(struct FileList *filelist, const char *filename) { - if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) { + if (filelist->filelist.entries_filtered_num == FILEDIR_NBR_ENTRIES_UNSET) { return -1; } @@ -2267,7 +2282,7 @@ int filelist_file_find_path(struct FileList *filelist, const char *filename) * This is only used to find again renamed entry, * annoying but looks hairy to get rid of it currently. */ - for (int fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) { + for (int fidx = 0; fidx < filelist->filelist.entries_filtered_num; fidx++) { FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx]; if (STREQ(entry->relpath, filename)) { return fidx; @@ -2279,11 +2294,11 @@ int filelist_file_find_path(struct FileList *filelist, const char *filename) int filelist_file_find_id(const FileList *filelist, const ID *id) { - if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) { + if (filelist->filelist.entries_filtered_num == FILEDIR_NBR_ENTRIES_UNSET) { return -1; } - for (int fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) { + for (int fidx = 0; fidx < filelist->filelist.entries_filtered_num; fidx++) { FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx]; if (entry->local_data.id == id) { return fidx; @@ -2393,23 +2408,23 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index) FileListEntryCache *cache = &filelist->filelist_cache; const size_t cache_size = cache->size; - const int nbr_entries = filelist->filelist.nbr_entries_filtered; + const int entries_num = filelist->filelist.entries_filtered_num; int start_index = max_ii(0, index - (cache_size / 2)); - int end_index = min_ii(nbr_entries, index + (cache_size / 2)); + int end_index = min_ii(entries_num, index + (cache_size / 2)); int i; const bool full_refresh = (filelist->flags & FL_IS_READY) == 0; - if ((index < 0) || (index >= nbr_entries)) { - // printf("Wrong index %d ([%d:%d])", index, 0, nbr_entries); + if ((index < 0) || (index >= entries_num)) { + // printf("Wrong index %d ([%d:%d])", index, 0, entries_num); return false; } /* Maximize cached range! */ if ((end_index - start_index) < cache_size) { if (start_index == 0) { - end_index = min_ii(nbr_entries, start_index + cache_size); + end_index = min_ii(entries_num, start_index + cache_size); } - else if (end_index == nbr_entries) { + else if (end_index == entries_num) { start_index = max_ii(0, end_index - cache_size); } } @@ -2846,7 +2861,7 @@ int ED_file_extension_icon(const char *path) int filelist_needs_reading(FileList *filelist) { - return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET) || + return (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET) || filelist_needs_force_reset(filelist); } @@ -2911,8 +2926,8 @@ void filelist_entries_select_index_range_set( FileList *filelist, FileSelection *sel, FileSelType select, uint flag, FileCheckType check) { /* select all valid files between first and last indicated */ - if ((sel->first >= 0) && (sel->first < filelist->filelist.nbr_entries_filtered) && - (sel->last >= 0) && (sel->last < filelist->filelist.nbr_entries_filtered)) { + if ((sel->first >= 0) && (sel->first < filelist->filelist.entries_filtered_num) && + (sel->last >= 0) && (sel->last < filelist->filelist.entries_filtered_num)) { int current_file; for (current_file = sel->first; current_file <= sel->last; current_file++) { filelist_entry_select_index_set(filelist, current_file, select, flag, check); @@ -2948,7 +2963,7 @@ uint filelist_entry_select_index_get(FileList *filelist, const int index, FileCh bool filelist_entry_is_selected(FileList *filelist, const int index) { - BLI_assert(index >= 0 && index < filelist->filelist.nbr_entries_filtered); + BLI_assert(index >= 0 && index < filelist->filelist.entries_filtered_num); FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index]; /* BLI_ghash_lookup returns NULL if not found, which gets mapped to 0, which gets mapped to @@ -3015,13 +3030,13 @@ static int filelist_readjob_list_dir(const char *root, const bool skip_currpar) { struct direntry *files; - int nbr_files, nbr_entries = 0; + int entries_num = 0; /* Full path of the item. */ char full_path[FILE_MAX]; - nbr_files = BLI_filelist_dir_contents(root, &files); + const int files_num = BLI_filelist_dir_contents(root, &files); if (files) { - int i = nbr_files; + int i = files_num; while (i--) { FileListInternEntry *entry; @@ -3095,11 +3110,11 @@ static int filelist_readjob_list_dir(const char *root, #endif BLI_addtail(entries, entry); - nbr_entries++; + entries_num++; } - BLI_filelist_free(files, nbr_files); + BLI_filelist_free(files, files_num); } - return nbr_entries; + return entries_num; } typedef enum ListLibOptions { @@ -3355,13 +3370,13 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) if (filelist->dir[0] == 0) { /* make directories */ # ifdef WITH_FREESTYLE - filelist->filelist.nbr_entries = 27; + filelist->filelist.entries_num = 27; # else - filelist->filelist.nbr_entries = 26; + filelist->filelist.entries_num = 26; # endif - filelist_resize(filelist, filelist->filelist.nbr_entries); + filelist_resize(filelist, filelist->filelist.entries_num); - for (a = 0; a < filelist->filelist.nbr_entries; a++) { + for (a = 0; a < filelist->filelist.entries_num; a++) { filelist->filelist.entries[a].typeflag |= FILE_TYPE_DIR; } @@ -3404,20 +3419,20 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) return; } - filelist->filelist.nbr_entries = 0; + filelist->filelist.entries_num = 0; for (id = lb->first; id; id = id->next) { if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') { - filelist->filelist.nbr_entries++; + filelist->filelist.entries_num++; } } /* XXX TODO: if data-browse or append/link #FLF_HIDE_PARENT has to be set. */ if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) { - filelist->filelist.nbr_entries++; + filelist->filelist.entries_num++; } - if (filelist->filelist.nbr_entries > 0) { - filelist_resize(filelist, filelist->filelist.nbr_entries); + if (filelist->filelist.entries_num > 0) { + filelist_resize(filelist, filelist->filelist.entries_num); } files = filelist->filelist.entries; @@ -3523,11 +3538,11 @@ typedef struct FileListReadJob { static void filelist_readjob_append_entries(FileListReadJob *job_params, ListBase *from_entries, - int nbr_from_entries, + int from_entries_num, short *do_update) { - BLI_assert(BLI_listbase_count(from_entries) == nbr_from_entries); - if (nbr_from_entries <= 0) { + BLI_assert(BLI_listbase_count(from_entries) == from_entries_num); + if (from_entries_num <= 0) { *do_update = false; return; } @@ -3535,7 +3550,7 @@ static void filelist_readjob_append_entries(FileListReadJob *job_params, FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ BLI_mutex_lock(&job_params->lock); BLI_movelisttolist(&filelist->filelist.entries, from_entries); - filelist->filelist.nbr_entries += nbr_from_entries; + filelist->filelist.entries_num += from_entries_num; BLI_mutex_unlock(&job_params->lock); *do_update = true; @@ -3591,7 +3606,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, char filter_glob[FILE_MAXFILE]; const char *root = filelist->filelist.root; const int max_recursion = filelist->max_recursion; - int nbr_done_dirs = 0, nbr_todo_dirs = 1; + int dirs_done_count = 0, dirs_todo_count = 1; todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__); td_dir = BLI_stack_push_r(todo_dirs); @@ -3611,7 +3626,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { FileListInternEntry *entry; - int nbr_entries = 0; + int entries_num = 0; char *subdir; char rel_subdir[FILE_MAX_LIBEXTRA]; @@ -3651,15 +3666,15 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, if (filelist->asset_library_ref) { list_lib_options |= LIST_LIB_ASSETS_ONLY; } - nbr_entries = filelist_readjob_list_lib( + entries_num = filelist_readjob_list_lib( subdir, &entries, list_lib_options, &indexer_runtime); - if (nbr_entries > 0) { + if (entries_num > 0) { is_lib = true; } } if (!is_lib) { - nbr_entries = filelist_readjob_list_dir( + entries_num = filelist_readjob_list_dir( subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); } @@ -3683,14 +3698,14 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, td_dir = BLI_stack_push_r(todo_dirs); td_dir->level = recursion_level + 1; td_dir->dir = BLI_strdup(dir); - nbr_todo_dirs++; + dirs_todo_count++; } } - filelist_readjob_append_entries(job_params, &entries, nbr_entries, do_update); + filelist_readjob_append_entries(job_params, &entries, entries_num, do_update); - nbr_done_dirs++; - *progress = (float)nbr_done_dirs / (float)nbr_todo_dirs; + dirs_done_count++; + *progress = (float)dirs_done_count / (float)dirs_todo_count; MEM_freeN(subdir); } @@ -3723,10 +3738,10 @@ static void filelist_readjob_do(const bool do_lib, // BLI_assert(filelist->filtered == NULL); BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET)); /* A valid, but empty directory from now. */ - filelist->filelist.nbr_entries = 0; + filelist->filelist.entries_num = 0; filelist_readjob_recursive_dir_add_items(do_lib, job_params, stop, do_update, progress); } @@ -3797,7 +3812,7 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, FileListInternEntry *entry; ListBase tmp_entries = {0}; ID *id_iter; - int nbr_entries = 0; + int entries_num = 0; /* Make sure no IDs are added/removed/reallocated in the main thread while this is running in * parallel. */ @@ -3820,19 +3835,19 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data, id_iter); entry->local_data.id = id_iter; - nbr_entries++; + entries_num++; BLI_addtail(&tmp_entries, entry); } FOREACH_MAIN_ID_END; BKE_main_unlock(job_params->current_main); - if (nbr_entries) { + if (entries_num) { *do_update = true; BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries); - filelist->filelist.nbr_entries += nbr_entries; - filelist->filelist.nbr_entries_filtered = -1; + filelist->filelist.entries_num += entries_num; + filelist->filelist.entries_filtered_num = -1; } } @@ -3857,10 +3872,10 @@ static void filelist_readjob_asset_library(FileListReadJob *job_params, FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET)); /* A valid, but empty file-list from now. */ - filelist->filelist.nbr_entries = 0; + filelist->filelist.entries_num = 0; /* NOP if already read. */ filelist_readjob_load_asset_library_data(job_params, do_update); @@ -3889,12 +3904,12 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, { FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET)); filelist_readjob_load_asset_library_data(job_params, do_update); /* A valid, but empty file-list from now. */ - filelist->filelist.nbr_entries = 0; + filelist->filelist.entries_num = 0; filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); } @@ -3917,7 +3932,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update FileListReadJob *flrj = flrjv; // printf("START filelist reading (%d files, main thread: %d)\n", - // flrj->filelist->filelist.nbr_entries, BLI_thread_is_main()); + // flrj->filelist->filelist.entries_num, BLI_thread_is_main()); BLI_mutex_lock(&flrj->lock); @@ -3926,7 +3941,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist = MEM_dupallocN(flrj->filelist); BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries); - flrj->tmp_filelist->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; + flrj->tmp_filelist->filelist.entries_num = FILEDIR_NBR_ENTRIES_UNSET; flrj->tmp_filelist->filelist_intern.filtered = NULL; BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries); @@ -3958,18 +3973,18 @@ static void filelist_readjob_update(void *flrjv) FileListReadJob *flrj = flrjv; FileListIntern *fl_intern = &flrj->filelist->filelist_intern; ListBase new_entries = {NULL}; - int nbr_entries, new_nbr_entries = 0; + int entries_num, new_entries_num = 0; BLI_movelisttolist(&new_entries, &fl_intern->entries); - nbr_entries = flrj->filelist->filelist.nbr_entries; + entries_num = flrj->filelist->filelist.entries_num; BLI_mutex_lock(&flrj->lock); - if (flrj->tmp_filelist->filelist.nbr_entries > 0) { + if (flrj->tmp_filelist->filelist.entries_num > 0) { /* We just move everything out of 'thread context' into final list. */ - new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries; + new_entries_num = flrj->tmp_filelist->filelist.entries_num; BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries); - flrj->tmp_filelist->filelist.nbr_entries = 0; + flrj->tmp_filelist->filelist.entries_num = 0; } if (flrj->tmp_filelist->asset_library) { @@ -3983,7 +3998,7 @@ static void filelist_readjob_update(void *flrjv) BLI_mutex_unlock(&flrj->lock); - if (new_nbr_entries) { + if (new_entries_num) { /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep * the asset library data we just read. */ filelist_clear_ex(flrj->filelist, false, true, false); @@ -3991,9 +4006,9 @@ static void filelist_readjob_update(void *flrjv) flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING); } - /* if no new_nbr_entries, this is NOP */ + /* if no new_entries_num, this is NOP */ BLI_movelisttolist(&fl_intern->entries, &new_entries); - flrj->filelist->filelist.nbr_entries = MAX2(nbr_entries, 0) + new_nbr_entries; + flrj->filelist->filelist.entries_num = MAX2(entries_num, 0) + new_entries_num; } static void filelist_readjob_endjob(void *flrjv) @@ -4011,11 +4026,11 @@ static void filelist_readjob_free(void *flrjv) { FileListReadJob *flrj = flrjv; - // printf("END filelist reading (%d files)\n", flrj->filelist->filelist.nbr_entries); + // printf("END filelist reading (%d files)\n", flrj->filelist->filelist.entries_num); if (flrj->tmp_filelist) { /* tmp_filelist shall never ever be filtered! */ - BLI_assert(flrj->tmp_filelist->filelist.nbr_entries == 0); + BLI_assert(flrj->tmp_filelist->filelist.entries_num == 0); BLI_assert(BLI_listbase_is_empty(&flrj->tmp_filelist->filelist.entries)); filelist_freelib(flrj->tmp_filelist); diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 847bf89bba8..ae0e5b23d55 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -968,13 +968,13 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) /* Check gvfs shares. */ const char *const xdg_runtime_dir = BLI_getenv("XDG_RUNTIME_DIR"); if (xdg_runtime_dir != NULL) { - struct direntry *dir; + struct direntry *dirs; char name[FILE_MAX]; BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/"); - const uint dir_len = BLI_filelist_dir_contents(name, &dir); - for (uint i = 0; i < dir_len; i++) { - if (dir[i].type & S_IFDIR) { - const char *dirname = dir[i].relname; + const uint dirs_num = BLI_filelist_dir_contents(name, &dirs); + for (uint i = 0; i < dirs_num; i++) { + if (dirs[i].type & S_IFDIR) { + const char *dirname = dirs[i].relname; if (dirname[0] != '.') { /* Dir names contain a lot of unwanted text. * Assuming every entry ends with the share name */ @@ -992,7 +992,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) } } } - BLI_filelist_free(dir, dir_len); + BLI_filelist_free(dirs, dirs_num); } # endif diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index fe8005892cf..2eb64e5b115 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -113,6 +113,7 @@ void GRAPH_OT_clean(struct wmOperatorType *ot); void GRAPH_OT_blend_to_neighbor(struct wmOperatorType *ot); void GRAPH_OT_breakdown(struct wmOperatorType *ot); void GRAPH_OT_decimate(struct wmOperatorType *ot); +void GRAPH_OT_blend_to_default(struct wmOperatorType *ot); void GRAPH_OT_sample(struct wmOperatorType *ot); void GRAPH_OT_bake(struct wmOperatorType *ot); void GRAPH_OT_unbake(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index b00e069470d..128925d4591 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -457,6 +457,7 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_decimate); WM_operatortype_append(GRAPH_OT_blend_to_neighbor); WM_operatortype_append(GRAPH_OT_breakdown); + WM_operatortype_append(GRAPH_OT_blend_to_default); WM_operatortype_append(GRAPH_OT_euler_filter); WM_operatortype_append(GRAPH_OT_delete); WM_operatortype_append(GRAPH_OT_duplicate); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 1a3355b0139..313f6ca1561 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -550,7 +550,7 @@ void GRAPH_OT_decimate(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Blend To Neighbor Operator +/** \name Blend to Neighbor Operator * \{ */ static void blend_to_neighbor_graph_keys(bAnimContext *ac, float factor) @@ -584,7 +584,7 @@ static void blend_to_neighbor_draw_status_header(bContext *C, tGraphSliderOp *gs ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR); - strcpy(mode_str, TIP_("Blend To Neighbor")); + strcpy(mode_str, TIP_("Blend to Neighbor")); if (hasNumInput(&gso->num)) { char str_ofs[NUM_STR_REP_LEN]; @@ -652,7 +652,7 @@ static int blend_to_neighbor_exec(bContext *C, wmOperator *op) void GRAPH_OT_blend_to_neighbor(wmOperatorType *ot) { /* Identifiers. */ - ot->name = "Blend To Neighbor"; + ot->name = "Blend to Neighbor"; ot->idname = "GRAPH_OT_blend_to_neighbor"; ot->description = "Blend selected keyframes to their left or right neighbor"; @@ -802,3 +802,131 @@ void GRAPH_OT_breakdown(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Blend to Default Value Operator + * \{ */ + +static void blend_to_default_graph_keys(bAnimContext *ac, const float factor) +{ + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + FCurve *fcu = (FCurve *)ale->key_data; + + /* Check if the curves actually have any points. */ + if (fcu == NULL || fcu->bezt == NULL || fcu->totvert == 0) { + continue; + } + + PointerRNA id_ptr; + RNA_id_pointer_create(ale->id, &id_ptr); + + blend_to_default_fcurve(&id_ptr, fcu, factor); + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); +} + +static void blend_to_default_draw_status_header(bContext *C, tGraphSliderOp *gso) +{ + char status_str[UI_MAX_DRAW_STR]; + char mode_str[32]; + char slider_string[UI_MAX_DRAW_STR]; + + ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR); + + strcpy(mode_str, TIP_("Blend to Default Value")); + + if (hasNumInput(&gso->num)) { + char str_ofs[NUM_STR_REP_LEN]; + + outputNumInput(&gso->num, str_ofs, &gso->scene->unit); + + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs); + } + else { + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string); + } + + ED_workspace_status_text(C, status_str); +} + +static void blend_to_default_modal_update(bContext *C, wmOperator *op) +{ + tGraphSliderOp *gso = op->customdata; + + blend_to_default_draw_status_header(C, gso); + + /* Set notifier that keyframes have changed. */ + reset_bezts(gso); + const float factor = ED_slider_factor_get(gso->slider); + RNA_property_float_set(op->ptr, gso->factor_prop, factor); + blend_to_default_graph_keys(&gso->ac, factor); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); +} + +static int blend_to_default_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const int invoke_result = graph_slider_invoke(C, op, event); + + if (invoke_result == OPERATOR_CANCELLED) { + return invoke_result; + } + + tGraphSliderOp *gso = op->customdata; + gso->modal_update = blend_to_default_modal_update; + gso->factor_prop = RNA_struct_find_property(op->ptr, "factor"); + blend_to_default_draw_status_header(C, gso); + + return invoke_result; +} + +static int blend_to_default_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + const float factor = RNA_float_get(op->ptr, "factor"); + + blend_to_default_graph_keys(&ac, factor); + + /* Set notifier that keyframes have changed. */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GRAPH_OT_blend_to_default(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Blend to Default Value"; + ot->idname = "GRAPH_OT_blend_to_default"; + ot->description = "Blend selected keys to their default value from their current position"; + + /* API callbacks. */ + ot->invoke = blend_to_default_invoke; + ot->modal = graph_slider_modal; + ot->exec = blend_to_default_exec; + ot->poll = graphop_editable_keyframes_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float_factor(ot->srna, + "factor", + 1.0f / 3.0f, + -FLT_MAX, + FLT_MAX, + "Factor", + "How much to blend to the default value", + 0.0f, + 1.0f); +} +/** \} */ diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index f5cc6083b25..c385420b18e 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -60,6 +60,9 @@ if(WITH_IMAGE_CINEON) add_definitions(-DWITH_CINEON) endif() +if(WITH_IMAGE_WEBP) + add_definitions(-DWITH_WEBP) +endif() blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 0af32a717a4..208928afc1f 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -20,6 +20,7 @@ #include "BKE_context.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_node.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -957,14 +958,11 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma { ImageFormatData *imf = imfptr->data; ID *id = imfptr->owner_id; - PointerRNA display_settings_ptr; - PropertyRNA *prop; const int depth_ok = BKE_imtype_valid_depths(imf->imtype); /* some settings depend on this being a scene that's rendered */ const bool is_render_out = (id && GS(id->name) == ID_SCE); uiLayout *col; - bool show_preview = false; col = uiLayoutColumn(layout, false); @@ -1004,7 +1002,6 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma } if (is_render_out && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { - show_preview = true; uiItemR(col, imfptr, "use_preview", 0, NULL, ICON_NONE); } @@ -1036,18 +1033,22 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma uiItemR(col, imfptr, "tiff_codec", 0, NULL, ICON_NONE); } - /* color management */ - if (color_management && (!BKE_imtype_requires_linear_float(imf->imtype) || - (show_preview && imf->flag & R_IMF_FLAG_PREVIEW_JPG))) { - prop = RNA_struct_find_property(imfptr, "display_settings"); - display_settings_ptr = RNA_property_pointer_get(imfptr, prop); - - col = uiLayoutColumn(layout, false); - uiItemL(col, IFACE_("Color Management"), ICON_NONE); - - uiItemR(col, &display_settings_ptr, "display_device", 0, NULL, ICON_NONE); + /* Override color management */ + if (color_management) { + uiItemS(col); + uiItemR(col, imfptr, "color_management", 0, NULL, ICON_NONE); - uiTemplateColormanagedViewSettings(col, NULL, imfptr, "view_settings"); + if (imf->color_management == R_IMF_COLOR_MANAGEMENT_OVERRIDE) { + if (BKE_imtype_requires_linear_float(imf->imtype)) { + PointerRNA linear_settings_ptr = RNA_pointer_get(imfptr, "linear_colorspace_settings"); + uiItemR(col, &linear_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE); + } + else { + PointerRNA display_settings_ptr = RNA_pointer_get(imfptr, "display_settings"); + uiItemR(col, &display_settings_ptr, "display_device", 0, NULL, ICON_NONE); + uiTemplateColormanagedViewSettings(col, NULL, imfptr, "view_settings"); + } + } } } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 3721ea81c04..1c4a1d7e8c9 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -38,6 +38,7 @@ #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_image_save.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -53,7 +54,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_moviecache.h" -#include "intern/openexr/openexr_multi.h" +#include "IMB_openexr.h" #include "RE_pipeline.h" @@ -1736,7 +1737,7 @@ static int image_save_options_init(Main *bmain, if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { /* imtype */ - opts->im_format = scene->r.im_format; + BKE_image_format_init_for_write(&opts->im_format, scene, NULL); is_depth_set = true; if (!BKE_image_is_multiview(ima)) { /* In case multiview is disabled, @@ -1752,14 +1753,18 @@ static int image_save_options_init(Main *bmain, opts->im_format.planes = ibuf->planes; } else { - BKE_imbuf_to_image_format(&opts->im_format, ibuf); + BKE_image_format_from_imbuf(&opts->im_format, ibuf); } /* use the multiview image settings as the default */ opts->im_format.stereo3d_format = *ima->stereo3d_format; opts->im_format.views_format = ima->views_format; + + BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene); } + opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; + if (ima->source == IMA_SRC_TILED) { BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); @@ -1809,13 +1814,6 @@ static int image_save_options_init(Main *bmain, STR_CONCAT(opts->filepath, len, ".<UDIM>"); } } - - /* color management */ - BKE_color_managed_display_settings_copy(&opts->im_format.display_settings, - &scene->display_settings); - - BKE_color_managed_view_settings_free(&opts->im_format.view_settings); - BKE_color_managed_view_settings_copy(&opts->im_format.view_settings, &scene->view_settings); } BKE_image_release_ibuf(ima, ibuf, lock); @@ -1829,8 +1827,8 @@ static void image_save_options_from_op(Main *bmain, ImageFormatData *imf) { if (imf) { - BKE_color_managed_view_settings_free(&opts->im_format.view_settings); - opts->im_format = *imf; + BKE_image_format_free(&opts->im_format); + BKE_image_format_copy(&opts->im_format, imf); } if (RNA_struct_property_is_set(op->ptr, "filepath")) { @@ -1843,8 +1841,8 @@ static void image_save_options_to_op(ImageSaveOptions *opts, wmOperator *op) { if (op->customdata) { ImageSaveData *isd = op->customdata; - BKE_color_managed_view_settings_free(&isd->im_format.view_settings); - isd->im_format = opts->im_format; + BKE_image_format_free(&isd->im_format); + BKE_image_format_copy(&isd->im_format, &opts->im_format); } RNA_string_set(op->ptr, "filepath", opts->filepath); @@ -1878,7 +1876,7 @@ static void image_save_as_free(wmOperator *op) { if (op->customdata) { ImageSaveData *isd = op->customdata; - BKE_color_managed_view_settings_free(&isd->im_format.view_settings); + BKE_image_format_free(&isd->im_format); MEM_freeN(op->customdata); op->customdata = NULL; @@ -1920,6 +1918,8 @@ static int image_save_as_exec(bContext *C, wmOperator *op) BKE_image_free_packedfiles(image); } + BKE_image_save_options_free(&opts); + image_save_as_free(op); return OPERATOR_FINISHED; @@ -1948,6 +1948,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS BKE_image_save_options_init(&opts, bmain, scene); if (image_save_options_init(bmain, &opts, ima, iuser, true, save_as_render) == 0) { + BKE_image_save_options_free(&opts); return OPERATOR_CANCELLED; } image_save_options_to_op(&opts, op); @@ -1964,7 +1965,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS isd->image = ima; isd->iuser = iuser; - memcpy(&isd->im_format, &opts.im_format, sizeof(opts.im_format)); + BKE_image_format_copy(&isd->im_format, &opts.im_format); op->customdata = isd; /* show multiview save options only if image has multiviews */ @@ -1974,6 +1975,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima)); image_filesel(C, op, opts.filepath); + BKE_image_save_options_free(&opts); return OPERATOR_RUNNING_MODAL; } @@ -2001,15 +2003,21 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) ImageSaveData *isd = op->customdata; PointerRNA imf_ptr; const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview"); + const bool use_color_management = RNA_boolean_get(op->ptr, "save_as_render"); - /* image template */ - RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr); - uiTemplateImageSettings(layout, &imf_ptr, false); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* main draw call */ uiDefAutoButsRNA( layout, op->ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + uiItemS(layout); + + /* image template */ + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr); + uiTemplateImageSettings(layout, &imf_ptr, use_color_management); + /* multiview template */ if (is_multiview) { uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); @@ -2132,6 +2140,7 @@ static int image_save_exec(bContext *C, wmOperator *op) BKE_image_save_options_init(&opts, bmain, scene); if (image_save_options_init(bmain, &opts, image, iuser, false, false) == 0) { + BKE_image_save_options_free(&opts); return OPERATOR_CANCELLED; } image_save_options_from_op(bmain, &opts, op, NULL); @@ -2147,7 +2156,7 @@ static int image_save_exec(bContext *C, wmOperator *op) ok = true; } - BKE_color_managed_view_settings_free(&opts.im_format.view_settings); + BKE_image_save_options_free(&opts); if (ok) { return OPERATOR_FINISHED; @@ -2399,6 +2408,7 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports) bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts); ok = ok && saved_successfully; } + BKE_image_save_options_free(&opts); } } } diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 3e7784d0364..42d3d841f4b 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -216,7 +216,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region) Scene *scene = CTX_data_scene(C); bAnimContext ac; View2D *v2d = ®ion->v2d; - short cfra_flag = 0; /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); @@ -240,11 +239,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region) UI_view2d_text_cache_draw(region); } - /* current frame */ - if (snla->flag & SNLA_DRAWTIME) { - cfra_flag |= DRAWCFRA_UNIT_SECONDS; - } - /* markers */ UI_view2d_view_orthoSpecial(region, v2d, 1); int marker_draw_flag = DRAW_MARKERS_MARGIN; diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index ccd3333fcc5..c524de2c55d 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -121,9 +121,6 @@ static void gather_socket_link_operations(bNodeTree &node_tree, Vector<SocketLinkOperation> &search_link_ops) { NODE_TYPES_BEGIN (node_type) { - if (StringRef(node_type->idname).find("Legacy") != StringRef::not_found) { - continue; - } const char *disabled_hint; if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) { continue; diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 30bd0fb528b..7fb15d69ab5 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -517,113 +517,6 @@ void NODE_OT_add_object(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Add Node Texture Operator - * \{ */ - -static Tex *node_add_texture_get_and_poll_texture_node_tree(Main *bmain, wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return (Tex *)BKE_libblock_find_session_uuid(bmain, ID_TE, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return (Tex *)BKE_libblock_find_name(bmain, ID_TE, name); -} - -static int node_add_texture_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - Tex *texture; - - if (!(texture = node_add_texture_get_and_poll_texture_node_tree(bmain, op))) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - bNode *texture_node = node_add_node(*C, - nullptr, - GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, - snode->runtime->cursor[0], - snode->runtime->cursor[1]); - if (!texture_node) { - BKE_report(op->reports, RPT_WARNING, "Could not add texture node"); - return OPERATOR_CANCELLED; - } - - texture_node->id = &texture->id; - id_us_plus(&texture->id); - - nodeSetActive(ntree, texture_node); - ED_node_tree_propagate_change(C, bmain, ntree); - DEG_relations_tag_update(bmain); - - return OPERATOR_FINISHED; -} - -static int node_add_texture_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - - /* Convert mouse coordinates to v2d space. */ - UI_view2d_region_to_view(®ion->v2d, - event->mval[0], - event->mval[1], - &snode->runtime->cursor[0], - &snode->runtime->cursor[1]); - - snode->runtime->cursor[0] /= UI_DPI_FAC; - snode->runtime->cursor[1] /= UI_DPI_FAC; - - return node_add_texture_exec(C, op); -} - -static bool node_add_texture_poll(bContext *C) -{ - const SpaceNode *snode = CTX_wm_space_node(C); - return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && - !UI_but_active_drop_name(C); -} - -void NODE_OT_add_texture(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Add Node Texture"; - ot->description = "Add a texture to the current node editor"; - ot->idname = "NODE_OT_add_texture"; - - /* callbacks */ - ot->exec = node_add_texture_exec; - ot->invoke = node_add_texture_invoke; - ot->poll = node_add_texture_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - RNA_def_string( - ot->srna, "name", "Texture", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Add Node Collection Operator * \{ */ diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index e638816e3fc..7f0c426922b 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -73,11 +73,10 @@ #include "node_intern.hh" /* own include */ -using blender::fn::CPPType; +using blender::GPointer; using blender::fn::FieldCPPType; using blender::fn::FieldInput; using blender::fn::GField; -using blender::fn::GPointer; namespace geo_log = blender::nodes::geometry_nodes_eval_log; extern "C" { @@ -1418,8 +1417,6 @@ static int node_error_type_to_icon(const geo_log::NodeWarningType type) return ICON_ERROR; case geo_log::NodeWarningType::Info: return ICON_INFO; - case geo_log::NodeWarningType::Legacy: - return ICON_ERROR; } BLI_assert(false); @@ -1430,8 +1427,6 @@ static uint8_t node_error_type_priority(const geo_log::NodeWarningType type) { switch (type) { case geo_log::NodeWarningType::Error: - return 4; - case geo_log::NodeWarningType::Legacy: return 3; case geo_log::NodeWarningType::Warning: return 2; diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 1ca2f877398..956bb581ee6 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -18,6 +18,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" @@ -274,28 +275,14 @@ static void compo_startjob(void *cjv, /* 1 is do_previews */ if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) { - ntreeCompositExecTree(cj->scene, - ntree, - &cj->scene->r, - false, - true, - &scene->view_settings, - &scene->display_settings, - ""); + ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, ""); } else { LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) { if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) { continue; } - ntreeCompositExecTree(cj->scene, - ntree, - &cj->scene->r, - false, - true, - &scene->view_settings, - &scene->display_settings, - srv->name); + ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, srv->name); } } @@ -897,19 +884,15 @@ struct NodeSizeWidget { int directions; }; -static void node_resize_init(bContext *C, - wmOperator *op, - const wmEvent *UNUSED(event), - const bNode *node, - NodeResizeDirection dir) +static void node_resize_init( + bContext *C, wmOperator *op, const float cursor[2], const bNode *node, NodeResizeDirection dir) { - SpaceNode *snode = CTX_wm_space_node(C); - NodeSizeWidget *nsw = MEM_cnew<NodeSizeWidget>(__func__); op->customdata = nsw; - nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC; - nsw->mystart = snode->runtime->cursor[1] * UI_DPI_FAC; + + nsw->mxstart = cursor[0]; + nsw->mystart = cursor[1]; /* store old */ nsw->oldlocx = node->locx; @@ -1068,7 +1051,7 @@ static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } - node_resize_init(C, op, event, node, dir); + node_resize_init(C, op, cursor, node, dir); return OPERATOR_RUNNING_MODAL; } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 319f97e57f5..cd40573607d 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -178,7 +178,6 @@ bool space_node_view_flag( void NODE_OT_view_all(wmOperatorType *ot); void NODE_OT_view_selected(wmOperatorType *ot); -void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot); void NODE_OT_backimage_move(wmOperatorType *ot); void NODE_OT_backimage_zoom(wmOperatorType *ot); @@ -241,7 +240,6 @@ void NODE_OT_add_reroute(wmOperatorType *ot); void NODE_OT_add_group(wmOperatorType *ot); void NODE_OT_add_object(wmOperatorType *ot); void NODE_OT_add_collection(wmOperatorType *ot); -void NODE_OT_add_texture(wmOperatorType *ot); void NODE_OT_add_file(wmOperatorType *ot); void NODE_OT_add_mask(wmOperatorType *ot); void NODE_OT_new_node_tree(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index e9903299300..ce000aba1da 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -37,7 +37,6 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_view_all); WM_operatortype_append(NODE_OT_view_selected); - WM_operatortype_append(NODE_OT_geometry_node_view_legacy); WM_operatortype_append(NODE_OT_mute_toggle); WM_operatortype_append(NODE_OT_hide_toggle); @@ -79,7 +78,6 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_add_group); WM_operatortype_append(NODE_OT_add_object); WM_operatortype_append(NODE_OT_add_collection); - WM_operatortype_append(NODE_OT_add_texture); WM_operatortype_append(NODE_OT_add_file); WM_operatortype_append(NODE_OT_add_mask); diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 8cd87574465..b63cb2eeee5 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -69,11 +69,11 @@ static bool node_link_item_compare(bNode *node, NodeLinkItem *item) return true; } -static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item) +static void node_link_item_apply(bNodeTree *ntree, bNode *node, NodeLinkItem *item) { if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { node->id = (ID *)item->ngroup; - BKE_ntree_update_main_tree(bmain, item->ngroup, nullptr); + BKE_ntree_update_tag_node_property(ntree, node); } else { /* nothing to do for now */ @@ -237,7 +237,8 @@ static void node_socket_add_replace(const bContext *C, nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to); } - node_link_item_apply(bmain, node_from, item); + node_link_item_apply(ntree, node_from, item); + ED_node_tree_propagate_change(C, bmain, ntree); } nodeSetActive(ntree, node_from); diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc index 36fdcf37fd7..f5f5a9e6f67 100644 --- a/source/blender/editors/space_node/node_view.cc +++ b/source/blender/editors/space_node/node_view.cc @@ -686,90 +686,4 @@ void NODE_OT_backimage_sample(wmOperatorType *ot) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name View Geometry Nodes Legacy Operator - * - * This operator should be removed when the 2.93 legacy nodes are removed. - * \{ */ - -static int space_node_view_geometry_nodes_legacy(bContext *C, SpaceNode *snode, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - - /* Only use the node editor's active node tree. Otherwise this will be too complicated. */ - bNodeTree *node_tree = snode->nodetree; - if (node_tree == nullptr || node_tree->type != NTREE_GEOMETRY) { - return OPERATOR_CANCELLED; - } - - bool found_legacy_node = false; - LISTBASE_FOREACH_BACKWARD (bNode *, node, &node_tree->nodes) { - StringRef idname{node->idname}; - if (idname.find("Legacy") == StringRef::not_found) { - node->flag &= ~NODE_SELECT; - } - else { - found_legacy_node = true; - node->flag |= NODE_SELECT; - } - } - - if (!found_legacy_node) { - WM_report(RPT_INFO, "Legacy node not found, may be in nested node group"); - } - - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - if (space_node_view_flag(*C, *snode, *region, NODE_SELECT, smooth_viewtx)) { - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static int geometry_node_view_legacy_exec(bContext *C, wmOperator *op) -{ - /* Allow running this operator directly in a specific node editor. */ - if (SpaceNode *snode = CTX_wm_space_node(C)) { - return space_node_view_geometry_nodes_legacy(C, snode, op); - } - - /* Since the operator is meant to be called from a button in the modifier panel, the node tree - * must be found from the screen, using the largest node editor if there is more than one. */ - if (ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0)) { - if (SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first)) { - ScrArea *old_area = CTX_wm_area(C); - ARegion *old_region = CTX_wm_region(C); - - /* Override the context since it is used by the View2D panning code. */ - CTX_wm_area_set(C, area); - CTX_wm_region_set(C, static_cast<ARegion *>(area->regionbase.last)); - const int result = space_node_view_geometry_nodes_legacy(C, snode, op); - CTX_wm_area_set(C, old_area); - CTX_wm_region_set(C, old_region); - return result; - } - } - - return OPERATOR_CANCELLED; -} - -static bool geometry_node_view_legacy_poll(bContext *C) -{ - /* Allow direct execution in a node editor, but also affecting any visible node editor. */ - return ED_operator_node_active(C) || BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0); -} - -void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot) -{ - ot->name = "View Deprecated Geometry Nodes"; - ot->idname = "NODE_OT_geometry_node_view_legacy"; - ot->description = "Select and view legacy geometry nodes in the node editor"; - - ot->exec = geometry_node_view_legacy_exec; - ot->poll = geometry_node_view_legacy_poll; - - ot->flag = OPTYPE_INTERNAL; -} - -/** \} */ - } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index a1fa0517c63..82b850653be 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -622,11 +622,6 @@ static bool node_collection_drop_poll(bContext *UNUSED(C), return WM_drag_is_ID_type(drag, ID_GR); } -static bool node_texture_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) -{ - return WM_drag_is_ID_type(drag, ID_TE); -} - static bool node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { @@ -687,12 +682,6 @@ static void node_dropboxes() WM_drag_free_imported_drag_ID, nullptr); WM_dropbox_add(lb, - "NODE_OT_add_texture", - node_texture_drop_poll, - node_id_drop_copy, - WM_drag_free_imported_drag_ID, - nullptr); - WM_dropbox_add(lb, "NODE_OT_add_group", node_group_drop_poll, node_group_drop_copy, diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index 716d5b67fe3..a4ff44512ef 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -301,7 +301,7 @@ static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *c if (ID_IS_OVERRIDE_LIBRARY_REAL(collection)) { if (ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(collection)) { - if (!data->is_liboverride_hierarchy_root_allowed) { + if (!(data->is_liboverride_hierarchy_root_allowed || data->is_liboverride_allowed)) { return TRAVERSE_SKIP_CHILDS; } } diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 4ef0bbbcde8..9857abb3da7 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -1082,7 +1082,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, } BLI_assert((restrict_column_offset * UI_UNIT_X + V2D_SCROLL_WIDTH) == - outliner_restrict_columns_width(space_outliner)); + outliner_right_columns_width(space_outliner)); /* Create buttons. */ uiBut *bt; @@ -1779,11 +1779,67 @@ static void outliner_draw_userbuts(uiBlock *block, } } -static bool outliner_draw_overrides_buts(uiBlock *block, - ARegion *region, - SpaceOutliner *space_outliner, - ListBase *lb, - const bool is_open) +static void outliner_draw_overrides_rna_buts(uiBlock *block, + const ARegion *region, + const SpaceOutliner *space_outliner, + const ListBase *lb, + const int x) +{ + const float pad_x = 2.0f * UI_DPI_FAC; + const float pad_y = 0.5f * U.pixelsize; + const float item_max_width = round_fl_to_int(OL_RNA_COL_SIZEX - 2 * pad_x); + const float item_height = round_fl_to_int(UI_UNIT_Y - 2.0f * pad_y); + + LISTBASE_FOREACH (const TreeElement *, te, lb) { + const TreeStoreElem *tselem = TREESTORE(te); + if (TSELEM_OPEN(tselem, space_outliner)) { + outliner_draw_overrides_rna_buts(block, region, space_outliner, &te->subtree, x); + } + + if (!outliner_is_element_in_view(te, ®ion->v2d)) { + continue; + } + if (tselem->type != TSE_LIBRARY_OVERRIDE) { + continue; + } + + TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>( + te); + + PointerRNA *ptr = &override_elem.override_rna_ptr; + PropertyRNA *prop = &override_elem.override_rna_prop; + const PropertyType prop_type = RNA_property_type(prop); + + uiBut *auto_but = uiDefAutoButR(block, + ptr, + prop, + -1, + (prop_type == PROP_ENUM) ? nullptr : "", + ICON_NONE, + x + pad_x, + te->ys + pad_y, + item_max_width, + item_height); + /* Added the button successfully, nothing else to do. Otherwise, cases for multiple buttons + * need to be handled. */ + if (auto_but) { + continue; + } + + if (!auto_but) { + /* TODO what if the array is longer, and doesn't fit nicely? What about multi-dimension + * arrays? */ + uiDefAutoButsArrayR( + block, ptr, prop, ICON_NONE, x + pad_x, te->ys + pad_y, item_max_width, item_height); + } + } +} + +static bool outliner_draw_overrides_warning_buts(uiBlock *block, + ARegion *region, + SpaceOutliner *space_outliner, + ListBase *lb, + const bool is_open) { bool any_item_has_warnings = false; @@ -1829,7 +1885,7 @@ static bool outliner_draw_overrides_buts(uiBlock *block, break; } - const bool any_child_has_warnings = outliner_draw_overrides_buts( + const bool any_child_has_warnings = outliner_draw_overrides_warning_buts( block, region, space_outliner, @@ -1863,28 +1919,20 @@ static bool outliner_draw_overrides_buts(uiBlock *block, return any_item_has_warnings; } -static void outliner_draw_rnacols(ARegion *region, int sizex) +static void outliner_draw_separator(ARegion *region, const int x) { View2D *v2d = ®ion->v2d; - float miny = v2d->cur.ymin; - if (miny < v2d->tot.ymin) { - miny = v2d->tot.ymin; - } - GPU_line_width(1.0f); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformThemeColorShadeAlpha(TH_BACK, -15, -200); - immBegin(GPU_PRIM_LINES, 4); - - immVertex2f(pos, sizex, v2d->cur.ymax); - immVertex2f(pos, sizex, miny); + immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, v2d->cur.ymax); - immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, miny); + immVertex2f(pos, x, v2d->cur.ymax); + immVertex2f(pos, x, v2d->cur.ymin); immEnd(); @@ -3637,7 +3685,7 @@ static void outliner_draw_tree(bContext *C, const TreeViewContext *tvc, ARegion *region, SpaceOutliner *space_outliner, - const float restrict_column_width, + const float right_column_width, const bool use_mode_column, const bool use_warning_column, TreeElement **te_edit) @@ -3672,8 +3720,8 @@ static void outliner_draw_tree(bContext *C, /* Set scissor so tree elements or lines can't overlap restriction icons. */ int scissor[4] = {0}; - if (restrict_column_width > 0.0f) { - int mask_x = BLI_rcti_size_x(®ion->v2d.mask) - (int)restrict_column_width + 1; + if (right_column_width > 0.0f) { + int mask_x = BLI_rcti_size_x(®ion->v2d.mask) - (int)right_column_width + 1; CLAMP_MIN(mask_x, 0); GPU_scissor_get(scissor); @@ -3699,11 +3747,11 @@ static void outliner_draw_tree(bContext *C, (te->flag & TE_DRAGGING) != 0, startx, &starty, - restrict_column_width, + right_column_width, te_edit); } - if (restrict_column_width > 0.0f) { + if (right_column_width > 0.0f) { /* Reset scissor. */ GPU_scissor(UNPACK4(scissor)); } @@ -3754,21 +3802,21 @@ static int outliner_data_api_buttons_start_x(int max_tree_width) static int outliner_width(SpaceOutliner *space_outliner, int max_tree_width, - float restrict_column_width) + float right_column_width) { if (space_outliner->outlinevis == SO_DATA_API) { return outliner_data_api_buttons_start_x(max_tree_width) + OL_RNA_COL_SIZEX + 10 * UI_DPI_FAC; } - return max_tree_width + restrict_column_width; + return max_tree_width + right_column_width; } static void outliner_update_viewable_area(ARegion *region, SpaceOutliner *space_outliner, int tree_width, int tree_height, - float restrict_column_width) + float right_column_width) { - int sizex = outliner_width(space_outliner, tree_width, restrict_column_width); + int sizex = outliner_width(space_outliner, tree_width, right_column_width); int sizey = tree_height; /* Extend size to allow for horizontal scrollbar and extra offset. */ @@ -3829,7 +3877,7 @@ void draw_outliner(const bContext *C) space_outliner->runtime->tree_display->hasWarnings(); /* Draw outliner stuff (background, hierarchy lines and names). */ - const float restrict_column_width = outliner_restrict_columns_width(space_outliner); + const float right_column_width = outliner_right_columns_width(space_outliner); outliner_back(region); block = UI_block_begin(C, region, __func__, UI_EMBOSS); outliner_draw_tree((bContext *)C, @@ -3837,7 +3885,7 @@ void draw_outliner(const bContext *C) &tvc, region, space_outliner, - restrict_column_width, + right_column_width, use_mode_column, use_warning_column, &te_edit); @@ -3852,7 +3900,8 @@ void draw_outliner(const bContext *C) if (space_outliner->outlinevis == SO_DATA_API) { int buttons_start_x = outliner_data_api_buttons_start_x(tree_width); /* draw rna buttons */ - outliner_draw_rnacols(region, buttons_start_x); + outliner_draw_separator(region, buttons_start_x); + outliner_draw_separator(region, buttons_start_x + OL_RNA_COL_SIZEX); UI_block_emboss_set(block, UI_EMBOSS); outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x, &space_outliner->tree); @@ -3864,9 +3913,17 @@ void draw_outliner(const bContext *C) } else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) { /* Draw overrides status columns. */ - outliner_draw_overrides_buts(block, region, space_outliner, &space_outliner->tree, true); + outliner_draw_overrides_warning_buts( + block, region, space_outliner, &space_outliner->tree, true); + + UI_block_emboss_set(block, UI_EMBOSS); + UI_block_flag_enable(block, UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE); + const int x = region->v2d.cur.xmax - right_column_width; + outliner_draw_separator(region, x); + outliner_draw_overrides_rna_buts(block, region, space_outliner, &space_outliner->tree, x); + UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS); } - else if (restrict_column_width > 0.0f) { + else if (right_column_width > 0.0f) { /* draw restriction columns */ RestrictPropertiesActive props_active; memset(&props_active, 1, sizeof(RestrictPropertiesActive)); @@ -3893,7 +3950,7 @@ void draw_outliner(const bContext *C) /* Draw edit buttons if necessary. */ if (te_edit) { - outliner_buttons(C, block, region, restrict_column_width, te_edit); + outliner_buttons(C, block, region, right_column_width, te_edit); } UI_block_end(C, block); @@ -3901,7 +3958,7 @@ void draw_outliner(const bContext *C) /* Update total viewable region. */ outliner_update_viewable_area( - region, space_outliner, tree_width, tree_height, restrict_column_width); + region, space_outliner, tree_width, tree_height, right_column_width); } /** \} */ diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index a60e082f6a5..ae67e7108bf 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -447,6 +447,17 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto (tselem->type == TSE_LAYER_COLLECTION)); UNUSED_VARS_NDEBUG(te); + if (ID_IS_OVERRIDE_LIBRARY(id)) { + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || + (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) == 0) { + BKE_reportf(reports, + RPT_WARNING, + "Cannot delete library override id '%s', it is part of an override hierarchy", + id->name); + return; + } + } + if (te->idcode == ID_LI && ((Library *)id)->parent != nullptr) { BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked library '%s'", id->name); return; diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index a9bdcc56787..7970841b4fd 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -638,7 +638,7 @@ bool outliner_tree_traverse(const SpaceOutliner *space_outliner, int filter_tselem_flag, TreeTraversalFunc func, void *customdata); -float outliner_restrict_columns_width(const struct SpaceOutliner *space_outliner); +float outliner_right_columns_width(const struct SpaceOutliner *space_outliner); /** * Find first tree element in tree with matching tree-store flag. */ diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index a583eb0364f..a202ded6deb 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -1551,7 +1551,7 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_ou const ARegion *region, float view_co_x) { - return (view_co_x > region->v2d.cur.xmax - outliner_restrict_columns_width(space_outliner)); + return (view_co_x > region->v2d.cur.xmax - outliner_right_columns_width(space_outliner)); } bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2]) diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 612baaa0752..0aea4521204 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -1677,6 +1677,12 @@ void OUTLINER_OT_object_operation(wmOperatorType *ot) using OutlinerDeleteFn = void (*)(bContext *C, ReportList *reports, Scene *scene, Object *ob); +typedef struct ObjectEditData { + GSet *objects_set; + bool is_liboverride_allowed; + bool is_liboverride_hierarchy_root_allowed; +} ObjectEditData; + static void outliner_do_object_delete(bContext *C, ReportList *reports, Scene *scene, @@ -1693,7 +1699,8 @@ static void outliner_do_object_delete(bContext *C, static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata) { - GSet *objects_to_delete = (GSet *)customdata; + ObjectEditData *data = reinterpret_cast<ObjectEditData *>(customdata); + GSet *objects_to_delete = data->objects_set; TreeStoreElem *tselem = TREESTORE(te); if (outliner_is_collection_tree_element(te)) { @@ -1708,9 +1715,15 @@ static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void ID *id = tselem->id; if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - if (!ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(id)) { - /* Only allow deletion of liboverride objects if they are root overrides. */ - return TRAVERSE_SKIP_CHILDS; + if (ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(id)) { + if (!(data->is_liboverride_hierarchy_root_allowed || data->is_liboverride_allowed)) { + return TRAVERSE_SKIP_CHILDS; + } + } + else { + if (!data->is_liboverride_allowed) { + return TRAVERSE_SKIP_CHILDS; + } } } @@ -1732,27 +1745,31 @@ static int outliner_delete_exec(bContext *C, wmOperator *op) /* Get selected objects skipping duplicates to prevent deleting objects linked to multiple * collections twice */ - GSet *objects_to_delete = BLI_gset_ptr_new(__func__); + ObjectEditData object_delete_data = {}; + object_delete_data.objects_set = BLI_gset_ptr_new(__func__); + object_delete_data.is_liboverride_allowed = false; + object_delete_data.is_liboverride_hierarchy_root_allowed = delete_hierarchy; outliner_tree_traverse(space_outliner, &space_outliner->tree, 0, TSE_SELECTED, outliner_find_objects_to_delete, - objects_to_delete); + &object_delete_data); if (delete_hierarchy) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); outliner_do_object_delete( - C, op->reports, scene, objects_to_delete, object_batch_delete_hierarchy_fn); + C, op->reports, scene, object_delete_data.objects_set, object_batch_delete_hierarchy_fn); BKE_id_multi_tagged_delete(bmain); } else { - outliner_do_object_delete(C, op->reports, scene, objects_to_delete, outliner_object_delete_fn); + outliner_do_object_delete( + C, op->reports, scene, object_delete_data.objects_set, outliner_object_delete_fn); } - BLI_gset_free(objects_to_delete, nullptr); + BLI_gset_free(object_delete_data.objects_set, nullptr); outliner_collection_delete(C, bmain, scene, op->reports, delete_hierarchy); diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 1a772287dfa..19fe40b612e 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -900,7 +900,6 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, /* ID types not (fully) ported to new design yet. */ if (te->abstract_element->expandPoll(*space_outliner)) { outliner_add_id_contents(space_outliner, te, tselem, id); - te->abstract_element->postExpand(*space_outliner); } } else if (ELEM(type, diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index 556f87617f6..ed5a2108d3c 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -314,7 +314,7 @@ bool outliner_tree_traverse(const SpaceOutliner *space_outliner, return true; } -float outliner_restrict_columns_width(const SpaceOutliner *space_outliner) +float outliner_right_columns_width(const SpaceOutliner *space_outliner) { int num_columns = 0; @@ -322,8 +322,10 @@ float outliner_restrict_columns_width(const SpaceOutliner *space_outliner) case SO_DATA_API: case SO_SEQUENCE: case SO_LIBRARIES: - case SO_OVERRIDES_LIBRARY: return 0.0f; + case SO_OVERRIDES_LIBRARY: + num_columns = OL_RNA_COL_SIZEX / UI_UNIT_X; + break; case SO_ID_ORPHANS: num_columns = 3; break; diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index f75182d25a0..97dc659155f 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -371,7 +371,7 @@ static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *area) static SpaceLink *outliner_duplicate(SpaceLink *sl) { SpaceOutliner *space_outliner = (SpaceOutliner *)sl; - SpaceOutliner *space_outliner_new = MEM_new<SpaceOutliner>(__func__, *space_outliner); + SpaceOutliner *space_outliner_new = MEM_cnew<SpaceOutliner>(__func__, *space_outliner); BLI_listbase_clear(&space_outliner_new->tree); space_outliner_new->treestore = nullptr; diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index bdca1954a9c..a60d3339042 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -136,8 +136,7 @@ class TreeDisplayOverrideLibrary final : public AbstractTreeDisplay { ListBase buildTree(const TreeSourceData &source_data) override; private: - TreeElement *add_library_contents(Main &, ListBase &, Library *); - bool override_library_id_filter_poll(const Library *lib, ID *id) const; + ListBase add_library_contents(Main &); short id_filter_get() const; }; diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc index f94727ba356..b5c0a10c834 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc @@ -32,72 +32,22 @@ TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outl ListBase TreeDisplayOverrideLibrary::buildTree(const TreeSourceData &source_data) { - ListBase tree = {nullptr}; - - { - /* current file first - mainvar provides tselem with unique pointer - not used */ - TreeElement *ten = add_library_contents(*source_data.bmain, tree, nullptr); - TreeStoreElem *tselem; - - if (ten) { - tselem = TREESTORE(ten); - if (!tselem->used) { - tselem->flag &= ~TSE_CLOSED; - } - } - } + ListBase tree = add_library_contents(*source_data.bmain); - for (ID *id : List<ID>(source_data.bmain->libraries)) { - Library *lib = reinterpret_cast<Library *>(id); - TreeElement *ten = add_library_contents(*source_data.bmain, tree, lib); - /* NULL-check matters, due to filtering there may not be a new element. */ - if (ten) { - lib->id.newid = (ID *)ten; + for (TreeElement *top_level_te : List<TreeElement>(tree)) { + TreeStoreElem *tselem = TREESTORE(top_level_te); + if (!tselem->used) { + tselem->flag &= ~TSE_CLOSED; } } - /* make hierarchy */ - for (TreeElement *ten : List<TreeElement>(tree)) { - if (ten == tree.first) { - /* First item is main, skip. */ - continue; - } - - TreeStoreElem *tselem = TREESTORE(ten); - Library *lib = (Library *)tselem->id; - BLI_assert(!lib || (GS(lib->id.name) == ID_LI)); - if (!lib || !lib->parent) { - continue; - } - - TreeElement *parent = (TreeElement *)lib->parent->id.newid; - - if (tselem->id->tag & LIB_TAG_INDIRECT) { - /* Only remove from 'first level' if lib is not also directly used. */ - BLI_remlink(&tree, ten); - BLI_addtail(&parent->subtree, ten); - ten->parent = parent; - } - else { - /* Else, make a new copy of the libtree for our parent. */ - TreeElement *dupten = add_library_contents(*source_data.bmain, parent->subtree, lib); - if (dupten) { - dupten->parent = parent; - } - } - } - /* restore newid pointers */ - for (ID *library_id : List<ID>(source_data.bmain->libraries)) { - library_id->newid = nullptr; - } - return tree; } -TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar, - ListBase &lb, - Library *lib) +ListBase TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar) { + ListBase tree = {nullptr}; + const short filter_id_type = id_filter_get(); ListBase *lbarray[INDEX_ID_MAX]; @@ -110,7 +60,6 @@ TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar, tot = set_listbasepointers(&mainvar, lbarray); } - TreeElement *tenlib = nullptr; for (int a = 0; a < tot; a++) { if (!lbarray[a] || !lbarray[a]->first) { continue; @@ -118,56 +67,51 @@ TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar, ID *id = nullptr; - /* check if there's data in current lib */ + /* check if there's data in current id list */ for (ID *id_iter : List<ID>(lbarray[a])) { - if (id_iter->lib == lib && ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) { + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) { id = id_iter; break; } } - if (id != nullptr) { - if (!tenlib) { - /* Create library tree element on demand, depending if there are any data-blocks. */ - if (lib) { - tenlib = outliner_add_element(&space_outliner_, &lb, lib, nullptr, TSE_SOME_ID, 0); - } - else { - tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0); - tenlib->name = IFACE_("Current File"); - } - if (tenlib->flag & TE_HAS_WARNING) { - has_warnings = true; - } - } + if (id == nullptr) { + continue; + } - /* Create data-block list parent element on demand. */ - TreeElement *ten; + /* Create data-block list parent element on demand. */ + TreeElement *id_base_te = nullptr; + ListBase *lb_to_expand = &tree; - if (filter_id_type) { - ten = tenlib; - } - else { - ten = outliner_add_element( - &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0); - ten->directdata = lbarray[a]; - ten->name = outliner_idcode_to_plural(GS(id->name)); - } + if (!filter_id_type) { + id_base_te = outliner_add_element( + &space_outliner_, &tree, lbarray[a], nullptr, TSE_ID_BASE, 0); + id_base_te->directdata = lbarray[a]; + id_base_te->name = outliner_idcode_to_plural(GS(id->name)); - for (ID *id : List<ID>(lbarray[a])) { - if (override_library_id_filter_poll(lib, id)) { - TreeElement *override_tree_element = outliner_add_element( - &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0); + lb_to_expand = &id_base_te->subtree; + } - if (BLI_listbase_is_empty(&override_tree_element->subtree)) { - outliner_free_tree_element(override_tree_element, &ten->subtree); - } + for (ID *id : List<ID>(lbarray[a])) { + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + TreeElement *override_tree_element = outliner_add_element( + &space_outliner_, lb_to_expand, id, id_base_te, TSE_LIBRARY_OVERRIDE_BASE, 0); + + if (BLI_listbase_is_empty(&override_tree_element->subtree)) { + outliner_free_tree_element(override_tree_element, lb_to_expand); } } } } - return tenlib; + /* Remove ID base elements that turn out to be empty. */ + LISTBASE_FOREACH_MUTABLE (TreeElement *, te, &tree) { + if (BLI_listbase_is_empty(&te->subtree)) { + outliner_free_tree_element(te, &tree); + } + } + + return tree; } short TreeDisplayOverrideLibrary::id_filter_get() const @@ -178,17 +122,4 @@ short TreeDisplayOverrideLibrary::id_filter_get() const return 0; } -bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(const Library *lib, ID *id) const -{ - if (id->lib != lib) { - return false; - } - - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - return false; - } - - return true; -} - } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc index 0ee5059a54d..19811e45b90 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc @@ -154,15 +154,6 @@ void TreeDisplayViewLayer::add_layer_collections_recursive(ListBase &tree, if (!exclude && show_objects_) { add_layer_collection_objects(ten->subtree, *lc, *ten); } - - const bool lib_overrides_visible = !exclude && (!SUPPORT_FILTER_OUTLINER(&space_outliner_) || - ((space_outliner_.filter & - SO_FILTER_NO_LIB_OVERRIDE) == 0)); - - if (lib_overrides_visible && ID_IS_OVERRIDE_LIBRARY_REAL(&lc->collection->id)) { - outliner_add_element( - &space_outliner_, &ten->subtree, &lc->collection->id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0); - } } } diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 3c2023d7905..ca67aad00db 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -107,7 +107,6 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner return; } tree_element.expand(space_outliner); - tree_element.postExpand(space_outliner); } bool tree_element_warnings_get(TreeElement *te, int *r_icon, const char **r_message) diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index 2fbc86705b9..6f2d803ae96 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -40,9 +40,6 @@ class AbstractTreeElement { { return true; } - virtual void postExpand(SpaceOutliner &) const - { - } /** * Just while transitioning to the new tree-element design: Some types are only partially ported, diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index 64c73f57107..ef5e056f229 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -93,17 +93,6 @@ TreeElementID::TreeElementID(TreeElement &legacy_te, ID &id) legacy_te_.idcode = GS(id.name); } -void TreeElementID::postExpand(SpaceOutliner &space_outliner) const -{ - const bool lib_overrides_visible = !SUPPORT_FILTER_OUTLINER(&space_outliner) || - ((space_outliner.filter & SO_FILTER_NO_LIB_OVERRIDE) == 0); - - if (lib_overrides_visible && ID_IS_OVERRIDE_LIBRARY_REAL(&id_)) { - outliner_add_element( - &space_outliner, &legacy_te_.subtree, &id_, &legacy_te_, TSE_LIBRARY_OVERRIDE_BASE, 0); - } -} - bool TreeElementID::expandPoll(const SpaceOutliner &space_outliner) const { const TreeStoreElem *tsepar = legacy_te_.parent ? TREESTORE(legacy_te_.parent) : nullptr; diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.hh b/source/blender/editors/space_outliner/tree/tree_element_id.hh index 75dc7e737e2..b7519fe06f9 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_id.hh @@ -24,7 +24,6 @@ class TreeElementID : public AbstractTreeElement { static std::unique_ptr<TreeElementID> createFromID(TreeElement &legacy_te, ID &id); - void postExpand(SpaceOutliner &) const override; bool expandPoll(const SpaceOutliner &) const override; /** diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 64c390d29b3..857f5577e59 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -73,7 +73,8 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const } } - TreeElementOverridesData data = {id, *override_prop, is_rna_path_valid}; + TreeElementOverridesData data = { + id, *override_prop, override_rna_ptr, *override_rna_prop, is_rna_path_valid}; outliner_add_element( &space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++); } @@ -81,11 +82,13 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data) - : AbstractTreeElement(legacy_te), override_prop_(override_data.override_property) + : AbstractTreeElement(legacy_te), + override_rna_ptr(override_data.override_rna_ptr), + override_rna_prop(override_data.override_rna_prop) { BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE); - legacy_te.name = override_prop_.rna_path; + legacy_te.name = override_data.override_property.rna_path; /* Abusing this for now, better way to do it is also pending current refactor of the whole tree * code to use C++. */ legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid); diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index 1987efcf6f6..a2d1409f193 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -6,13 +6,21 @@ #pragma once +#include "RNA_types.h" + #include "tree_element.hh" +struct ID; +struct IDOverrideLibraryProperty; + namespace blender::ed::outliner { struct TreeElementOverridesData { ID &id; IDOverrideLibraryProperty &override_property; + PointerRNA &override_rna_ptr; + PropertyRNA &override_rna_prop; + bool is_rna_path_valid; }; @@ -27,7 +35,9 @@ class TreeElementOverridesBase final : public AbstractTreeElement { }; class TreeElementOverridesProperty final : public AbstractTreeElement { - IDOverrideLibraryProperty &override_prop_; + public: + PointerRNA override_rna_ptr; + PropertyRNA &override_rna_prop; public: TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.cc b/source/blender/editors/space_outliner/tree/tree_element_rna.cc index abc7cd8f8ce..914104f1f06 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_rna.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_rna.cc @@ -52,7 +52,7 @@ bool TreeElementRNACommon::isRNAValid() const return rna_ptr_.data != nullptr; } -bool TreeElementRNACommon::expandPoll(const SpaceOutliner &) const +bool TreeElementRNACommon::expandPoll(const SpaceOutliner &UNUSED(space_outliner)) const { return isRNAValid(); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 9370b349cb4..0ed366209f6 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2681,7 +2681,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region) Editing *ed = SEQ_editing_get(scene); SpaceSeq *sseq = CTX_wm_space_seq(C); View2D *v2d = ®ion->v2d; - short cfra_flag = 0; float col[3]; seq_prefetch_wm_notify(C, scene); @@ -2728,9 +2727,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region) } UI_view2d_view_ortho(v2d); - if ((sseq->flag & SEQ_DRAWFRAMES) == 0) { - cfra_flag |= DRAWCFRA_UNIT_SECONDS; - } UI_view2d_view_orthoSpecial(region, v2d, 1); int marker_draw_flag = DRAW_MARKERS_MARGIN; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc index 964738ff2c1..19fe61f0ed3 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc @@ -5,6 +5,7 @@ #include "MEM_guardedalloc.h" #include "BLI_color.hh" +#include "BLI_cpp_type.hh" #include "BLI_hash.hh" #include "BLI_math_vec_types.hh" #include "BLI_string.h" @@ -12,14 +13,12 @@ #include "BKE_geometry_set.hh" -#include "FN_cpp_type.hh" - #include "spreadsheet_column.hh" #include "spreadsheet_column_values.hh" namespace blender::ed::spreadsheet { -eSpreadsheetColumnValueType cpp_type_to_column_type(const fn::CPPType &type) +eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type) { if (type.is<bool>()) { return SPREADSHEET_VALUE_TYPE_BOOL; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index 454518016bc..7cf9238d34e 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -4,15 +4,14 @@ #include "DNA_space_types.h" +#include "BLI_generic_virtual_array.hh" #include "BLI_string_ref.hh" -#include "FN_generic_virtual_array.hh" - namespace blender::ed::spreadsheet { struct CellDrawParams; -eSpreadsheetColumnValueType cpp_type_to_column_type(const fn::CPPType &type); +eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type); /** * This represents a column in a spreadsheet. It has a name and provides a value for all the cells @@ -22,10 +21,10 @@ class ColumnValues final { protected: std::string name_; - fn::GVArray data_; + GVArray data_; public: - ColumnValues(std::string name, fn::GVArray data) : name_(std::move(name)), data_(std::move(data)) + ColumnValues(std::string name, GVArray data) : name_(std::move(name)), data_(std::move(data)) { /* The array should not be empty. */ BLI_assert(data_); @@ -48,7 +47,7 @@ class ColumnValues final { return data_.size(); } - const fn::GVArray &data() const + const GVArray &data() const { return data_; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 3c94c466da1..0ad64db1b6d 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -53,11 +53,11 @@ void ExtraColumns::foreach_default_column_ids( std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( const SpreadsheetColumnID &column_id) const { - const fn::GSpan *values = columns_.lookup_ptr(column_id.name); + const GSpan *values = columns_.lookup_ptr(column_id.name); if (values == nullptr) { return {}; } - return std::make_unique<ColumnValues>(column_id.name, fn::GVArray::ForSpan(*values)); + return std::make_unique<ColumnValues>(column_id.name, GVArray::ForSpan(*values)); } void GeometryDataSource::foreach_default_column_ids( @@ -199,7 +199,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( if (!attribute) { return {}; } - fn::GVArray varray = std::move(attribute.varray); + GVArray varray = std::move(attribute.varray); if (attribute.domain != domain_) { return {}; } @@ -462,7 +462,7 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, } if (const geo_log::GenericValueLog *generic_value_log = dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { - fn::GPointer value = generic_value_log->value(); + GPointer value = generic_value_log->value(); r_fields.add("Viewer", fn::make_constant_field(*value.type(), value.get())); } } @@ -508,7 +508,7 @@ class GeometryComponentCacheValue : public SpreadsheetCache::Value { public: /* Stores the result of fields evaluated on a geometry component. Without this, fields would have * to be reevaluated on every redraw. */ - Map<std::pair<AttributeDomain, GField>, fn::GArray<>> arrays; + Map<std::pair<AttributeDomain, GField>, GArray<>> arrays; }; static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, @@ -529,8 +529,8 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, const GField &field = item.value; /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ - fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { - fn::GArray<> evaluated_array(field.cpp_type(), domain_size); + GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { + GArray<> evaluated_array(field.cpp_type(), domain_size); bke::GeometryComponentFieldContext field_context{component, domain}; fn::FieldEvaluator field_evaluator{field_context, domain_size}; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 303f495e3df..8b281e5a558 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -21,10 +21,10 @@ namespace blender::ed::spreadsheet { class ExtraColumns { private: /** Maps column names to their data. The data is actually stored in the spreadsheet cache. */ - Map<std::string, fn::GSpan> columns_; + Map<std::string, GSpan> columns_; public: - void add(std::string name, fn::GSpan data) + void add(std::string name, GSpan data) { columns_.add(std::move(name), data); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 33fd7329e6d..db466f8ccf3 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -89,7 +89,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { return; } - const fn::GVArray &data = column.data(); + const GVArray &data = column.data(); if (data.type().is<int>()) { const int value = data.get<int>(real_index); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index 1fddd751d78..e45317c2a5c 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -42,7 +42,7 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, Vector<int64_t> &new_indices) { const ColumnValues &column = *columns.lookup(row_filter.column_name); - const fn::GVArray &column_data = column.data(); + const GVArray &column_data = column.data(); if (column_data.type().is<float>()) { const float value = row_filter.value_float; switch (row_filter.operation) { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 52b4fc1d8aa..d9388bc82ef 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -43,6 +43,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "BKE_action.h" #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_curve.h" @@ -1534,7 +1535,7 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* keyingset to use (dynamic enum) */ + /* #Object.id.name to select (dynamic enum). */ prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Object Name", ""); RNA_def_enum_funcs(prop, object_select_menu_enum_itemf); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE); @@ -1548,16 +1549,18 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -static Base *object_mouse_select_menu(bContext *C, - ViewContext *vc, - const GPUSelectResult *buffer, - const int hits, - const int mval[2], - bool extend, - bool deselect, - bool toggle) +/** + * \return True when a menu was activated. + */ +static bool object_mouse_select_menu(bContext *C, + ViewContext *vc, + const GPUSelectResult *buffer, + const int hits, + const int mval[2], + const struct SelectPick_Params *params, + Base **r_basact) { - short baseCount = 0; + int base_count = 0; bool ok; LinkNodePair linklist = {NULL, NULL}; @@ -1586,23 +1589,26 @@ static Base *object_mouse_select_menu(bContext *C, } if (ok) { - baseCount++; + base_count++; BLI_linklist_append(&linklist, base); - if (baseCount == SEL_MENU_SIZE) { + if (base_count == SEL_MENU_SIZE) { break; } } } CTX_DATA_END; - if (baseCount == 0) { - return NULL; + *r_basact = NULL; + + if (base_count == 0) { + return false; } - if (baseCount == 1) { + if (base_count == 1) { Base *base = (Base *)linklist.list->link; BLI_linklist_free(linklist.list, NULL); - return base; + *r_basact = base; + return false; } /* UI, full in static array values that we later use in an enum function */ @@ -1624,22 +1630,25 @@ static Base *object_mouse_select_menu(bContext *C, PointerRNA ptr; WM_operator_properties_create_ptr(&ptr, ot); - RNA_boolean_set(&ptr, "extend", extend); - RNA_boolean_set(&ptr, "deselect", deselect); - RNA_boolean_set(&ptr, "toggle", toggle); + RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD); + RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB); + RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR); WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); WM_operator_properties_free(&ptr); BLI_linklist_free(linklist.list, NULL); - return NULL; + return true; } static int bone_select_menu_exec(bContext *C, wmOperator *op) { const int name_index = RNA_enum_get(op->ptr, "name"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect = RNA_boolean_get(op->ptr, "deselect"); - const bool toggle = RNA_boolean_get(op->ptr, "toggle"); + + const struct SelectPick_Params params = { + .sel_op = ED_select_op_from_booleans(RNA_boolean_get(op->ptr, "extend"), + RNA_boolean_get(op->ptr, "deselect"), + RNA_boolean_get(op->ptr, "toggle")), + }; View3D *v3d = CTX_wm_view3d(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1653,21 +1662,20 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op) BLI_assert(BASE_SELECTABLE(v3d, basact)); - if (basact->object->mode == OB_MODE_EDIT) { + if (basact->object->mode & OB_MODE_EDIT) { EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr; - ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, extend, deselect, toggle); + ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, ¶ms); } else { bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr; - ED_armature_pose_select_pick_bone( - view_layer, v3d, basact->object, pchan->bone, extend, deselect, toggle); + ED_armature_pose_select_pick_bone(view_layer, v3d, basact->object, pchan->bone, ¶ms); } /* Weak but ensures we activate the menu again before using the enum. */ memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data)); /* We make the armature selected: - * Not-selected active object in posemode won't work well for tools. */ + * Not-selected active object in pose-mode won't work well for tools. */ ED_object_base_select(basact, BA_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); @@ -1675,14 +1683,22 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op) /* In weight-paint, we use selected bone to select vertex-group, * so don't switch to new active object. */ - if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) { - /* Prevent activating. - * Selection causes this to be considered the 'active' pose in weight-paint mode. - * Eventually this limitation may be removed. - * For now, de-select all other pose objects deforming this mesh. */ - ED_armature_pose_select_in_wpaint_mode(view_layer, basact); - - basact = NULL; + if (oldbasact) { + if (basact->object->mode & OB_MODE_EDIT) { + /* Pass. */ + } + else if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) { + /* Prevent activating. + * Selection causes this to be considered the 'active' pose in weight-paint mode. + * Eventually this limitation may be removed. + * For now, de-select all other pose objects deforming this mesh. */ + ED_armature_pose_select_in_wpaint_mode(view_layer, basact); + } + else { + if (oldbasact != basact) { + ED_object_base_activate(C, basact); + } + } } /* Undo? */ @@ -1712,7 +1728,7 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* keyingset to use (dynamic enum) */ + /* #Object.id.name to select (dynamic enum). */ prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Bone Name", ""); RNA_def_enum_funcs(prop, object_select_menu_enum_itemf); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE); @@ -1725,17 +1741,19 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot) prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } + +/** + * \return True when a menu was activated. + */ static bool bone_mouse_select_menu(bContext *C, const GPUSelectResult *buffer, const int hits, const bool is_editmode, - const bool extend, - const bool deselect, - const bool toggle) + const struct SelectPick_Params *params) { BLI_assert(buffer); - short baseCount = 0; + int bone_count = 0; LinkNodePair base_list = {NULL, NULL}; LinkNodePair bone_list = {NULL, NULL}; GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu"); @@ -1794,12 +1812,12 @@ static bool bone_mouse_select_menu(bContext *C, const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr); if (!is_duplicate_bone) { - baseCount++; + bone_count++; BLI_linklist_append(&base_list, bone_base); BLI_linklist_append(&bone_list, bone_ptr); BLI_gset_insert(added_bones, bone_ptr); - if (baseCount == SEL_MENU_SIZE) { + if (bone_count == SEL_MENU_SIZE) { break; } } @@ -1807,10 +1825,10 @@ static bool bone_mouse_select_menu(bContext *C, BLI_gset_free(added_bones, NULL); - if (baseCount == 0) { + if (bone_count == 0) { return false; } - if (baseCount == 1) { + if (bone_count == 1) { BLI_linklist_free(base_list.list, NULL); BLI_linklist_free(bone_list.list, NULL); return false; @@ -1847,9 +1865,9 @@ static bool bone_mouse_select_menu(bContext *C, PointerRNA ptr; WM_operator_properties_create_ptr(&ptr, ot); - RNA_boolean_set(&ptr, "extend", extend); - RNA_boolean_set(&ptr, "deselect", deselect); - RNA_boolean_set(&ptr, "toggle", toggle); + RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD); + RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB); + RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR); WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); WM_operator_properties_free(&ptr); @@ -2030,6 +2048,40 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, } /** + * Compare result of 'GPU_select': 'GPUSelectResult', + * Needed for stable sorting, so cycling through all items near the cursor behaves predictably. + */ +static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p) +{ + GPUSelectResult *a = (GPUSelectResult *)sel_a_p; + GPUSelectResult *b = (GPUSelectResult *)sel_b_p; + + if (a->depth < b->depth) { + return -1; + } + if (a->depth > b->depth) { + return 1; + } + + /* Depths match, sort by id. */ + uint sel_a = a->id; + uint sel_b = b->id; + +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&sel_a); + BLI_endian_switch_uint32(&sel_b); +#endif + + if (sel_a < sel_b) { + return -1; + } + if (sel_a > sel_b) { + return 1; + } + return 0; +} + +/** * \param has_bones: When true, skip non-bone hits, also allow bases to be used * that are visible but not select-able, * since you may be in pose mode with an un-selectable object. @@ -2039,115 +2091,204 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, static Base *mouse_select_eval_buffer(ViewContext *vc, const GPUSelectResult *buffer, int hits, - Base *startbase, - bool has_bones, bool do_nearest, - int *r_sub_selection) + bool has_bones, + bool do_bones_get_priotity, + int *r_select_id_subelem) { ViewLayer *view_layer = vc->view_layer; View3D *v3d = vc->v3d; - Base *base, *basact = NULL; int a; - int sub_selection_id = 0; + + bool found = false; + int select_id = 0; + int select_id_subelem = 0; if (do_nearest) { uint min = 0xFFFFFFFF; - int selcol = 0, notcol = 0; + int hit_index = -1; - if (has_bones) { + if (has_bones && do_bones_get_priotity) { /* we skip non-bone hits */ for (a = 0; a < hits; a++) { if (min > buffer[a].depth && (buffer[a].id & 0xFFFF0000)) { min = buffer[a].depth; - selcol = buffer[a].id & 0xFFFF; - sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16; + hit_index = a; } } } else { - /* only exclude active object when it is selected... */ - if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED) && hits > 1) { - notcol = BASACT(view_layer)->object->runtime.select_id; - } for (a = 0; a < hits; a++) { - if (min > buffer[a].depth && notcol != (buffer[a].id & 0xFFFF)) { + /* Any object. */ + if (min > buffer[a].depth) { min = buffer[a].depth; - selcol = buffer[a].id & 0xFFFF; - sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16; + hit_index = a; } } - } - base = FIRSTBASE(view_layer); - while (base) { - if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) { - if (base->object->runtime.select_id == selcol) { - break; + /* Find the best active & non-active hits. + * NOTE(@campbellbarton): Checking if `hits > 1` isn't a reliable way to know + * if there are multiple objects selected since it's possible the same object + * generates multiple hits, either from: + * - Multiple sub-components (bones & camera tracks). + * - Multiple selectable elements such as the object center and the geometry. + * + * For this reason, keep track of the best hit as well as the best hit that + * excludes the selected & active object, using this value when it's valid. */ + if ((hit_index != -1) && + /* Special case, cycling away from the active object should only be done when it + * doesn't have a bone selection, otherwise selecting sub-elements is difficult. */ + ((buffer[hit_index].id & 0xFFFF0000) == 0) && + /* Only exclude active object when it is selected. */ + (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) && + /* Allow disabling this behavior entirely. */ + (U.experimental.use_select_nearest_on_first_click == false)) { + + const int select_id_active = BASACT(view_layer)->object->runtime.select_id; + + /* Check if `hit_index` is the current active object. */ + if ((buffer[hit_index].id & 0xFFFF) == select_id_active) { + uint min_not_active = 0xFFFFFFFF; + int hit_index_not_active = -1; + for (a = 0; a < hits; a++) { + /* Any object other than the active-selected. */ + if (select_id_active == (buffer[a].id & 0xFFFF)) { + continue; + } + if (min_not_active > buffer[a].depth) { + min_not_active = buffer[a].depth; + hit_index_not_active = a; + } + } + + /* When the active was selected, first try to use the index + * for the best non-active hit that was found. */ + if (hit_index_not_active != -1) { + hit_index = hit_index_not_active; + } } } - base = base->next; } - if (base) { - basact = base; + + if (hit_index != -1) { + select_id = buffer[hit_index].id & 0xFFFF; + select_id_subelem = (buffer[hit_index].id & 0xFFFF0000) >> 16; + found = true; + /* No need to set `min` to `buffer[hit_index].depth`, it's not used from now on. */ } } else { - base = startbase; - while (base) { - /* skip objects with select restriction, to prevent prematurely ending this loop - * with an un-selectable choice */ - if (has_bones ? (base->flag & BASE_VISIBLE_VIEWLAYER) == 0 : - (base->flag & BASE_SELECTABLE) == 0) { - base = base->next; - if (base == NULL) { - base = FIRSTBASE(view_layer); - } - if (base == startbase) { - break; + { + GPUSelectResult *buffer_sorted = MEM_mallocN(sizeof(*buffer_sorted) * hits, __func__); + memcpy(buffer_sorted, buffer, sizeof(*buffer_sorted) * hits); + /* Remove non-bone objects. */ + if (has_bones && do_bones_get_priotity) { + /* Loop backwards to reduce re-ordering. */ + for (a = hits - 1; a >= 0; a--) { + if ((buffer_sorted[a].id & 0xFFFF0000) == 0) { + buffer_sorted[a] = buffer_sorted[--hits]; + } } } + qsort(buffer_sorted, hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp); + buffer = buffer_sorted; + } - if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) { - for (a = 0; a < hits; a++) { - if (has_bones) { - /* skip non-bone objects */ - if (buffer[a].id & 0xFFFF0000) { - if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) { - basact = base; - } - } - } - else { - if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) { - basact = base; - } + int hit_index = -1; + + /* It's possible there are no hits (all objects contained bones). */ + if (hits > 0) { + /* Only exclude active object when it is selected. */ + if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) { + const int select_id_active = BASACT(view_layer)->object->runtime.select_id; + for (int i_next = 0, i_prev = hits - 1; i_next < hits; i_prev = i_next++) { + if ((select_id_active == (buffer[i_prev].id & 0xFFFF)) && + (select_id_active != (buffer[i_next].id & 0xFFFF))) { + hit_index = i_next; + break; } } } - if (basact) { - break; + /* When the active object is unselected or not in `buffer`, use the nearest. */ + if (hit_index == -1) { + /* Just pick the nearest. */ + hit_index = 0; } + } - base = base->next; - if (base == NULL) { - base = FIRSTBASE(view_layer); - } - if (base == startbase) { - break; - } + if (hit_index != -1) { + select_id = buffer[hit_index].id & 0xFFFF; + select_id_subelem = (buffer[hit_index].id & 0xFFFF0000) >> 16; + found = true; } + MEM_freeN((void *)buffer); } - if (basact && r_sub_selection) { - *r_sub_selection = sub_selection_id; + Base *basact = NULL; + if (found) { + for (Base *base = FIRSTBASE(view_layer); base; base = base->next) { + if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) { + if (base->object->runtime.select_id == select_id) { + basact = base; + break; + } + } + } + + if (basact && r_select_id_subelem) { + *r_select_id_subelem = select_id_subelem; + } } return basact; } +static Base *mouse_select_object_center(ViewContext *vc, Base *startbase, const int mval[2]) +{ + ARegion *region = vc->region; + ViewLayer *view_layer = vc->view_layer; + View3D *v3d = vc->v3d; + + Base *oldbasact = BASACT(view_layer); + + const float mval_fl[2] = {(float)mval[0], (float)mval[1]}; + float dist = ED_view3d_select_dist_px() * 1.3333f; + Base *basact = NULL; + + /* Put the active object at a disadvantage to cycle through other objects. */ + const float penalty_dist = 10.0f * UI_DPI_FAC; + Base *base = startbase; + while (base) { + if (BASE_SELECTABLE(v3d, base)) { + float screen_co[2]; + if (ED_view3d_project_float_global( + region, base->object->obmat[3], screen_co, V3D_PROJ_TEST_CLIP_DEFAULT) == + V3D_PROJ_RET_OK) { + float dist_test = len_manhattan_v2v2(mval_fl, screen_co); + if (base == oldbasact) { + dist_test += penalty_dist; + } + if (dist_test < dist) { + dist = dist_test; + basact = base; + } + } + } + base = base->next; + + if (base == NULL) { + base = FIRSTBASE(view_layer); + } + if (base == startbase) { + break; + } + } + return basact; +} + static Base *ed_view3d_give_base_under_cursor_ex(bContext *C, const int mval[2], int *r_material_slot) @@ -2176,13 +2317,8 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C, if (hits > 0) { const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits); - basact = mouse_select_eval_buffer(&vc, - buffer, - hits, - vc.view_layer->object_bases.first, - has_bones, - do_nearest, - r_material_slot); + basact = mouse_select_eval_buffer( + &vc, buffer, hits, do_nearest, has_bones, true, r_material_slot); } return basact; @@ -2237,346 +2373,483 @@ static void deselect_all_tracks(MovieTracking *tracking) } } -/* mval is region coords */ -static bool ed_object_select_pick(bContext *C, - const int mval[2], - bool extend, - bool deselect, - bool toggle, - bool obcenter, - bool enumerate, - bool object) +static bool ed_object_select_pick_camera_track(bContext *C, + Scene *scene, + Base *basact, + MovieClip *clip, + const struct GPUSelectResult *buffer, + const short hits, + const struct SelectPick_Params *params) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewContext vc; - /* setup view context for argument to callbacks */ - ED_view3d_viewcontext_init(C, &vc, depsgraph); + bool changed = false; + bool found = false; - const ARegion *region = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - /* Don't set when the context has no active object (hidden), see: T60807. */ - const Base *oldbasact = vc.obact ? BASACT(view_layer) : NULL; - Base *base, *startbase = NULL, *basact = NULL; - const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT; - bool is_obedit; - float dist = ED_view3d_select_dist_px() * 1.3333f; - bool retval = false; - int hits; - const float mval_fl[2] = {(float)mval[0], (float)mval[1]}; + MovieTracking *tracking = &clip->tracking; + ListBase *tracksbase = NULL; + MovieTrackingTrack *track = NULL; - is_obedit = (vc.obedit != NULL); - if (object) { - /* Signal for #view3d_opengl_select to skip edit-mode objects. */ - vc.obedit = NULL; - } + for (int i = 0; i < hits; i++) { + const int hitresult = buffer[i].id; - /* In pose mode we don't want to mess with object selection. */ - const bool is_pose_mode = (vc.obact && vc.obact->mode & OB_MODE_POSE); + /* If there's bundles in buffer select bundles first, + * so non-camera elements should be ignored in buffer. */ + if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) { + continue; + } + /* Index of bundle is 1<<16-based. if there's no "bone" index + * in height word, this buffer value belongs to camera. not to bundle. */ + if ((hitresult & 0xFFFF0000) == 0) { + continue; + } - /* always start list from basact in wire mode */ - startbase = FIRSTBASE(view_layer); - if (oldbasact && oldbasact->next) { - startbase = oldbasact->next; + track = BKE_tracking_track_get_indexed(&clip->tracking, hitresult >> 16, &tracksbase); + found = true; + break; } - /* This block uses the control key to make the object selected - * by its center point rather than its contents */ - - /* In edit-mode do not activate. */ - if (obcenter) { - - /* NOTE: shift+alt goes to group-flush-selecting. */ - if (enumerate) { - basact = object_mouse_select_menu(C, &vc, NULL, 0, mval, extend, deselect, toggle); + /* Note `params->deselect_all` is ignored for tracks as in this case + * all objects will be de-selected (not tracks). */ + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && TRACK_SELECTED(track)) { + found = false; } - else { - base = startbase; - while (base) { - if (BASE_SELECTABLE(v3d, base)) { - float screen_co[2]; - if (ED_view3d_project_float_global( - region, base->object->obmat[3], screen_co, V3D_PROJ_TEST_CLIP_DEFAULT) == - V3D_PROJ_RET_OK) { - float dist_temp = len_manhattan_v2v2(mval_fl, screen_co); - if (base == oldbasact) { - dist_temp += 10.0f; - } - if (dist_temp < dist) { - dist = dist_temp; - basact = base; - } - } - } - base = base->next; + else if (found /* `|| params->deselect_all` */) { + /* Deselect everything. */ + deselect_all_tracks(tracking); + changed = true; + } + } - if (base == NULL) { - base = FIRSTBASE(view_layer); + if (found) { + switch (params->sel_op) { + case SEL_OP_ADD: { + BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true); + break; + } + case SEL_OP_SUB: { + BKE_tracking_track_deselect(track, TRACK_AREA_ALL); + break; + } + case SEL_OP_XOR: { + if (TRACK_SELECTED(track)) { + BKE_tracking_track_deselect(track, TRACK_AREA_ALL); } - if (base == startbase) { - break; + else { + BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true); } + break; } - } - if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { - if (is_obedit == false) { - if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) { - if (object_mode == OB_MODE_OBJECT) { - struct Main *bmain = CTX_data_main(C); - ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object); - } - if (!BKE_object_is_mode_compat(basact->object, object_mode)) { - basact = NULL; - } - } + case SEL_OP_SET: { + BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, false); + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } } + + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&clip->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + changed = true; } - else { - GPUSelectResult buffer[MAXPICKELEMS]; - bool do_nearest; - // TIMEIT_START(select_time); + return changed || found; +} - /* if objects have posemode set, the bones are in the same selection buffer */ - const eV3DSelectObjectFilter select_filter = ((object == false) ? - ED_view3d_select_filter_from_mode(scene, - vc.obact) : - VIEW3D_SELECT_FILTER_NOP); - hits = mixed_bones_object_selectbuffer_extended( - &vc, buffer, ARRAY_SIZE(buffer), mval, select_filter, true, enumerate, &do_nearest); +/** + * Cursor selection picking for object & pose-mode. + * + * \param mval: Region relative cursor coordinates. + * \param params: Selection parameters. + * \param center: Select by the cursors on-screen distances to the center/origin + * instead of the geometry any other contents of the item being selected. + * This could be used to select by bones by their origin too, currently it's only used for objects. + * \param enumerate: Show a menu for objects at the cursor location. + * Otherwise fall-through to non-menu selection. + * \param object_only: Only select objects (not bones / track markers). + */ +static bool ed_object_select_pick(bContext *C, + const int mval[2], + const struct SelectPick_Params *params, + const bool center, + const bool enumerate, + const bool object_only) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewContext vc; + /* Setup view context for argument to callbacks. */ + ED_view3d_viewcontext_init(C, &vc, depsgraph); - // TIMEIT_END(select_time); + Scene *scene = vc.scene; + View3D *v3d = vc.v3d; - if (hits > 0) { - /* NOTE: bundles are handling in the same way as bones. */ - const bool has_bones = object ? false : selectbuffer_has_bones(buffer, hits); + /* Menu activation may find a base to make active (if it only finds a single item to select). */ + Base *basact_override = NULL; - /* NOTE: shift+alt goes to group-flush-selecting. */ - if (enumerate) { - if (has_bones && - bone_mouse_select_menu(C, buffer, hits, false, extend, deselect, toggle)) { - basact = NULL; + const bool is_obedit = (vc.obedit != NULL); + if (object_only) { + /* Signal for #view3d_opengl_select to skip edit-mode objects. */ + vc.obedit = NULL; + } + + /* Set for GPU depth buffer picking, leave NULL when selecting by center. */ + struct { + GPUSelectResult buffer[MAXPICKELEMS]; + int hits; + bool do_nearest; + bool has_bones; + } *gpu = NULL; + + /* First handle menu selection, early exit if a menu opens + * since this takes ownership of the selection action. + * + * Even when there is no menu `basact_override` may be set to avoid having to re-find + * the item under the cursor. */ + + if (center == false) { + gpu = MEM_mallocN(sizeof(*gpu), __func__); + gpu->do_nearest = false; + gpu->has_bones = false; + + /* If objects have pose-mode set, the bones are in the same selection buffer. */ + const eV3DSelectObjectFilter select_filter = ((object_only == false) ? + ED_view3d_select_filter_from_mode(scene, + vc.obact) : + VIEW3D_SELECT_FILTER_NOP); + gpu->hits = mixed_bones_object_selectbuffer_extended(&vc, + gpu->buffer, + ARRAY_SIZE(gpu->buffer), + mval, + select_filter, + true, + enumerate, + &gpu->do_nearest); + gpu->has_bones = (object_only && gpu->hits > 0) ? + false : + selectbuffer_has_bones(gpu->buffer, gpu->hits); + } + + /* First handle menu selection, early exit when a menu was opened. + * Otherwise fall through to regular selection. */ + if (enumerate) { + bool has_menu = false; + if (center) { + if (object_mouse_select_menu(C, &vc, NULL, 0, mval, params, &basact_override)) { + has_menu = true; + } + } + else { + if (gpu->hits != 0) { + if (gpu->has_bones && bone_mouse_select_menu(C, gpu->buffer, gpu->hits, false, params)) { + has_menu = true; } - else { - basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle); + else if (object_mouse_select_menu( + C, &vc, gpu->buffer, gpu->hits, mval, params, &basact_override)) { + has_menu = true; } } - else { - basact = mouse_select_eval_buffer( - &vc, buffer, hits, startbase, has_bones, do_nearest, NULL); - } - - if (has_bones && basact) { - if (basact->object->type == OB_CAMERA) { - MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false); - if (clip != NULL && oldbasact == basact) { - bool changed = false; - - for (int i = 0; i < hits; i++) { - const int hitresult = buffer[i].id; - - /* if there's bundles in buffer select bundles first, - * so non-camera elements should be ignored in buffer */ - if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) { - continue; - } - - /* index of bundle is 1<<16-based. if there's no "bone" index - * in height word, this buffer value belongs to camera. not to bundle - */ - if (hitresult & 0xFFFF0000) { - MovieTracking *tracking = &clip->tracking; - ListBase *tracksbase; - MovieTrackingTrack *track; - - track = BKE_tracking_track_get_indexed( - &clip->tracking, hitresult >> 16, &tracksbase); + } - if (TRACK_SELECTED(track) && extend) { - changed = false; - BKE_tracking_track_deselect(track, TRACK_AREA_ALL); - } - else { - int oldsel = TRACK_SELECTED(track) ? 1 : 0; - if (!extend) { - deselect_all_tracks(tracking); - } + /* Let the menu handle any further actions. */ + if (has_menu) { + if (gpu != NULL) { + MEM_freeN(gpu); + } + return false; + } + } - BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, extend); + /* No menu, continue with selection. */ - if (oldsel != (TRACK_SELECTED(track) ? 1 : 0)) { - changed = true; - } - } + ViewLayer *view_layer = vc.view_layer; + /* Don't set when the context has no active object (hidden), see: T60807. */ + const Base *oldbasact = vc.obact ? BASACT(view_layer) : NULL; + /* Always start list from `basact` when cycling the selection. */ + Base *startbase = (oldbasact && oldbasact->next) ? oldbasact->next : FIRSTBASE(view_layer); - ED_object_base_select(basact, BA_SELECT); + /* The next object's base to make active. */ + Base *basact = NULL; + const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT; - retval = true; + /* When enabled, don't attempt any further selection. */ + bool handled = false; - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - DEG_id_tag_update(&clip->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + /* Split `changed` into data-types so their associated updates can be properly performed. + * This is also needed as multiple changes may happen at once. + * Selecting a pose-bone or track can also select the object for e.g. */ + bool changed_object = false; + bool changed_pose = false; + bool changed_track = false; - break; - } - } + /* Handle setting the new base active (even when `handled == true`). */ + bool use_activate_selected_base = false; - if (!changed) { - /* fallback to regular object selection if no new bundles were selected, - * allows to select object parented to reconstruction object */ - basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest, NULL); - } + if (center) { + if (basact_override) { + basact = basact_override; + } + else { + basact = mouse_select_object_center(&vc, startbase, mval); + } + } + else { + if (basact_override) { + basact = basact_override; + } + else { + /* Regarding bone priority. + * + * - When in pose-bone, it's useful that any selection containing a bone + * gets priority over other geometry (background scenery for example). + * + * - When in object-mode, don't prioritize bones as it would cause + * pose-objects behind other objects to get priority + * (mainly noticeable when #SCE_OBJECT_MODE_LOCK is disabled). + * + * This way prioritizing based on pose-mode has a bias to stay in pose-mode + * without having to enforce this through locking the object mode. */ + bool do_bones_get_priotity = (object_mode & OB_MODE_POSE) != 0; + + basact = (gpu->hits > 0) ? mouse_select_eval_buffer(&vc, + gpu->buffer, + gpu->hits, + gpu->do_nearest, + gpu->has_bones, + do_bones_get_priotity, + NULL) : + NULL; + } + + /* Select pose-bones or camera-tracks. */ + if (((gpu->hits > 0) && gpu->has_bones) || + /* Special case, even when there are no hits, pose logic may de-select all bones. */ + ((gpu->hits == 0) && (object_mode & OB_MODE_POSE))) { + + if (basact && (gpu->has_bones && (basact->object->type == OB_CAMERA))) { + MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false); + if (clip != NULL) { + if (ed_object_select_pick_camera_track( + C, scene, basact, clip, gpu->buffer, gpu->hits, params)) { + ED_object_base_select(basact, BA_SELECT); + /* Don't set `handled` here as the object activation may be necessary. */ + changed_object = true; + + changed_track = true; + } + else { + /* Fallback to regular object selection if no new bundles were selected, + * allows to select object parented to reconstruction object. */ + basact = mouse_select_eval_buffer( + &vc, gpu->buffer, gpu->hits, gpu->do_nearest, false, false, NULL); } } - else if (ED_armature_pose_select_pick_with_buffer(view_layer, - v3d, - basact, - buffer, - hits, - extend, - deselect, - toggle, - do_nearest)) { - /* then bone is found */ - - /* we make the armature selected: - * not-selected active object in posemode won't work well for tools */ + } + else if (ED_armature_pose_select_pick_with_buffer(view_layer, + v3d, + basact ? basact : (Base *)oldbasact, + gpu->buffer, + gpu->hits, + params, + gpu->do_nearest)) { + + changed_pose = true; + + /* When there is no `baseact` this will have operated on `oldbasact`, + * allowing #SelectPick_Params.deselect_all work in pose-mode. + * In this case no object operations are needed. */ + if (basact != NULL) { + /* By convention the armature-object is selected when in pose-mode. + * While leaving it unselected will work, leaving pose-mode would leave the object + * active + unselected which isn't ideal when performing other actions on the object. */ ED_object_base_select(basact, BA_SELECT); + changed_object = true; - retval = true; WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object); - DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); - /* In weight-paint, we use selected bone to select vertex-group, - * so don't switch to new active object. */ - if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) { - /* Prevent activating. - * Selection causes this to be considered the 'active' pose in weight-paint mode. - * Eventually this limitation may be removed. - * For now, de-select all other pose objects deforming this mesh. */ - ED_armature_pose_select_in_wpaint_mode(view_layer, basact); + /* In weight-paint, we use selected bone to select vertex-group. + * In this case the active object mustn't change as it would leave weight-paint mode. */ + if (oldbasact) { + if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) { + /* Prevent activating. + * Selection causes this to be considered the 'active' pose in weight-paint mode. + * Eventually this limitation may be removed. + * For now, de-select all other pose objects deforming this mesh. */ + ED_armature_pose_select_in_wpaint_mode(view_layer, basact); + + handled = true; + } + else if ((object_mode & OB_MODE_POSE) && (basact->object->mode & OB_MODE_POSE)) { + /* Within pose-mode, keep the current selection when switching pose bones, + * this is noticeable when in pose mode with multiple objects at once. + * Where selecting the bone of a different object would de-select this one. + * After that, exiting pose-mode would only have the active armature selected. + * This matches multi-object edit-mode behavior. */ + handled = true; + + if (oldbasact != basact) { + use_activate_selected_base = true; + } + } + else { + /* Don't set `handled` here as the object selection may be necessary + * when starting out in object-mode and moving into pose-mode, + * when moving from pose to object-mode using object selection also makes sense. */ + } + } + } + } + /* Prevent bone/track selecting to pass on to object selecting. */ + if (basact == oldbasact) { + handled = true; + } + } + } + if (handled == false) { + if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + /* No special logic in edit-mode. */ + if (is_obedit == false) { + if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) { + if (object_mode == OB_MODE_OBJECT) { + struct Main *bmain = vc.bmain; + ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object); + } + if (!BKE_object_is_mode_compat(basact->object, object_mode)) { basact = NULL; } } - /* prevent bone selecting to pass on to object selecting */ - if (basact == oldbasact) { - basact = NULL; - } - } - if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { - if (is_obedit == false) { - if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) { - if (object_mode == OB_MODE_OBJECT) { - struct Main *bmain = CTX_data_main(C); - ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object); - } - if (!BKE_object_is_mode_compat(basact->object, object_mode)) { - basact = NULL; - } + /* Disallow switching modes, + * special exception for edit-mode - vertex-parent operator. */ + if (basact && oldbasact) { + if ((oldbasact->object->mode != basact->object->mode) && + (oldbasact->object->mode & basact->object->mode) == 0) { + basact = NULL; } } } } } - if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { - /* Disallow switching modes, - * special exception for edit-mode - vertex-parent operator. */ - if (is_obedit == false) { - if (oldbasact && basact) { - if ((oldbasact->object->mode != basact->object->mode) && - (oldbasact->object->mode & basact->object->mode) == 0) { - basact = NULL; + /* Ensure code above doesn't change the active base. This code is already fairly involved, + * it's best if changing the active object is localized to a single place. */ + BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL)); + + bool found = (basact != NULL); + if ((handled == false) && (vc.obedit == NULL)) { + /* Object-mode (pose mode will have been handled already). */ + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (basact->flag & BASE_SELECTED)) { + found = false; + /* NOTE(@campbellbarton): Experimental behavior to set active even keeping the selection + * without this it's inconvenient to set the active object. */ + if (basact != oldbasact) { + use_activate_selected_base = true; + } + } + else if (found || params->deselect_all) { + /* Deselect everything. */ + /* `basact` may be NULL. */ + if (ED_view3d_object_deselect_all_except(view_layer, basact)) { + changed_object = true; } } } } - /* Ensure code above doesn't change the active base. */ - BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL)); - - /* so, do we have something selected? */ - if (basact) { - retval = true; + if ((handled == false) && found) { if (vc.obedit) { - /* only do select */ + /* Only do the select (use for setting vertex parents & hooks). + * In edit-mode do not activate. */ ED_view3d_object_deselect_all_except(view_layer, basact); ED_object_base_select(basact, BA_SELECT); + + changed_object = true; } - /* also prevent making it active on mouse selection */ + /* Also prevent making it active on mouse selection. */ else if (BASE_SELECTABLE(v3d, basact)) { - const bool use_activate_selected_base = (oldbasact != basact) && (is_obedit == false); - if (extend) { - ED_object_base_select(basact, BA_SELECT); - } - else if (deselect) { - ED_object_base_select(basact, BA_DESELECT); - } - else if (toggle) { - if (basact->flag & BASE_SELECTED) { - /* Keep selected if the base is to be activated. */ - if (use_activate_selected_base == false) { - ED_object_base_select(basact, BA_DESELECT); - } - } - else { + use_activate_selected_base |= (oldbasact != basact) && (is_obedit == false); + + switch (params->sel_op) { + case SEL_OP_ADD: { ED_object_base_select(basact, BA_SELECT); + break; } - } - else { - /* When enabled, this puts other objects out of multi pose-mode. */ - if (is_pose_mode == false || (basact->object->mode & OB_MODE_POSE) == 0) { + case SEL_OP_SUB: { + ED_object_base_select(basact, BA_DESELECT); + break; + } + case SEL_OP_XOR: { + if (basact->flag & BASE_SELECTED) { + /* Keep selected if the base is to be activated. */ + if (use_activate_selected_base == false) { + ED_object_base_select(basact, BA_DESELECT); + } + } + else { + ED_object_base_select(basact, BA_SELECT); + } + break; + } + case SEL_OP_SET: { ED_view3d_object_deselect_all_except(view_layer, basact); ED_object_base_select(basact, BA_SELECT); + break; } - } - - if (use_activate_selected_base) { - ED_object_base_activate(C, basact); /* adds notifier */ - if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) { - WM_toolsystem_update_from_context_view3d(C); + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } } - /* Set special modes for grease pencil - * The grease pencil modes are not real modes, but a hack to make the interface - * consistent, so need some tricks to keep UI synchronized */ - /* XXX: This stuff needs reviewing (Aligorith) */ - if (false && (((oldbasact) && oldbasact->object->type == OB_GPENCIL) || - (basact->object->type == OB_GPENCIL))) { - /* set cursor */ - if (ELEM(basact->object->mode, - OB_MODE_PAINT_GPENCIL, - OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL, - OB_MODE_VERTEX_GPENCIL)) { - ED_gpencil_toggle_brush_cursor(C, true, NULL); - } - else { - /* TODO: maybe is better use restore */ - ED_gpencil_toggle_brush_cursor(C, false, NULL); - } - } + changed_object = true; } + } + /* Perform the activation even when 'handled', since this is used to ensure + * the object from the pose-bone selected is also activated. */ + if (use_activate_selected_base && (basact != NULL)) { + changed_object = true; + ED_object_base_activate(C, basact); /* adds notifier */ + if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) { + WM_toolsystem_update_from_context_view3d(C); + } + } + + if (changed_object) { DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + ED_outliner_select_sync_from_object_tag(C); } - return retval; + if (changed_pose) { + ED_outliner_select_sync_from_pose_bone_tag(C); + } + + if (gpu != NULL) { + MEM_freeN(gpu); + } + + return (changed_object || changed_pose || changed_track); } -/* mouse selection in weight paint */ -/* gets called via generic mouse select operator */ -static bool ed_wpaint_vertex_select_pick( - bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, Object *obact) +/** + * Mouse selection in weight paint. + * Called via generic mouse select operator. + * + * \return True when pick finds an element or the selection changed. + */ +static bool ed_wpaint_vertex_select_pick(bContext *C, + const int mval[2], + const struct SelectPick_Params *params, + Object *obact) { View3D *v3d = CTX_wm_view3d(C); const bool use_zbuf = !XRAY_ENABLED(v3d); @@ -2584,21 +2857,44 @@ static bool ed_wpaint_vertex_select_pick( Mesh *me = obact->data; /* already checked for NULL */ uint index = 0; MVert *mv; + bool changed = false; - if (ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index)) { - mv = &me->mvert[index]; - if (extend) { - mv->flag |= SELECT; - } - else if (deselect) { - mv->flag &= ~SELECT; + bool found = ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index); + + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (me->mvert[index].flag & SELECT)) { + found = false; } - else if (toggle) { - mv->flag ^= SELECT; + else if (found || params->deselect_all) { + /* Deselect everything. */ + changed |= paintface_deselect_all_visible(C, obact, SEL_DESELECT, false); } - else { - paintvert_deselect_all_visible(obact, SEL_DESELECT, false); - mv->flag |= SELECT; + } + + if (found) { + mv = &me->mvert[index]; + switch (params->sel_op) { + case SEL_OP_ADD: { + mv->flag |= SELECT; + break; + } + case SEL_OP_SUB: { + mv->flag &= ~SELECT; + break; + } + case SEL_OP_XOR: { + mv->flag ^= SELECT; + break; + } + case SEL_OP_SET: { + paintvert_deselect_all_visible(obact, SEL_DESELECT, false); + mv->flag |= SELECT; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; + } } /* update mselect */ @@ -2610,10 +2906,15 @@ static bool ed_wpaint_vertex_select_pick( } paintvert_flush_flags(obact); + + changed = true; + } + + if (changed) { paintvert_tag_select_update(C, obact); - return true; } - return false; + + return changed || found; } static int view3d_select_exec(bContext *C, wmOperator *op) @@ -2621,29 +2922,36 @@ static int view3d_select_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); Object *obact = CTX_data_active_object(C); - bool extend = RNA_boolean_get(op->ptr, "extend"); - bool deselect = RNA_boolean_get(op->ptr, "deselect"); - bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - bool toggle = RNA_boolean_get(op->ptr, "toggle"); + const struct SelectPick_Params params = { + .sel_op = ED_select_op_from_booleans(RNA_boolean_get(op->ptr, "extend"), + RNA_boolean_get(op->ptr, "deselect"), + RNA_boolean_get(op->ptr, "toggle")), + .deselect_all = RNA_boolean_get(op->ptr, "deselect_all"), + .select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough"), + + }; bool center = RNA_boolean_get(op->ptr, "center"); bool enumerate = RNA_boolean_get(op->ptr, "enumerate"); /* Only force object select for edit-mode to support vertex parenting, * or paint-select to allow pose bone select with vert/face select. */ - bool object = (RNA_boolean_get(op->ptr, "object") && - (obedit || BKE_paint_select_elem_test(obact) || - /* so its possible to select bones in weight-paint mode (LMB select) */ - (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT) && - BKE_object_pose_armature_get(obact)))); - - bool retval = false; - int location[2]; + bool object_only = (RNA_boolean_get(op->ptr, "object") && + (obedit || BKE_paint_select_elem_test(obact) || + /* so its possible to select bones in weight-paint mode (LMB select) */ + (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT) && + BKE_object_pose_armature_get(obact)))); + + /* This could be called "changed_or_found" since this is true when there is an element + * under the cursor to select, even if it happens that the selection & active state doesn't + * actually change. This is important so undo pushes are predictable. */ + bool changed = false; + int mval[2]; - RNA_int_get_array(op->ptr, "location", location); + RNA_int_get_array(op->ptr, "location", mval); view3d_operator_needs_opengl(C); BKE_object_update_select_id(CTX_data_main(C)); - if (object) { + if (object_only) { obedit = NULL; obact = NULL; @@ -2653,12 +2961,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op) center = false; } - if (obedit && object == false) { + if (obedit && object_only == false) { if (obedit->type == OB_MESH) { - retval = EDBM_select_pick(C, location, extend, deselect, toggle); - if (!retval && deselect_all) { - retval = EDBM_mesh_deselect_all_multi(C); - } + changed = EDBM_select_pick(C, mval, ¶ms); } else if (obedit->type == OB_ARMATURE) { if (enumerate) { @@ -2667,107 +2972,50 @@ static int view3d_select_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init(C, &vc, depsgraph); GPUSelectResult buffer[MAXPICKELEMS]; - const int hits = mixed_bones_object_selectbuffer(&vc, - buffer, - ARRAY_SIZE(buffer), - location, - VIEW3D_SELECT_FILTER_NOP, - false, - true, - false); - retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle); - } - if (!retval) { - retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle); + const int hits = mixed_bones_object_selectbuffer( + &vc, buffer, ARRAY_SIZE(buffer), mval, VIEW3D_SELECT_FILTER_NOP, false, true, false); + changed = bone_mouse_select_menu(C, buffer, hits, true, ¶ms); } - - if (!retval && deselect_all) { - retval = ED_armature_edit_deselect_all_visible_multi(C); - } - if (retval) { - ED_outliner_select_sync_from_edit_bone_tag(C); + if (!changed) { + changed = ED_armature_edit_select_pick(C, mval, ¶ms); } } else if (obedit->type == OB_LATTICE) { - retval = ED_lattice_select_pick(C, location, extend, deselect, toggle); - if (!retval && deselect_all) { - retval = ED_lattice_deselect_all_multi(C); - } + changed = ED_lattice_select_pick(C, mval, ¶ms); } else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { - retval = ED_curve_editnurb_select_pick(C, location, extend, deselect, toggle); - if (!retval && deselect_all) { - retval = ED_curve_deselect_all_multi(C); - } + changed = ED_curve_editnurb_select_pick(C, mval, ¶ms); } else if (obedit->type == OB_MBALL) { - retval = ED_mball_select_pick(C, location, extend, deselect, toggle); - if (!retval && deselect_all) { - retval = ED_mball_deselect_all_multi(C); - } + changed = ED_mball_select_pick(C, mval, ¶ms); } else if (obedit->type == OB_FONT) { - retval = ED_curve_editfont_select_pick(C, location, extend, deselect, toggle); - if (!retval && deselect_all) { - /* pass */ - } - } - if (retval) { - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + changed = ED_curve_editfont_select_pick(C, mval, ¶ms); } } else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) { - retval = PE_mouse_particles(C, location, extend, deselect, toggle); - if (!retval && deselect_all) { - retval = PE_deselect_all_visible(C); - } + changed = PE_mouse_particles(C, mval, ¶ms); } else if (obact && BKE_paint_select_face_test(obact)) { - retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle); - if (!retval && deselect_all) { - retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, true); - } + changed = paintface_mouse_select(C, mval, ¶ms, obact); } else if (BKE_paint_select_vert_test(obact)) { - retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact); - if (!retval && deselect_all) { - retval = paintvert_deselect_all_visible(obact, SEL_DESELECT, false); - if (retval) { - paintvert_tag_select_update(C, obact); - } - } + changed = ed_wpaint_vertex_select_pick(C, mval, ¶ms, obact); } else { - retval = ed_object_select_pick( - C, location, extend, deselect, toggle, center, enumerate, object); - if (!retval && deselect_all) { - if (ED_pose_object_from_context(C)) { - retval = ED_pose_deselect_all_multi(C, SEL_DESELECT, false); - } - else { - retval = ED_object_base_deselect_all( - CTX_data_view_layer(C), CTX_wm_view3d(C), SEL_DESELECT); - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - } - } - - if (retval) { - if (obact && obact->mode & OB_MODE_POSE) { - ED_outliner_select_sync_from_pose_bone_tag(C); - } - else { - ED_outliner_select_sync_from_object_tag(C); - } - } + changed = ed_object_select_pick(C, mval, ¶ms, center, enumerate, object_only); } + /* Pass-through flag may be cleared, see #WM_operator_flag_only_pass_through_on_press. */ + /* Pass-through allows tweaks * FINISHED to signal one operator worked */ - if (retval) { + if (changed) { WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } - return OPERATOR_PASS_THROUGH; /* nothing selected, just passthrough */ + /* Nothing selected, just passthrough. */ + return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED; } static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 92d312cebce..975f4370425 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1519,12 +1519,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) wmMsgParams_RNA msg_key_params = {{0}}; RNA_pointer_create(&t->scene->id, &RNA_ToolSettings, ts, &msg_key_params.ptr); - _WM_MESSAGE_EXTERN_BEGIN; - extern PropertyRNA rna_ToolSettings_use_snap; - extern PropertyRNA rna_ToolSettings_use_snap_node; - extern PropertyRNA rna_ToolSettings_use_snap_sequencer; - extern PropertyRNA rna_ToolSettings_use_snap_uv; - _WM_MESSAGE_EXTERN_END; if (t->spacetype == SPACE_NODE) { snap_flag_ptr = &ts->snap_flag_node; msg_key_params.prop = &rna_ToolSettings_use_snap_node; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 7ace6343985..bf898b9053f 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -567,10 +567,10 @@ static char snap_flag_from_spacetype(TransInfo *t) if (t->spacetype == SPACE_NODE) { return ts->snap_flag_node; } - else if (t->spacetype == SPACE_IMAGE) { + if (t->spacetype == SPACE_IMAGE) { return ts->snap_uv_flag; } - else if (t->spacetype == SPACE_SEQ) { + if (t->spacetype == SPACE_SEQ) { return ts->snap_flag_seq; } return ts->snap_flag; diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 8b7133892ff..87053fe03d1 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -440,6 +440,44 @@ typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx, bool is_object_active, void *data); +static bool snap_object_is_snappable(const SnapObjectContext *sctx, + const eSnapSelect snap_select, + const Base *base_act, + const Base *base, + const bool is_in_object_mode) +{ + if (!BASE_VISIBLE(sctx->runtime.v3d, base)) { + return false; + } + + if ((snap_select == SNAP_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) { + return true; + } + + if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) { + return false; + } + + if (snap_select == SNAP_NOT_ACTIVE) { + return base_act == base; + } + + if (snap_select == SNAP_NOT_SELECTED) { + if (is_in_object_mode) { + return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)); + } + + /* What is selectable or not is part of the object and depends on the mode. */ + return true; + } + + if (snap_select == SNAP_SELECTABLE) { + return (base->flag & BASE_SELECTABLE) != 0; + } + + return true; +} + /** * Walks through all objects in the scene to create the list of objects to snap. */ @@ -458,38 +496,13 @@ static void iter_snap_objects(SnapObjectContext *sctx, return; } + const bool is_in_object_mode = !base_act || base_act->object->mode == OB_MODE_OBJECT; for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { - if (!BASE_VISIBLE(sctx->runtime.v3d, base)) { - continue; - } - - if ((snap_select == SNAP_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) { - /* pass */ - } - else if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) { + if (!snap_object_is_snappable(sctx, snap_select, base_act, base, is_in_object_mode)) { continue; } const bool is_object_active = (base == base_act); - if (snap_select == SNAP_NOT_ACTIVE) { - if (is_object_active) { - continue; - } - } - else if (snap_select == SNAP_NOT_SELECTED) { - if (is_object_active && base->object->mode != OB_MODE_OBJECT) { - /* Pass. Consider the selection of elements being edited. */ - } - else if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) { - continue; - } - } - else if (snap_select == SNAP_SELECTABLE) { - if (!(base->flag & BASE_SELECTABLE)) { - continue; - } - } - Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object); if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { ListBase *lb = object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval); diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index b6893c3032a..380c7ed0e43 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -112,3 +112,17 @@ bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, return false; } + +eSelectOp ED_select_op_from_booleans(const bool extend, const bool deselect, const bool toggle) +{ + if (extend) { + return SEL_OP_ADD; + } + if (deselect) { + return SEL_OP_SUB; + } + if (toggle) { + return SEL_OP_XOR; + } + return SEL_OP_SET; +} diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 3f7c7745bff..5ad326c19e5 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -4021,7 +4021,7 @@ static void p_smooth(PChart *chart) PFace *f; int j, it2, maxiter2, it; int nedges = chart->nedges, nwheel, gridx, gridy; - int edgesx, edgesy, nsize, esize, i, x, y, maxiter, totiter; + int edgesx, edgesy, nsize, esize, i, x, y, maxiter; float minv[2], maxv[2], median, invmedian, avglen2d, avglen3d; float center[2], dx, dy, *nodes, dlimit, d, *oldnodesx, *oldnodesy; float *nodesx, *nodesy, *hedges, *vedges, climit, moved, padding; @@ -4185,7 +4185,6 @@ static void p_smooth(PChart *chart) /* smooth the grid */ maxiter = 10; - totiter = 0; climit = 0.00001f * nsize; for (it = 0; it < maxiter; it++) { @@ -4210,7 +4209,6 @@ static void p_smooth(PChart *chart) for (it2 = 0; it2 < maxiter2; it2++) { d = 0.0f; - totiter += 1; memcpy(oldnodesx, nodesx, sizeof(float) * nsize); memcpy(oldnodesy, nodesy, sizeof(float) * nsize); diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 938b798f4b6..ed4aa6985c4 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2388,12 +2388,11 @@ void UV_OT_select_all(wmOperatorType *ot) /** \name Mouse Select Operator * \{ */ -static int uv_mouse_select_multi(bContext *C, - Object **objects, - uint objects_len, - const float co[2], - const bool extend, - const bool deselect_all) +static bool uv_mouse_select_multi(bContext *C, + Object **objects, + uint objects_len, + const float co[2], + const struct SelectPick_Params *params) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); const ARegion *region = CTX_wm_region(C); @@ -2477,117 +2476,145 @@ static int uv_mouse_select_multi(bContext *C, } } - if (!found_item) { - if (deselect_all) { - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + bool found = found_item; + bool changed = false; + + bool is_selected = false; + if (found) { + Object *obedit = hit.ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + if (selectmode == UV_SELECT_FACE) { + is_selected = uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset); + } + else if (selectmode == UV_SELECT_EDGE) { + is_selected = uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset); + } + else { /* Vertex or island. */ + is_selected = uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset); + } + } + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && is_selected) { + found = false; + } + else if (found || params->deselect_all) { + /* Deselect everything. */ + uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; uv_select_tag_update_for_object(depsgraph, ts, obedit); } - - return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; + changed = true; } - return OPERATOR_CANCELLED; } - Object *obedit = hit.ob; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + if (found) { + Object *obedit = hit.ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* do selection */ - if (selectmode == UV_SELECT_ISLAND) { - if (!extend) { - uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit); - } - /* Current behavior of 'extend' - * is actually toggling, so pass extend flag as 'toggle' here */ - uv_select_linked_multi(scene, objects, objects_len, &hit, false, false, extend, false); - } - else if (extend) { - bool select = true; - if (selectmode == UV_SELECT_VERTEX) { - /* (de)select uv vertex */ - select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset); - uvedit_uv_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_EDGE) { - /* (de)select edge */ - select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset)); - uvedit_edge_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_FACE) { - /* (de)select face */ - select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset)); - uvedit_face_select_set_with_sticky(scene, em, hit.efa, select, true, cd_loop_uv_offset); - flush = -1; + if (selectmode == UV_SELECT_ISLAND) { + const bool extend = params->sel_op == SEL_OP_ADD; + const bool deselect = params->sel_op == SEL_OP_SUB; + const bool toggle = params->sel_op == SEL_OP_XOR; + /* Current behavior of 'extend' + * is actually toggling, so pass extend flag as 'toggle' here */ + uv_select_linked_multi(scene, objects, objects_len, &hit, extend, deselect, toggle, false); + /* TODO: check if this actually changed. */ + changed = true; } + else { + BLI_assert(ELEM(selectmode, UV_SELECT_VERTEX, UV_SELECT_EDGE, UV_SELECT_FACE)); + bool select_value = false; + switch (params->sel_op) { + case SEL_OP_ADD: { + select_value = true; + break; + } + case SEL_OP_SUB: { + select_value = false; + break; + } + case SEL_OP_XOR: { + select_value = !is_selected; + break; + } + case SEL_OP_SET: { + /* Deselect has already been performed. */ + select_value = true; + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; + } + } - /* de-selecting an edge may deselect a face too - validate */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (select == false) { - BM_select_history_validate(em->bm); + if (selectmode == UV_SELECT_FACE) { + uvedit_face_select_set_with_sticky( + scene, em, hit.efa, select_value, true, cd_loop_uv_offset); + flush = 1; + } + else if (selectmode == UV_SELECT_EDGE) { + uvedit_edge_select_set_with_sticky( + scene, em, hit.l, select_value, true, cd_loop_uv_offset); + flush = 1; + } + else if (selectmode == UV_SELECT_VERTEX) { + uvedit_uv_select_set_with_sticky(scene, em, hit.l, select_value, true, cd_loop_uv_offset); + flush = 1; + } + else { + BLI_assert_unreachable(); } - } - /* (de)select sticky uv nodes */ - if (sticky != SI_STICKY_DISABLE) { - flush = select ? 1 : -1; - } - } - else { - const bool select = true; - /* deselect all */ - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + /* De-selecting an edge may deselect a face too - validate. */ + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (select_value == false) { + BM_select_history_validate(em->bm); + } + } - if (selectmode == UV_SELECT_VERTEX) { - /* select vertex */ - uvedit_uv_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_EDGE) { - /* select edge */ - uvedit_edge_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_FACE) { - /* select face */ - uvedit_face_select_set_with_sticky(scene, em, hit.efa, select, true, cd_loop_uv_offset); - flush = 1; + /* (de)select sticky UV nodes. */ + if (sticky != SI_STICKY_DISABLE) { + flush = select_value ? 1 : -1; + } + + changed = true; } - } - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (flush != 0) { - EDBM_selectmode_flush(em); + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (flush != 0) { + EDBM_selectmode_flush(em); + } + } + else { + /* Setting the selection implies a single element, which doesn't need to be flushed. */ + if (params->sel_op != SEL_OP_SET) { + ED_uvedit_selectmode_flush(scene, em); + } } - } - /* #extend=false implies single vertex selection, which doesn't need to be flushed. */ - else if (extend) { - ED_uvedit_selectmode_flush(scene, em); } - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obiter = objects[ob_index]; - uv_select_tag_update_for_object(depsgraph, ts, obiter); + if (changed && found) { + /* Only update the `hit` object as de-selecting all will have refreshed the others. */ + Object *obedit = hit.ob; + uv_select_tag_update_for_object(depsgraph, ts, obedit); } - return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; + return changed || found; } -static int uv_mouse_select(bContext *C, - const float co[2], - const bool extend, - const bool deselect_all) +static bool uv_mouse_select(bContext *C, const float co[2], const struct SelectPick_Params *params) { ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, ((View3D *)NULL), &objects_len); - int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, deselect_all); + bool changed = uv_mouse_select_multi(C, objects, objects_len, co, params); MEM_freeN(objects); - return ret; + return changed; } static int uv_select_exec(bContext *C, wmOperator *op) @@ -2595,10 +2622,20 @@ static int uv_select_exec(bContext *C, wmOperator *op) float co[2]; RNA_float_get_array(op->ptr, "location", co); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + const struct SelectPick_Params params = { + .sel_op = ED_select_op_from_booleans(RNA_boolean_get(op->ptr, "extend"), + RNA_boolean_get(op->ptr, "deselect"), + RNA_boolean_get(op->ptr, "toggle")), + .deselect_all = RNA_boolean_get(op->ptr, "deselect_all"), + .select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough"), + }; - return uv_mouse_select(C, co, extend, deselect_all); + const bool changed = uv_mouse_select(C, co, ¶ms); + + if (changed) { + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + } + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -2629,18 +2666,8 @@ void UV_OT_select(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - prop = RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + WM_operator_properties_mouse_select(ot); prop = RNA_def_float_vector( ot->srna, diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 3ed99052753..63300656fda 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -2969,6 +2969,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) me, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); /* select all uv loops first - pack parameters needs this to make sure charts are registered */ ED_uvedit_select_all(bm); diff --git a/source/blender/freestyle/CMakeLists.txt b/source/blender/freestyle/CMakeLists.txt index 2a5a2d0d957..c2fad9fef3a 100644 --- a/source/blender/freestyle/CMakeLists.txt +++ b/source/blender/freestyle/CMakeLists.txt @@ -508,8 +508,6 @@ set(SRC intern/view_map/ViewMapAdvancedIterators.h intern/view_map/ViewMapBuilder.cpp intern/view_map/ViewMapBuilder.h - intern/view_map/ViewMapIO.cpp - intern/view_map/ViewMapIO.h intern/view_map/ViewMapIterators.h intern/view_map/ViewMapTesselator.cpp intern/view_map/ViewMapTesselator.h diff --git a/source/blender/freestyle/intern/application/Controller.cpp b/source/blender/freestyle/intern/application/Controller.cpp index 024bc80f3fa..cc815b5317f 100644 --- a/source/blender/freestyle/intern/application/Controller.cpp +++ b/source/blender/freestyle/intern/application/Controller.cpp @@ -36,7 +36,6 @@ extern "C" { #include "../view_map/SteerableViewMap.h" #include "../view_map/ViewMap.h" -#include "../view_map/ViewMapIO.h" #include "../view_map/ViewMapTesselator.h" #include "../winged_edge/Curvature.h" @@ -1099,13 +1098,10 @@ void Controller::init_options() Config::Path *cpath = Config::Path::getInstance(); // Directories - ViewMapIO::Options::setModelsPath(cpath->getModelsPath()); TextureManager::Options::setPatternsPath(cpath->getPatternsPath()); TextureManager::Options::setBrushesPath(cpath->getModelsPath()); // ViewMap Format - ViewMapIO::Options::rmFlags(ViewMapIO::Options::FLOAT_VECTORS); - ViewMapIO::Options::rmFlags(ViewMapIO::Options::NO_OCCLUDERS); setComputeSteerableViewMapFlag(false); // Visibility diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index 63585df97a9..96bab8c2028 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -31,6 +31,13 @@ #include "BPy_ViewMap.h" #include "BPy_ViewShape.h" +#include "BKE_appdir.h" +#include "DNA_scene_types.h" +#include "FRS_freestyle.h" +#include "RNA_access.h" +#include "RNA_prototypes.h" +#include "bpy_rna.h" /* pyrna_struct_CreatePyObject() */ + #ifdef __cplusplus extern "C" { #endif @@ -39,13 +46,6 @@ extern "C" { //------------------------ MODULE FUNCTIONS ---------------------------------- -#include "BKE_appdir.h" -#include "DNA_scene_types.h" -#include "FRS_freestyle.h" -#include "RNA_access.h" -#include "RNA_prototypes.h" -#include "bpy_rna.h" /* pyrna_struct_CreatePyObject() */ - static char Freestyle_getCurrentScene___doc__[] = ".. function:: getCurrentScene()\n" "\n" diff --git a/source/blender/freestyle/intern/python/BPy_MediumType.cpp b/source/blender/freestyle/intern/python/BPy_MediumType.cpp index 494e01967d6..cf8e900e003 100644 --- a/source/blender/freestyle/intern/python/BPy_MediumType.cpp +++ b/source/blender/freestyle/intern/python/BPy_MediumType.cpp @@ -21,7 +21,7 @@ using namespace Freestyle; PyDoc_STRVAR(MediumType_doc, "Class hierarchy: int > :class:`MediumType`\n" "\n" - "The different blending modes available to similate the interaction\n" + "The different blending modes available to simulate the interaction\n" "media-medium:\n" "\n" "* Stroke.DRY_MEDIUM: To simulate a dry medium such as Pencil or Charcoal.\n" diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index dc3b7d9795a..5642a80e77f 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -430,8 +430,8 @@ static void computeCumulativeVisibility(ViewMap *ioViewMap, int nSamples = 0; vector<WFace *> wFaces; WFace *wFace = nullptr; - unsigned cnt = 0; - unsigned cntStep = (unsigned)ceil(0.01f * vedges.size()); + unsigned count = 0; + unsigned count_step = (unsigned)ceil(0.01f * vedges.size()); unsigned tmpQI = 0; unsigned qiClasses[256]; unsigned maxIndex, maxCard; @@ -441,13 +441,13 @@ static void computeCumulativeVisibility(ViewMap *ioViewMap, if (iRenderMonitor->testBreak()) { break; } - if (cnt % cntStep == 0) { + if (count % count_step == 0) { stringstream ss; - ss << "Freestyle: Visibility computations " << (100 * cnt / vedges.size()) << "%"; + ss << "Freestyle: Visibility computations " << (100 * count / vedges.size()) << "%"; iRenderMonitor->setInfo(ss.str()); - iRenderMonitor->progress((float)cnt / vedges.size()); + iRenderMonitor->progress((float)count / vedges.size()); } - cnt++; + count++; } #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { @@ -621,9 +621,9 @@ static void computeCumulativeVisibility(ViewMap *ioViewMap, } if (iRenderMonitor && !vedges.empty()) { stringstream ss; - ss << "Freestyle: Visibility computations " << (100 * cnt / vedges.size()) << "%"; + ss << "Freestyle: Visibility computations " << (100 * count / vedges.size()) << "%"; iRenderMonitor->setInfo(ss.str()); - iRenderMonitor->progress((float)cnt / vedges.size()); + iRenderMonitor->progress((float)count / vedges.size()); } } diff --git a/source/blender/freestyle/intern/view_map/ViewMapIO.cpp b/source/blender/freestyle/intern/view_map/ViewMapIO.cpp deleted file mode 100644 index 47d9e61ba2f..00000000000 --- a/source/blender/freestyle/intern/view_map/ViewMapIO.cpp +++ /dev/null @@ -1,1294 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup freestyle - * \brief Functions to manage I/O for the view map - */ - -#include <climits> - -#include "ViewMapIO.h" - -#ifdef IRIX -# define WRITE(n) Internal::write<sizeof((n))>(out, (const char *)(&(n))) -# define READ(n) Internal::read<sizeof((n))>(in, (char *)(&(n))) -#else -# define WRITE(n) out.write((const char *)(&(n)), sizeof((n))) -# define READ(n) in.read((char *)(&(n)), sizeof((n))) -#endif - -#define WRITE_IF_NON_NULL(ptr) \ - if (ptr) { \ - WRITE((ptr)->userdata); \ - } \ - else { \ - WRITE(ZERO); \ - } \ - (void)0 - -#define READ_IF_NON_NULL(ptr, array) \ - READ(tmp); \ - if (tmp) { \ - (ptr) = (array)[tmp]; \ - } \ - else { \ - (ptr) = NULL; \ - } \ - (void)0 - -namespace Freestyle::ViewMapIO { - -namespace Internal { - -static ViewMap *g_vm; - -//////////////////// 'load' Functions //////////////////// - -inline int load(istream &in, Vec3r &v) -{ - if (Options::getFlags() & Options::FLOAT_VECTORS) { - float tmp; - READ(tmp); - v[0] = tmp; - READ(tmp); - v[1] = tmp; - READ(tmp); - v[2] = tmp; - } - else { - Vec3r::value_type tmp; - READ(tmp); - v[0] = tmp; - READ(tmp); - v[1] = tmp; - READ(tmp); - v[2] = tmp; - } - return 0; -} - -inline int load(istream &in, Polygon3r &p) -{ - unsigned tmp; - - // Id - READ(tmp); - p.setId(tmp); - - // vertices (List) - vector<Vec3r> tmp_vec; - Vec3r v; - READ(tmp); - for (unsigned int i = 0; i < tmp; i++) { - load(in, v); - tmp_vec.push_back(v); - } - p.setVertices(tmp_vec); - - // min & max - // Already computed (in the SetVertices() method) - - return 0; -} - -inline int load(istream &in, FrsMaterial &m) -{ - float tmp_array[4]; - int i; - - // Diffuse - for (i = 0; i < 4; i++) { - READ(tmp_array[i]); - } - m.setDiffuse(tmp_array[0], tmp_array[1], tmp_array[2], tmp_array[3]); - - // Specular - for (i = 0; i < 4; i++) { - READ(tmp_array[i]); - } - m.setSpecular(tmp_array[0], tmp_array[1], tmp_array[2], tmp_array[3]); - - // Ambient - for (i = 0; i < 4; i++) { - READ(tmp_array[i]); - } - m.setAmbient(tmp_array[0], tmp_array[1], tmp_array[2], tmp_array[3]); - - // Emission - for (i = 0; i < 4; i++) { - READ(tmp_array[i]); - } - m.setEmission(tmp_array[0], tmp_array[1], tmp_array[2], tmp_array[3]); - - // Shininess - READ(tmp_array[0]); - m.setShininess(tmp_array[0]); - - return 0; -} - -static int load(istream &in, ViewShape *vs) -{ - if (!vs || !vs->sshape()) { - return 1; - } - - // SShape - - // -> Id - Id::id_type id1, id2; - READ(id1); - READ(id2); - vs->sshape()->setId(Id(id1, id2)); - - // -> Importance - float importance; - READ(importance); - vs->sshape()->setImportance(importance); - - // -> BBox - // Not necessary (only used during view map computatiom) - - unsigned i, size, tmp; - - // -> Material - READ(size); - vector<FrsMaterial> frs_materials; - FrsMaterial m; - for (i = 0; i < size; ++i) { - load(in, m); - frs_materials.push_back(m); - } - vs->sshape()->setFrsMaterials(frs_materials); - - // -> VerticesList (List) - READ(size); - for (i = 0; i < size; i++) { - SVertex *sv; - READ_IF_NON_NULL(sv, g_vm->SVertices()); - vs->sshape()->AddNewVertex(sv); - } - - // -> Chains (List) - READ(size); - for (i = 0; i < size; i++) { - FEdge *fe; - READ_IF_NON_NULL(fe, g_vm->FEdges()); - vs->sshape()->AddChain(fe); - } - - // -> EdgesList (List) - READ(size); - for (i = 0; i < size; i++) { - FEdge *fe; - READ_IF_NON_NULL(fe, g_vm->FEdges()); - vs->sshape()->AddEdge(fe); - } - - // ViewEdges (List) - READ(size); - for (i = 0; i < size; i++) { - ViewEdge *ve; - READ_IF_NON_NULL(ve, g_vm->ViewEdges()); - vs->AddEdge(ve); - } - - // ViewVertices (List) - READ(size); - for (i = 0; i < size; i++) { - ViewVertex *vv; - READ_IF_NON_NULL(vv, g_vm->ViewVertices()); - vs->AddVertex(vv); - } - - return 0; -} - -static int load(istream &in, FEdge *fe) -{ - if (!fe) { - return 1; - } - - bool b; - - FEdgeSmooth *fesmooth = nullptr; - FEdgeSharp *fesharp = nullptr; - if (fe->isSmooth()) { - fesmooth = dynamic_cast<FEdgeSmooth *>(fe); - } - else { - fesharp = dynamic_cast<FEdgeSharp *>(fe); - } - - // Id - Id::id_type id1, id2; - READ(id1); - READ(id2); - fe->setId(Id(id1, id2)); - - // Nature - Nature::EdgeNature nature; - READ(nature); - fe->setNature(nature); - -#if 0 // hasVisibilityPoint - bool b; - READ(b); - fe->setHasVisibilityPoint(b); -#endif - - Vec3r v; - unsigned int matindex; - -#if 0 - // VisibilityPointA - load(in, v); - fe->setVisibilityPointA(v); - - // VisibilityPointB - load(in, v); - fe->setVisibilityPointB(v); -#endif - - if (fe->isSmooth()) { - // Normal - load(in, v); - fesmooth->setNormal(v); - - // Material - READ(matindex); - fesmooth->setFrsMaterialIndex(matindex); - } - else { - // aNormal - load(in, v); - fesharp->setNormalA(v); - - // bNormal - load(in, v); - fesharp->setNormalB(v); - - // Materials - READ(matindex); - fesharp->setaFrsMaterialIndex(matindex); - READ(matindex); - fesharp->setbFrsMaterialIndex(matindex); - } - - unsigned tmp; - - // VertexA - SVertex *sva; - READ_IF_NON_NULL(sva, g_vm->SVertices()); - fe->setVertexA(sva); - - // VertexB - SVertex *svb; - READ_IF_NON_NULL(svb, g_vm->SVertices()); - fe->setVertexB(svb); - - // NextEdge - FEdge *nfe; - READ_IF_NON_NULL(nfe, g_vm->FEdges()); - fe->setNextEdge(nfe); - - // PreviousEdge - FEdge *pfe; - READ_IF_NON_NULL(pfe, g_vm->FEdges()); - fe->setPreviousEdge(pfe); - - // ViewEdge - ViewEdge *ve; - READ_IF_NON_NULL(ve, g_vm->ViewEdges()); - fe->setViewEdge(ve); - - // Face - // Not necessary (only used during view map computatiom) - - Polygon3r p; - - // aFace - load(in, p); - fe->setaFace(p); - - // occludeeEmpty - READ(b); - fe->setOccludeeEmpty(b); - - // occludeeIntersection - load(in, v); - fe->setOccludeeIntersection(v); - - return 0; -} - -static int load(istream &in, SVertex *sv) -{ - if (!sv) { - return 1; - } - - // Id - Id::id_type id1, id2; - READ(id1); - READ(id2); - sv->setId(Id(id1, id2)); - - Vec3r v; - - // Point3D - load(in, v); - sv->setPoint3D(v); - - // Point2D - load(in, v); - sv->setPoint2D(v); - - unsigned tmp; - - // Shape - ViewShape *vs; - READ_IF_NON_NULL(vs, g_vm->ViewShapes()); - sv->setShape(vs->sshape()); - - // pViewVertex - ViewVertex *vv; - READ_IF_NON_NULL(vv, g_vm->ViewVertices()); - sv->setViewVertex(vv); - - unsigned i, size; - - // Normals (List) - READ(size); - for (i = 0; i < size; i++) { - load(in, v); - sv->AddNormal(v); - } - - // FEdges (List) - READ(size); - FEdge *fe; - for (i = 0; i < size; i++) { - READ_IF_NON_NULL(fe, g_vm->FEdges()); - sv->AddFEdge(fe); - } - - return 0; -} - -static int load(istream &in, ViewEdge *ve) -{ - if (!ve) { - return 1; - } - - unsigned tmp; - - // Id - Id::id_type id1, id2; - READ(id1); - READ(id2); - ve->setId(Id(id1, id2)); - - // Nature - Nature::EdgeNature nature; - READ(nature); - ve->setNature(nature); - - // QI - READ(tmp); - ve->setQI(tmp); - - // Shape - ViewShape *vs; - READ_IF_NON_NULL(vs, g_vm->ViewShapes()); - ve->setShape(vs); - - // aShape - ViewShape *avs; - READ_IF_NON_NULL(avs, g_vm->ViewShapes()); - ve->setaShape(avs); - - // FEdgeA - FEdge *fea; - READ_IF_NON_NULL(fea, g_vm->FEdges()); - ve->setFEdgeA(fea); - - // FEdgeB - FEdge *feb; - READ_IF_NON_NULL(feb, g_vm->FEdges()); - ve->setFEdgeB(feb); - - // A - ViewVertex *vva; - READ_IF_NON_NULL(vva, g_vm->ViewVertices()); - ve->setA(vva); - - // B - ViewVertex *vvb; - READ_IF_NON_NULL(vvb, g_vm->ViewVertices()); - ve->setB(vvb); - - // Occluders (List) - if (!(Options::getFlags() & Options::NO_OCCLUDERS)) { - unsigned size; - READ(size); - ViewShape *vso; - for (unsigned int i = 0; i < size; i++) { - READ_IF_NON_NULL(vso, g_vm->ViewShapes()); - ve->AddOccluder(vso); - } - } - - return 0; -} - -static int load(istream &in, ViewVertex *vv) -{ - if (!vv) { - return 1; - } - - unsigned tmp; - bool b; - - // Nature - Nature::VertexNature nature; - READ(nature); - vv->setNature(nature); - - if (vv->getNature() & Nature::T_VERTEX) { - TVertex *tv = dynamic_cast<TVertex *>(vv); - - // Id - Id::id_type id1, id2; - READ(id1); - READ(id2); - tv->setId(Id(id1, id2)); - - // FrontSVertex - SVertex *fsv; - READ_IF_NON_NULL(fsv, g_vm->SVertices()); - tv->setFrontSVertex(fsv); - - // BackSVertex - SVertex *bsv; - READ_IF_NON_NULL(bsv, g_vm->SVertices()); - tv->setBackSVertex(bsv); - - // FrontEdgeA - ViewEdge *fea; - READ_IF_NON_NULL(fea, g_vm->ViewEdges()); - READ(b); - tv->setFrontEdgeA(fea, b); - - // FrontEdgeB - ViewEdge *feb; - READ_IF_NON_NULL(feb, g_vm->ViewEdges()); - READ(b); - tv->setFrontEdgeB(feb, b); - - // BackEdgeA - ViewEdge *bea; - READ_IF_NON_NULL(bea, g_vm->ViewEdges()); - READ(b); - tv->setBackEdgeA(bea, b); - - // BackEdgeB - ViewEdge *beb; - READ_IF_NON_NULL(beb, g_vm->ViewEdges()); - READ(b); - tv->setBackEdgeB(beb, b); - } - else if (vv->getNature() & Nature::NON_T_VERTEX) { - NonTVertex *ntv = dynamic_cast<NonTVertex *>(vv); - - // SVertex - SVertex *sv; - READ_IF_NON_NULL(sv, g_vm->SVertices()); - ntv->setSVertex(sv); - - // ViewEdges (List) - unsigned size; - READ(size); - ViewEdge *ve; - for (unsigned int i = 0; i < size; i++) { - READ_IF_NON_NULL(ve, g_vm->ViewEdges()); - READ(b); - ntv->AddViewEdge(ve, b); - } - } - - return 0; -} - -//////////////////// 'save' Functions //////////////////// - -inline int save(ostream &out, const Vec3r &v) -{ - if (Options::getFlags() & Options::FLOAT_VECTORS) { - float tmp; - - tmp = v[0]; - WRITE(tmp); - tmp = v[1]; - WRITE(tmp); - tmp = v[2]; - WRITE(tmp); - } - else { - Vec3r::value_type tmp; - - tmp = v[0]; - WRITE(tmp); - tmp = v[1]; - WRITE(tmp); - tmp = v[2]; - WRITE(tmp); - } - return 0; -} - -inline int save(ostream &out, const Polygon3r &p) -{ - unsigned tmp; - - // Id - tmp = p.getId(); - WRITE(tmp); - - // vertices (List) - tmp = p.getVertices().size(); - WRITE(tmp); - for (vector<Vec3r>::const_iterator i = p.getVertices().begin(); i != p.getVertices().end(); - i++) { - save(out, *i); - } - - // min & max - // Do not need to be saved - - return 0; -} - -inline int save(ostream &out, const FrsMaterial &m) -{ - unsigned i; - - // Diffuse - for (i = 0; i < 4; i++) { - WRITE(m.diffuse()[i]); - } - - // Specular - for (i = 0; i < 4; i++) { - WRITE(m.specular()[i]); - } - - // Ambient - for (i = 0; i < 4; i++) { - WRITE(m.ambient()[i]); - } - - // Emission - for (i = 0; i < 4; i++) { - WRITE(m.emission()[i]); - } - - // Shininess - float shininess = m.shininess(); - WRITE(shininess); - - return 0; -} - -static int save(ostream &out, ViewShape *vs) -{ - if (!vs || !vs->sshape()) { - cerr << "Warning: null ViewShape" << endl; - return 1; - } - - unsigned tmp; - - // SShape - - // -> Id - Id::id_type id = vs->sshape()->getId().getFirst(); - WRITE(id); - id = vs->sshape()->getId().getSecond(); - WRITE(id); - - // -> Importance - float importance = vs->sshape()->importance(); - WRITE(importance); - - // -> BBox - // Not necessary (only used during view map computatiom) - - // -> Material - unsigned int size = vs->sshape()->frs_materials().size(); - WRITE(size); - for (unsigned int i = 0; i < size; ++i) { - save(out, vs->sshape()->frs_material(i)); - } - - // -> VerticesList (List) - tmp = vs->sshape()->getVertexList().size(); - WRITE(tmp); - for (vector<SVertex *>::const_iterator i1 = vs->sshape()->getVertexList().begin(); - i1 != vs->sshape()->getVertexList().end(); - i1++) { - WRITE_IF_NON_NULL(*i1); - } - - // -> Chains (List) - tmp = vs->sshape()->getChains().size(); - WRITE(tmp); - for (vector<FEdge *>::const_iterator i2 = vs->sshape()->getChains().begin(); - i2 != vs->sshape()->getChains().end(); - i2++) { - WRITE_IF_NON_NULL(*i2); - } - - // -> EdgesList (List) - tmp = vs->sshape()->getEdgeList().size(); - WRITE(tmp); - for (vector<FEdge *>::const_iterator i3 = vs->sshape()->getEdgeList().begin(); - i3 != vs->sshape()->getEdgeList().end(); - i3++) { - WRITE_IF_NON_NULL(*i3); - } - - // ViewEdges (List) - tmp = vs->edges().size(); - WRITE(tmp); - for (vector<ViewEdge *>::const_iterator i4 = vs->edges().begin(); i4 != vs->edges().end(); - i4++) { - WRITE_IF_NON_NULL(*i4); - } - - // ViewVertices (List) - tmp = vs->vertices().size(); - WRITE(tmp); - for (vector<ViewVertex *>::const_iterator i5 = vs->vertices().begin(); - i5 != vs->vertices().end(); - i5++) { - WRITE_IF_NON_NULL(*i5); - } - - return 0; -} - -static int save(ostream &out, FEdge *fe) -{ - if (!fe) { - cerr << "Warning: null FEdge" << endl; - return 1; - } - - FEdgeSmooth *fesmooth = dynamic_cast<FEdgeSmooth *>(fe); - FEdgeSharp *fesharp = dynamic_cast<FEdgeSharp *>(fe); - - // Id - Id::id_type id = fe->getId().getFirst(); - WRITE(id); - id = fe->getId().getSecond(); - WRITE(id); - - // Nature - Nature::EdgeNature nature = fe->getNature(); - WRITE(nature); - - bool b; - -#if 0 - // hasVisibilityPoint - b = fe->hasVisibilityPoint(); - WRITE(b); - - // VisibilityPointA - save(out, fe->visibilityPointA()); - - // VisibilityPointB - save(out, fe->visibilityPointB()); -#endif - - unsigned index; - if (fe->isSmooth()) { - // normal - save(out, fesmooth->normal()); - // material - index = fesmooth->frs_materialIndex(); - WRITE(index); - } - else { - // aNormal - save(out, fesharp->normalA()); - // bNormal - save(out, fesharp->normalB()); - // aMaterial - index = fesharp->aFrsMaterialIndex(); - WRITE(index); - // bMaterial - index = fesharp->bFrsMaterialIndex(); - WRITE(index); - } - - // VertexA - WRITE_IF_NON_NULL(fe->vertexA()); - - // VertexB - WRITE_IF_NON_NULL(fe->vertexB()); - - // NextEdge - WRITE_IF_NON_NULL(fe->nextEdge()); - - // PreviousEdge - WRITE_IF_NON_NULL(fe->previousEdge()); - - // ViewEdge - WRITE_IF_NON_NULL(fe->viewedge()); - - // Face - // Not necessary (only used during view map computatiom) - - // aFace - save(out, (Polygon3r &)fe->aFace()); - - // occludeeEmpty - b = fe->getOccludeeEmpty(); - WRITE(b); - - // occludeeIntersection - save(out, fe->getOccludeeIntersection()); - - return 0; -} - -static int save(ostream &out, SVertex *sv) -{ - if (!sv) { - cerr << "Warning: null SVertex" << endl; - return 1; - } - - unsigned tmp; - - // Id - Id::id_type id = sv->getId().getFirst(); - WRITE(id); - id = sv->getId().getSecond(); - WRITE(id); - - Vec3r v; - - // Point3D - v = sv->point3D(); - save(out, sv->point3D()); - - // Point2D - v = sv->point2D(); - save(out, v); - - // Shape - WRITE_IF_NON_NULL(sv->shape()); - - // pViewVertex - WRITE_IF_NON_NULL(sv->viewvertex()); - - // Normals (List) - // NOTE: the 'size()' method of a set doesn't seem to return the actual size of the given set, so - // we have to hack it... - set<Vec3r>::const_iterator i; - for (i = sv->normals().begin(), tmp = 0; i != sv->normals().end(); i++, tmp++) { - /* pass */ - } - WRITE(tmp); - for (i = sv->normals().begin(); i != sv->normals().end(); i++) { - save(out, *i); - } - - // FEdges (List) - tmp = sv->fedges().size(); - WRITE(tmp); - for (vector<FEdge *>::const_iterator j = sv->fedges_begin(); j != sv->fedges_end(); j++) { - WRITE_IF_NON_NULL(*j); - } - - return 0; -} - -static int save(ostream &out, ViewEdge *ve) -{ - if (!ve) { - cerr << "Warning: null ViewEdge" << endl; - return 1; - } - - unsigned tmp; - - // Id - Id::id_type id = ve->getId().getFirst(); - WRITE(id); - id = ve->getId().getSecond(); - WRITE(id); - - // Nature - Nature::EdgeNature nature = ve->getNature(); - WRITE(nature); - - // QI - unsigned qi = ve->qi(); - WRITE(qi); - - // Shape - WRITE_IF_NON_NULL(ve->shape()); - - // aShape - WRITE_IF_NON_NULL(ve->aShape()); - - // FEdgeA - WRITE_IF_NON_NULL(ve->fedgeA()); - - // FEdgeB - WRITE_IF_NON_NULL(ve->fedgeB()); - - // A - WRITE_IF_NON_NULL(ve->A()); - - // B - WRITE_IF_NON_NULL(ve->B()); - - // Occluders (List) - if (!(Options::getFlags() & Options::NO_OCCLUDERS)) { - tmp = ve->occluders().size(); - WRITE(tmp); - for (vector<ViewShape *>::const_iterator i = ve->occluders().begin(); - i != ve->occluders().end(); - i++) { - WRITE_IF_NON_NULL((*i)); - } - } - - return 0; -} - -static int save(ostream &out, ViewVertex *vv) -{ - if (!vv) { - cerr << "Warning: null ViewVertex" << endl; - return 1; - } - - // Nature - Nature::VertexNature nature = vv->getNature(); - WRITE(nature); - - if (vv->getNature() & Nature::T_VERTEX) { - TVertex *tv = dynamic_cast<TVertex *>(vv); - - // Id - Id::id_type id = tv->getId().getFirst(); - WRITE(id); - id = tv->getId().getSecond(); - WRITE(id); - - // FrontSVertex - WRITE_IF_NON_NULL(tv->frontSVertex()); - - // BackSVertex - WRITE_IF_NON_NULL(tv->backSVertex()); - - // FrontEdgeA - WRITE_IF_NON_NULL(tv->frontEdgeA().first); - WRITE(tv->frontEdgeA().second); - - // FrontEdgeB - WRITE_IF_NON_NULL(tv->frontEdgeB().first); - WRITE(tv->frontEdgeB().second); - - // BackEdgeA - WRITE_IF_NON_NULL(tv->backEdgeA().first); - WRITE(tv->backEdgeA().second); - - // BackEdgeB - WRITE_IF_NON_NULL(tv->backEdgeB().first); - WRITE(tv->backEdgeB().second); - } - else if (vv->getNature() & Nature::NON_T_VERTEX) { - NonTVertex *ntv = dynamic_cast<NonTVertex *>(vv); - - // SVertex - WRITE_IF_NON_NULL(ntv->svertex()); - - // ViewEdges (List) - unsigned size = ntv->viewedges().size(); - WRITE(size); - vector<ViewVertex::directedViewEdge>::const_iterator i = ntv->viewedges().begin(); - for (; i != ntv->viewedges().end(); i++) { - WRITE_IF_NON_NULL(i->first); - WRITE(i->second); - } - } - else { - cerr << "Warning: unexpected ViewVertex nature" << endl; - return 1; - } - - return 0; -} - -} // End of namespace Internal - -//////////////////// "Public" 'load' and 'save' functions //////////////////// - -#define SET_PROGRESS(n) \ - if (pb) { \ - pb->setProgress((n)); \ - } \ - (void)0 - -int load(istream &in, ViewMap *vm, ProgressBar *pb) -{ - if (!vm) { - return 1; - } - - // soc unused - unsigned tmp; - int err = 0; - Internal::g_vm = vm; - - // Management of the progress bar (if present) - if (pb) { - pb->reset(); - pb->setLabelText("Loading View Map..."); - pb->setTotalSteps(6); - pb->setProgress(0); - } - - // Read and set the options - unsigned char flags; - READ(flags); - Options::setFlags(flags); - - // Read the size of the five ViewMap's lists (with some extra information for the ViewVertices) - // and instantiate them (with default costructors) - unsigned vs_s, fe_s, fe_rle1, fe_rle2, sv_s, ve_s, vv_s, vv_rle1, vv_rle2; - READ(vs_s); - READ(fe_s); - - if (fe_s) { - bool b; - READ(b); - /* NOLINTNEXTLINE: bugprone-infinite-loop */ - for (READ(fe_rle1), fe_rle2 = 0; fe_rle1 <= fe_s; fe_rle2 = fe_rle1, READ(fe_rle1)) { - if (b) { - for (unsigned int i = fe_rle2; i < fe_rle1; i++) { - FEdgeSmooth *fes = new FEdgeSmooth; - vm->AddFEdge(fes); - } - b = !b; - } - else if (!b) { - for (unsigned int i = fe_rle2; i < fe_rle1; i++) { - FEdgeSharp *fes = new FEdgeSharp; - vm->AddFEdge(fes); - } - b = !b; - } - } - } - - READ(sv_s); - READ(ve_s); - READ(vv_s); - - if (vv_s) { - Nature::VertexNature nature; - READ(nature); - /* NOLINTNEXTLINE: bugprone-infinite-loop */ - for (READ(vv_rle1), vv_rle2 = 0; vv_rle1 <= vv_s; vv_rle2 = vv_rle1, READ(vv_rle1)) { - if (nature & Nature::T_VERTEX) { - for (unsigned int i = vv_rle2; i < vv_rle1; i++) { - TVertex *tv = new TVertex(); - vm->AddViewVertex(tv); - } - nature = Nature::NON_T_VERTEX; - } - else if (nature & Nature::NON_T_VERTEX) { - for (unsigned int i = vv_rle2; i < vv_rle1; i++) { - NonTVertex *ntv = new NonTVertex(); - vm->AddViewVertex(ntv); - } - nature = Nature::T_VERTEX; - } - } - } - - for (unsigned int i0 = 0; i0 < vs_s; i0++) { - SShape *ss = new SShape(); - ViewShape *vs = new ViewShape(); - vs->setSShape(ss); - ss->setViewShape(vs); - vm->AddViewShape(vs); - } -#if 0 - for (unsigned int i1 = 0; i1 < fe_s; i1++) { - FEdge *fe = new FEdge(); - vm->AddFEdge(fe); - } -#endif - for (unsigned int i2 = 0; i2 < sv_s; i2++) { - SVertex *sv = new SVertex(); - vm->AddSVertex(sv); - } - for (unsigned int i3 = 0; i3 < ve_s; i3++) { - ViewEdge *ve = new ViewEdge(); - vm->AddViewEdge(ve); - } - - // Read the values for all the objects created above - SET_PROGRESS(1); - for (vector<ViewShape *>::const_iterator i4 = vm->ViewShapes().begin(); - i4 != vm->ViewShapes().end(); - i4++) { - err += Internal::load(in, *i4); - } - SET_PROGRESS(2); - for (vector<FEdge *>::const_iterator i5 = vm->FEdges().begin(); i5 != vm->FEdges().end(); i5++) { - err += Internal::load(in, *i5); - } - SET_PROGRESS(3); - for (vector<SVertex *>::const_iterator i6 = vm->SVertices().begin(); i6 != vm->SVertices().end(); - i6++) { - err += Internal::load(in, *i6); - } - SET_PROGRESS(4); - for (vector<ViewEdge *>::const_iterator i7 = vm->ViewEdges().begin(); - i7 != vm->ViewEdges().end(); - i7++) { - err += Internal::load(in, *i7); - } - SET_PROGRESS(5); - for (vector<ViewVertex *>::const_iterator i8 = vm->ViewVertices().begin(); - i8 != vm->ViewVertices().end(); - i8++) { - err += Internal::load(in, *i8); - } - SET_PROGRESS(6); - - // Read the shape id to index mapping - unsigned map_s; - READ(map_s); - unsigned id, index; - for (unsigned int i4 = 0; i4 < map_s; ++i4) { - READ(id); - READ(index); - vm->shapeIdToIndexMap()[id] = index; - } - - return err; -} - -int save(ostream &out, ViewMap *vm, ProgressBar *pb) -{ - if (!vm) { - return 1; - } - - int err = 0; - - // Management of the progress bar (if present) - if (pb) { - pb->reset(); - pb->setLabelText("Saving View Map..."); - pb->setTotalSteps(6); - pb->setProgress(0); - } - - // For every object, initialize its userdata member to its index in the ViewMap list - for (unsigned int i0 = 0; i0 < vm->ViewShapes().size(); i0++) { - vm->ViewShapes()[i0]->userdata = POINTER_FROM_UINT(i0); - vm->ViewShapes()[i0]->sshape()->userdata = POINTER_FROM_UINT(i0); - } - for (unsigned int i1 = 0; i1 < vm->FEdges().size(); i1++) { - vm->FEdges()[i1]->userdata = POINTER_FROM_UINT(i1); - } - for (unsigned int i2 = 0; i2 < vm->SVertices().size(); i2++) { - vm->SVertices()[i2]->userdata = POINTER_FROM_UINT(i2); - } - for (unsigned int i3 = 0; i3 < vm->ViewEdges().size(); i3++) { - vm->ViewEdges()[i3]->userdata = POINTER_FROM_UINT(i3); - } - for (unsigned int i4 = 0; i4 < vm->ViewVertices().size(); i4++) { - vm->ViewVertices()[i4]->userdata = POINTER_FROM_UINT(i4); - } - - // Write the current options - unsigned char flags = Options::getFlags(); - WRITE(flags); - - // Write the size of the five lists (with some extra information for the ViewVertices) - unsigned size; - size = vm->ViewShapes().size(); - WRITE(size); - size = vm->FEdges().size(); - WRITE(size); - if (size) { - bool b = vm->FEdges()[0]->isSmooth(); - WRITE(b); - for (unsigned int i = 0; i < size; i++) { - while (i < size && (vm->FEdges()[i]->isSmooth() == b)) { - i++; - } - if (i < size) { - WRITE(i); - b = !b; - } - } - WRITE(size); - size++; - WRITE(size); - } - size = vm->SVertices().size(); - WRITE(size); - size = vm->ViewEdges().size(); - WRITE(size); - size = vm->ViewVertices().size(); - WRITE(size); - if (size) { - Nature::VertexNature nature = vm->ViewVertices()[0]->getNature(); - WRITE(nature); - nature &= ~Nature::VIEW_VERTEX; - for (unsigned int i = 0; i < size; i++) { - while (i < size && (vm->ViewVertices()[i]->getNature() & nature)) { - i++; - } - if (i < size) { - WRITE(i); - nature = vm->ViewVertices()[i]->getNature() & ~Nature::VIEW_VERTEX; - } - } - WRITE(size); - size++; - WRITE(size); - } - - // Write all the elts of the ViewShapes List - SET_PROGRESS(1); - for (vector<ViewShape *>::const_iterator i5 = vm->ViewShapes().begin(); - i5 != vm->ViewShapes().end(); - i5++) { - err += Internal::save(out, *i5); - } - SET_PROGRESS(2); - for (vector<FEdge *>::const_iterator i6 = vm->FEdges().begin(); i6 != vm->FEdges().end(); i6++) { - err += Internal::save(out, *i6); - } - SET_PROGRESS(3); - for (vector<SVertex *>::const_iterator i7 = vm->SVertices().begin(); i7 != vm->SVertices().end(); - i7++) { - err += Internal::save(out, *i7); - } - SET_PROGRESS(4); - for (vector<ViewEdge *>::const_iterator i8 = vm->ViewEdges().begin(); - i8 != vm->ViewEdges().end(); - i8++) { - err += Internal::save(out, *i8); - } - SET_PROGRESS(5); - for (vector<ViewVertex *>::const_iterator i9 = vm->ViewVertices().begin(); - i9 != vm->ViewVertices().end(); - i9++) { - err += Internal::save(out, *i9); - } - - // Write the shape id to index mapping - size = vm->shapeIdToIndexMap().size(); - WRITE(size); - unsigned int id, index; - for (ViewMap::id_to_index_map::iterator mit = vm->shapeIdToIndexMap().begin(), - mitend = vm->shapeIdToIndexMap().end(); - mit != mitend; - ++mit) { - id = mit->first; - index = mit->second; - WRITE(id); - WRITE(index); - } - - // Reset 'userdata' members - for (vector<ViewShape *>::const_iterator j0 = vm->ViewShapes().begin(); - j0 != vm->ViewShapes().end(); - j0++) { - (*j0)->userdata = nullptr; - (*j0)->sshape()->userdata = nullptr; - } - for (vector<FEdge *>::const_iterator j1 = vm->FEdges().begin(); j1 != vm->FEdges().end(); j1++) { - (*j1)->userdata = nullptr; - } - for (vector<SVertex *>::const_iterator j2 = vm->SVertices().begin(); j2 != vm->SVertices().end(); - j2++) { - (*j2)->userdata = nullptr; - } - for (vector<ViewEdge *>::const_iterator j3 = vm->ViewEdges().begin(); - j3 != vm->ViewEdges().end(); - j3++) { - (*j3)->userdata = nullptr; - } - for (vector<ViewVertex *>::const_iterator j4 = vm->ViewVertices().begin(); - j4 != vm->ViewVertices().end(); - j4++) { - (*j4)->userdata = nullptr; - } - SET_PROGRESS(6); - - return err; -} - -//////////////////// Options //////////////////// - -namespace Options { - -namespace Internal { - -static unsigned char g_flags = 0; -static string g_models_path; - -} // End of namespace Internal - -void setFlags(const unsigned char flags) -{ - Internal::g_flags = flags; -} - -void addFlags(const unsigned char flags) -{ - Internal::g_flags |= flags; -} - -void rmFlags(const unsigned char flags) -{ - Internal::g_flags &= ~flags; -} - -unsigned char getFlags() -{ - return Internal::g_flags; -} - -void setModelsPath(const string &path) -{ - Internal::g_models_path = path; -} - -string getModelsPath() -{ - return Internal::g_models_path; -} - -} // namespace Options - -} // namespace Freestyle::ViewMapIO diff --git a/source/blender/freestyle/intern/view_map/ViewMapIO.h b/source/blender/freestyle/intern/view_map/ViewMapIO.h deleted file mode 100644 index 4796b28a38a..00000000000 --- a/source/blender/freestyle/intern/view_map/ViewMapIO.h +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** \file - * \ingroup freestyle - * \brief Functions to manage I/O for the view map - */ - -#include <fstream> -#include <string> - -#include "ViewMap.h" - -#include "../system/FreestyleConfig.h" -#include "../system/ProgressBar.h" - -namespace Freestyle { - -namespace ViewMapIO { - -static const unsigned ZERO = UINT_MAX; - -int load(istream &in, ViewMap *vm, ProgressBar *pb = NULL); - -int save(ostream &out, ViewMap *vm, ProgressBar *pb = NULL); - -namespace Options { - -static const unsigned char FLOAT_VECTORS = 1; -static const unsigned char NO_OCCLUDERS = 2; - -void setFlags(unsigned char flags); - -void addFlags(unsigned char flags); - -void rmFlags(unsigned char flags); - -unsigned char getFlags(); - -void setModelsPath(const string &path); - -string getModelsPath(); - -}; // namespace Options - -#ifdef IRIX - -namespace Internal { - -template<unsigned S> ostream &write(ostream &out, const char *str) -{ - out.put(str[S - 1]); - return write<S - 1>(out, str); -} - -template<> ostream &write<1>(ostream &out, const char *str) -{ - return out.put(str[0]); -} - -template<> ostream &write<0>(ostream &out, const char *) -{ - return out; -} - -template<unsigned S> istream &read(istream &in, char *str) -{ - in.get(str[S - 1]); - return read<S - 1>(in, str); -} - -template<> istream &read<1>(istream &in, char *str) -{ - return in.get(str[0]); -} - -template<> istream &read<0>(istream &in, char *) -{ - return in; -} - -} // End of namespace Internal - -#endif // IRIX - -} // End of namespace ViewMapIO - -} /* namespace Freestyle */ diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.h b/source/blender/freestyle/intern/winged_edge/WEdge.h index 6d39446056f..00f4c537d4a 100644 --- a/source/blender/freestyle/intern/winged_edge/WEdge.h +++ b/source/blender/freestyle/intern/winged_edge/WEdge.h @@ -143,12 +143,14 @@ class WVertex { public: /** Iterator to iterate over a vertex incoming edges in the CCW order. */ -#if defined(__GNUC__) && (__GNUC__ < 3) - class incoming_edge_iterator : public input_iterator<WOEdge *, ptrdiff_t> -#else - class incoming_edge_iterator : public iterator<input_iterator_tag, WOEdge *, ptrdiff_t> -#endif - { + class incoming_edge_iterator { + public: + using iterator_category = input_iterator_tag; + using value_type = WOEdge *; + using difference_type = ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + private: WVertex *_vertex; // @@ -156,25 +158,12 @@ class WVertex { WOEdge *_current; public: -#if defined(__GNUC__) && (__GNUC__ < 3) - inline incoming_edge_iterator() : input_iterator<WOEdge *, ptrdiff_t>() - { - } -#else - inline incoming_edge_iterator() : iterator<input_iterator_tag, WOEdge *, ptrdiff_t>() - { - } -#endif - virtual ~incoming_edge_iterator(){}; // soc + inline incoming_edge_iterator() = default; + virtual ~incoming_edge_iterator() = default; protected: friend class WVertex; inline incoming_edge_iterator(WVertex *iVertex, WOEdge *iBegin, WOEdge *iCurrent) -#if defined(__GNUC__) && (__GNUC__ < 3) - : input_iterator<WOEdge *, ptrdiff_t>() -#else - : iterator<input_iterator_tag, WOEdge *, ptrdiff_t>() -#endif { _vertex = iVertex; _begin = iBegin; @@ -183,11 +172,6 @@ class WVertex { public: inline incoming_edge_iterator(const incoming_edge_iterator &iBrother) -#if defined(__GNUC__) && (__GNUC__ < 3) - : input_iterator<WOEdge *, ptrdiff_t>(iBrother) -#else - : iterator<input_iterator_tag, WOEdge *, ptrdiff_t>(iBrother) -#endif { _vertex = iBrother._vertex; _begin = iBrother._begin; @@ -233,47 +217,30 @@ class WVertex { #endif }; - /** Iterator to iterate over a vertex faces in the CCW order */ -#if defined(__GNUC__) && (__GNUC__ < 3) - class face_iterator : public input_iterator<WFace *, ptrdiff_t> -#else - class face_iterator : public iterator<input_iterator_tag, WFace *, ptrdiff_t> -#endif - { + class face_iterator { + public: + using iterator_category = input_iterator_tag; + using value_type = WFace *; + using difference_type = ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + private: incoming_edge_iterator _edge_it; public: -#if defined(__GNUC__) && (__GNUC__ < 3) - inline face_iterator() : input_iterator<WFace *, ptrdiff_t>() - { - } -#else - inline face_iterator() : iterator<input_iterator_tag, WFace *, ptrdiff_t>() - { - } -#endif - virtual ~face_iterator(){}; // soc + inline face_iterator() = default; + virtual ~face_iterator() = default; protected: friend class WVertex; inline face_iterator(incoming_edge_iterator it) -#if defined(__GNUC__) && (__GNUC__ < 3) - : input_iterator<WFace *, ptrdiff_t>() -#else - : iterator<input_iterator_tag, WFace *, ptrdiff_t>() -#endif { _edge_it = it; } public: inline face_iterator(const face_iterator &iBrother) -#if defined(__GNUC__) && (__GNUC__ < 3) - : input_iterator<WFace *, ptrdiff_t>(iBrother) -#else - : iterator<input_iterator_tag, WFace *, ptrdiff_t>(iBrother) -#endif { _edge_it = iBrother._edge_it; } diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 55432003010..f1298a7f5b7 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -13,9 +13,6 @@ set(INC_SYS set(SRC intern/cpp_types.cc intern/field.cc - intern/generic_vector_array.cc - intern/generic_virtual_array.cc - intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc intern/multi_function_params.cc @@ -24,17 +21,8 @@ set(SRC intern/multi_function_procedure_executor.cc intern/multi_function_procedure_optimization.cc - FN_cpp_type.hh - FN_cpp_type_make.hh FN_field.hh FN_field_cpp_type.hh - FN_generic_array.hh - FN_generic_pointer.hh - FN_generic_span.hh - FN_generic_value_map.hh - FN_generic_vector_array.hh - FN_generic_virtual_array.hh - FN_generic_virtual_vector_array.hh FN_multi_function.hh FN_multi_function_builder.hh FN_multi_function_context.hh @@ -72,11 +60,7 @@ blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC - tests/FN_cpp_type_test.cc tests/FN_field_test.cc - tests/FN_generic_array_test.cc - tests/FN_generic_span_test.cc - tests/FN_generic_vector_array_test.cc tests/FN_multi_function_procedure_test.cc tests/FN_multi_function_test.cc diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 2cba833ce64..5a27cda0787 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -33,11 +33,11 @@ */ #include "BLI_function_ref.hh" +#include "BLI_generic_virtual_array.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" #include "BLI_vector_set.hh" -#include "FN_generic_virtual_array.hh" #include "FN_multi_function_builder.hh" namespace blender::fn { @@ -119,7 +119,7 @@ template<typename NodePtr> class GFieldBase { return get_default_hash_2(*node_, node_output_index_); } - const fn::CPPType &cpp_type() const + const CPPType &cpp_type() const { return node_->output_cpp_type(node_output_index_); } @@ -476,13 +476,13 @@ template<typename T> T evaluate_constant_field(const Field<T> &field) return value; } +GField make_constant_field(const CPPType &type, const void *value); + template<typename T> Field<T> make_constant_field(T value) { return make_constant_field(CPPType::get<T>(), &value); } -GField make_constant_field(const CPPType &type, const void *value); - /** * If the field depends on some input, the same field is returned. * Otherwise the field is evaluated and a new field is created that just computes this constant. diff --git a/source/blender/functions/FN_field_cpp_type.hh b/source/blender/functions/FN_field_cpp_type.hh index 24477246582..63a648f3202 100644 --- a/source/blender/functions/FN_field_cpp_type.hh +++ b/source/blender/functions/FN_field_cpp_type.hh @@ -6,7 +6,7 @@ * \ingroup fn */ -#include "FN_cpp_type_make.hh" +#include "BLI_cpp_type_make.hh" #include "FN_field.hh" namespace blender::fn { @@ -127,16 +127,14 @@ class ValueOrFieldCPPType : public CPPType { } // namespace blender::fn #define MAKE_FIELD_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \ - template<> \ - const blender::fn::CPPType &blender::fn::CPPType::get_impl<blender::fn::Field<FIELD_TYPE>>() \ + template<> const blender::CPPType &blender::CPPType::get_impl<blender::fn::Field<FIELD_TYPE>>() \ { \ static blender::fn::FieldCPPType cpp_type{ \ blender::fn::FieldCPPTypeParam<blender::fn::Field<FIELD_TYPE>>(), STRINGIFY(DEBUG_NAME)}; \ return cpp_type; \ } \ template<> \ - const blender::fn::CPPType & \ - blender::fn::CPPType::get_impl<blender::fn::ValueOrField<FIELD_TYPE>>() \ + const blender::CPPType &blender::CPPType::get_impl<blender::fn::ValueOrField<FIELD_TYPE>>() \ { \ static blender::fn::ValueOrFieldCPPType cpp_type{ \ blender::fn::FieldCPPTypeParam<blender::fn::ValueOrField<FIELD_TYPE>>(), \ diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh index cca6955aeb0..015df179ef0 100644 --- a/source/blender/functions/FN_multi_function.hh +++ b/source/blender/functions/FN_multi_function.hh @@ -154,9 +154,6 @@ inline MFParamsBuilder::MFParamsBuilder(const MultiFunction &fn, const IndexMask } namespace multi_function_types { -using fn::CPPType; -using fn::GMutableSpan; -using fn::GSpan; using fn::MFContext; using fn::MFContextBuilder; using fn::MFDataType; diff --git a/source/blender/functions/FN_multi_function_data_type.hh b/source/blender/functions/FN_multi_function_data_type.hh index a0ded253d26..d63e0faaca5 100644 --- a/source/blender/functions/FN_multi_function_data_type.hh +++ b/source/blender/functions/FN_multi_function_data_type.hh @@ -10,7 +10,7 @@ * is possible when necessary. */ -#include "FN_cpp_type.hh" +#include "BLI_cpp_type.hh" namespace blender::fn { diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 38736527771..67f31a61dc4 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -13,11 +13,11 @@ #include <mutex> +#include "BLI_generic_pointer.hh" +#include "BLI_generic_vector_array.hh" +#include "BLI_generic_virtual_vector_array.hh" #include "BLI_resource_scope.hh" -#include "FN_generic_pointer.hh" -#include "FN_generic_vector_array.hh" -#include "FN_generic_virtual_vector_array.hh" #include "FN_multi_function_signature.hh" namespace blender::fn { diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 5606f660237..a2adc2ea8b6 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -1,38 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "FN_cpp_type_make.hh" -#include "FN_field_cpp_type.hh" - #include "BLI_color.hh" +#include "BLI_cpp_type_make.hh" #include "BLI_float4x4.hh" #include "BLI_math_vec_types.hh" -namespace blender::fn { - -MAKE_CPP_TYPE(bool, bool, CPPTypeFlags::BasicType) - -MAKE_CPP_TYPE(float, float, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(float2, blender::float2, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(float3, blender::float3, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(float4x4, blender::float4x4, CPPTypeFlags::BasicType) - -MAKE_CPP_TYPE(int32, int32_t, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(int8, int8_t, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(uint32, uint32_t, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(uint8, uint8_t, CPPTypeFlags::BasicType) - -MAKE_CPP_TYPE(ColorGeometry4f, blender::ColorGeometry4f, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b, CPPTypeFlags::BasicType) - -MAKE_CPP_TYPE(string, std::string, CPPTypeFlags::BasicType) +#include "FN_field_cpp_type.hh" MAKE_FIELD_CPP_TYPE(FloatField, float); -MAKE_FIELD_CPP_TYPE(Float2Field, float2); -MAKE_FIELD_CPP_TYPE(Float3Field, float3); +MAKE_FIELD_CPP_TYPE(Float2Field, blender::float2); +MAKE_FIELD_CPP_TYPE(Float3Field, blender::float3); MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f); MAKE_FIELD_CPP_TYPE(BoolField, bool); MAKE_FIELD_CPP_TYPE(Int8Field, int8_t); MAKE_FIELD_CPP_TYPE(Int32Field, int32_t); MAKE_FIELD_CPP_TYPE(StringField, std::string); - -} // namespace blender::fn diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 7b514b6a49b..9f742f11ce4 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_index_mask_ops.hh" #include "BLI_map.hh" #include "BLI_multi_value_map.hh" #include "BLI_set.hh" @@ -692,29 +693,21 @@ GPointer FieldConstant::value() const * FieldEvaluator. */ -static Vector<int64_t> indices_from_selection(IndexMask mask, const VArray<bool> &selection) +static IndexMask index_mask_from_selection(const IndexMask full_mask, + VArray<bool> &selection, + ResourceScope &scope) { - /* If the selection is just a single value, it's best to avoid calling this - * function when constructing an IndexMask and use an IndexRange instead. */ - BLI_assert(!selection.is_single()); - - Vector<int64_t> indices; if (selection.is_span()) { Span<bool> span = selection.get_internal_span(); - for (const int64_t i : mask) { - if (span[i]) { - indices.append(i); - } - } + return index_mask_ops::find_indices_based_on_predicate( + full_mask, 4096, scope.construct<Vector<int64_t>>(), [&](const int curve_index) { + return span[curve_index]; + }); } - else { - for (const int i : mask) { - if (selection[i]) { - indices.append(i); - } - } - } - return indices; + return index_mask_ops::find_indices_based_on_predicate( + full_mask, 1024, scope.construct<Vector<int64_t>>(), [&](const int curve_index) { + return selection[curve_index]; + }); } int FieldEvaluator::add_with_destination(GField field, GVMutableArray dst) @@ -763,7 +756,7 @@ static IndexMask evaluate_selection(const Field<bool> &selection_field, } return IndexRange(0); } - return scope.add_value(indices_from_selection(full_mask, selection)).as_span(); + return index_mask_from_selection(full_mask, selection, scope); } return full_mask; } @@ -799,8 +792,7 @@ IndexMask FieldEvaluator::get_evaluated_as_mask(const int field_index) } return IndexRange(0); } - - return scope_.add_value(indices_from_selection(mask_, varray)).as_span(); + return index_mask_from_selection(mask_, varray, scope_); } IndexMask FieldEvaluator::get_evaluated_selection_as_mask() diff --git a/source/blender/functions/tests/FN_field_test.cc b/source/blender/functions/tests/FN_field_test.cc index 6417066b47b..ea3c0471411 100644 --- a/source/blender/functions/tests/FN_field_test.cc +++ b/source/blender/functions/tests/FN_field_test.cc @@ -2,7 +2,7 @@ #include "testing/testing.h" -#include "FN_cpp_type.hh" +#include "BLI_cpp_type.hh" #include "FN_field.hh" #include "FN_multi_function_builder.hh" #include "FN_multi_function_test_common.hh" diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index 0c2377fde6d..5ba9bc066fd 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -32,7 +32,7 @@ static void copy_with_map(const VArray<T> &src, Span<int> map, MutableSpan<T> ds static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_component, const Span<int> vert_indices, const Span<int> curve_offsets, - const IndexRange cyclic_splines) + const IndexRange cyclic_curves) { Curves *curves_id = bke::curves_new_nomain(vert_indices.size(), curve_offsets.size()); bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); @@ -41,7 +41,7 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen curves.curve_types().fill(CURVE_TYPE_POLY); curves.cyclic().fill(false); - curves.cyclic().slice(cyclic_splines).fill(true); + curves.cyclic().slice(cyclic_curves).fill(true); Set<bke::AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids(); @@ -59,15 +59,15 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen continue; } - const fn::GVArray mesh_attribute = mesh_component.attribute_try_get_for_read( - attribute_id, ATTR_DOMAIN_POINT); + const GVArray mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id, + ATTR_DOMAIN_POINT); /* Some attributes might not exist if they were builtin attribute on domains that don't * have any elements, i.e. a face attribute on the output of the line primitive node. */ if (!mesh_attribute) { continue; } - /* Copy attribute based on the map for this spline. */ + /* Copy attribute based on the map for this curve. */ attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) { using T = decltype(dummy); bke::OutputAttribute_Typed<T> attribute = @@ -81,12 +81,12 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen } struct CurveFromEdgesOutput { - /** The indices in the mesh for each control point of each result splines. */ + /** The indices in the mesh for each control point of each result curves. */ Vector<int> vert_indices; /** The first index of each curve in the result. */ Vector<int> curve_offsets; - /** A subset of splines that should be set cyclic. */ - IndexRange cyclic_splines; + /** A subset of curves that should be set cyclic. */ + IndexRange cyclic_curves; }; static CurveFromEdgesOutput edges_to_curve_point_indices(Span<MVert> verts, @@ -128,7 +128,7 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span<MVert> verts, Array<int> unused_edges = std::move(used_slots); for (const int start_vert : verts.index_range()) { - /* The vertex will be part of a cyclic spline. */ + /* The vertex will be part of a cyclic curve. */ if (neighbor_count[start_vert] == 2) { continue; } @@ -171,10 +171,10 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span<MVert> verts, } } - /* All splines added after this are cyclic. */ + /* All curves added after this are cyclic. */ const int cyclic_start = curve_offsets.size(); - /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + /* All remaining edges are part of cyclic curves (we skipped vertices with two edges before). */ for (const int start_vert : verts.index_range()) { if (unused_edges[start_vert] != 2) { continue; @@ -230,7 +230,7 @@ Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMa selected_edges); return create_curve_from_vert_indices( - mesh_component, output.vert_indices, output.curve_offsets, output.cyclic_splines); + mesh_component, output.vert_indices, output.curve_offsets, output.cyclic_curves); } } // namespace blender::geometry diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 32bad07de4f..8d186edc607 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -18,7 +18,6 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_pointcloud.h" -#include "BKE_spline.hh" #include "BKE_type_conversions.hh" namespace blender::geometry { @@ -30,12 +29,6 @@ using blender::bke::object_get_evaluated_geometry_set; using blender::bke::OutputAttribute; using blender::bke::OutputAttribute_Typed; using blender::bke::ReadAttributeLookup; -using blender::fn::CPPType; -using blender::fn::GArray; -using blender::fn::GMutableSpan; -using blender::fn::GSpan; -using blender::fn::GVArray; -using blender::fn::GVArray_GSpan; /** * An ordered set of attribute ids. Attributes are ordered to avoid name lookups in many places. @@ -274,7 +267,7 @@ static void threaded_copy(const GSpan src, GMutableSpan dst) }); } -static void threaded_fill(const fn::GPointer value, GMutableSpan dst) +static void threaded_fill(const GPointer value, GMutableSpan dst) { BLI_assert(*value.type() == dst.type()); threading::parallel_for(IndexRange(dst.size()), 1024, [&](const IndexRange range) { @@ -1146,8 +1139,8 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, const Curves &curves_id = *curves_info.curves; const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - const IndexRange dst_point_range{task.start_indices.point, curves.points_size()}; - const IndexRange dst_curve_range{task.start_indices.curve, curves.curves_size()}; + const IndexRange dst_point_range{task.start_indices.point, curves.points_num()}; + const IndexRange dst_curve_range{task.start_indices.curve, curves.curves_num()}; copy_transformed_positions( curves.positions(), task.transform, dst_curves.positions().slice(dst_point_range)); @@ -1201,9 +1194,9 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, [&](const AttributeDomain domain) { switch (domain) { case ATTR_DOMAIN_POINT: - return IndexRange(task.start_indices.point, curves.points_size()); + return IndexRange(task.start_indices.point, curves.points_num()); case ATTR_DOMAIN_CURVE: - return IndexRange(task.start_indices.curve, curves.curves_size()); + return IndexRange(task.start_indices.curve, curves.curves_num()); default: BLI_assert_unreachable(); return IndexRange(); diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 752d4aea61c..6108629183c 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -37,6 +37,7 @@ set(SRC intern/MOD_gpencilbuild.c intern/MOD_gpencilcolor.c intern/MOD_gpencildash.c + intern/MOD_gpencilenvelope.c intern/MOD_gpencilhook.c intern/MOD_gpencillattice.c intern/MOD_gpencillength.c diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h index ff280b9ca0d..e88d864a86e 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -35,6 +35,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle; extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart; extern GpencilModifierTypeInfo modifierType_Gpencil_Dash; extern GpencilModifierTypeInfo modifierType_Gpencil_Shrinkwrap; +extern GpencilModifierTypeInfo modifierType_Gpencil_Envelope; /* MOD_gpencil_util.c */ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index e766615101a..6cf7f6f11e5 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -56,6 +56,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) INIT_GP_TYPE(Lineart); INIT_GP_TYPE(Dash); INIT_GP_TYPE(Shrinkwrap); + INIT_GP_TYPE(Envelope); #undef INIT_GP_TYPE } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index 439073752da..25c7fdca9f6 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -97,12 +97,13 @@ static bool stroke_dash(const bGPDstroke *gps, int new_stroke_offset = 0; int trim_start = 0; + int sequence_length = 0; for (int i = 0; i < dmd->segments_len; i++) { - if (dmd->segments[i].dash + real_gap(&dmd->segments[i]) < 1) { - BLI_assert_unreachable(); - /* This means there's a part that doesn't have any length, can't do dot-dash. */ - return false; - } + sequence_length += dmd->segments[i].dash + real_gap(&dmd->segments[i]); + } + if (sequence_length < 1) { + /* This means the whole segment has no length, can't do dot-dash. */ + return false; } const DashGpencilModifierSegment *const first_segment = &dmd->segments[0]; @@ -204,9 +205,10 @@ static void apply_dash_for_frame( dmd->flag & GP_LENGTH_INVERT_PASS, dmd->flag & GP_LENGTH_INVERT_LAYERPASS, dmd->flag & GP_LENGTH_INVERT_MATERIAL)) { - stroke_dash(gps, dmd, &result); - BLI_remlink(&gpf->strokes, gps); - BKE_gpencil_free_stroke(gps); + if (stroke_dash(gps, dmd, &result)) { + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); + } } } bGPDstroke *gps_dash; @@ -232,6 +234,18 @@ static void bakeModifier(Main *UNUSED(bmain), /* -------------------------------- */ +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)md; + + int sequence_length = 0; + for (int i = 0; i < dmd->segments_len; i++) { + sequence_length += dmd->segments[i].dash + real_gap(&dmd->segments[i]); + } + /* This means the whole segment has no length, can't do dot-dash. */ + return sequence_length < 1; +} + /* Generic "generateStrokes" callback */ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) { @@ -362,7 +376,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Dash = { /* initData */ initData, /* freeData */ freeData, - /* isDisabled */ NULL, + /* isDisabled */ isDisabled, /* updateDepsgraph */ NULL, /* dependsOnTime */ NULL, /* foreachIDLink */ foreachIDLink, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c new file mode 100644 index 00000000000..f8ac8d95493 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c @@ -0,0 +1,628 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2017 Blender Foundation. */ + +/** \file + * \ingroup modifiers + */ + +#include <stdio.h> + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_math_geom.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_lib_query.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "MOD_gpencil_modifiertypes.h" +#include "MOD_gpencil_ui_common.h" +#include "MOD_gpencil_util.h" + +#include "MEM_guardedalloc.h" + +static void initData(GpencilModifierData *md) +{ + EnvelopeGpencilModifierData *gpmd = (EnvelopeGpencilModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier)); + + MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(EnvelopeGpencilModifierData), modifier); +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copydata_generic(md, target); +} + +static float calc_min_radius_v3v3(float p1[3], float p2[3], float dir[3]) +{ + /* Use plane-conic-intersections to choose the maximal radius. + * The conic is defined in 4D as f({x,y,z,t}) = x*x + y*y + z*z - t*t = 0 + * Then a plane is defined parametrically as + * {p}(u, v) = {p1,0}*u + {p2,0}*(1-u) + {dir,1}*v with 0 <= u <= 1 and v >= 0 + * Now compute the intersection point with the smallest t. + * To do so, compute the parameters u, v such that f(p(u, v)) = 0 and v is minimal. + * This can be done analytically and the solution is: + * u = -dot(p2,dir) / dot(p1-p2, dir) +/- sqrt((dot(p2,dir) / dot(p1-p2, dir))^2 - + * (2*dot(p1-p2,p2)*dot(p2,dir)-dot(p2,p2)*dot(p1-p2,dir))/(dot(p1-p2,dir)*dot(p1-p2,p1-p2))); + * v = ({p1}u + {p2}*(1-u))^2 / (2*(dot(p1,dir)*u + dot(p2,dir)*(1-u))); + */ + float diff[3]; + float p1_dir = dot_v3v3(p1, dir); + float p2_dir = dot_v3v3(p2, dir); + float p2_sqr = len_squared_v3(p2); + float diff_dir = p1_dir - p2_dir; + float u = 0.5f; + if (diff_dir != 0.0f) { + float p = p2_dir / diff_dir; + sub_v3_v3v3(diff, p1, p2); + float diff_sqr = len_squared_v3(diff); + float diff_p2 = dot_v3v3(diff, p2); + float q = (2 * diff_p2 * p2_dir - p2_sqr * diff_dir) / (diff_dir * diff_sqr); + if (p * p - q >= 0) { + u = -p - sqrtf(p * p - q) * copysign(1.0f, p); + CLAMP(u, 0.0f, 1.0f); + } + else { + u = 0.5f - copysign(0.5f, p); + } + } + else { + float p1_sqr = len_squared_v3(p1); + u = p1_sqr < p2_sqr ? 1.0f : 0.0f; + } + float p[3]; + interp_v3_v3v3(p, p2, p1, u); + /* v is the determined minimal radius. In case p1 and p2 are the same, there is a + * simple proof for the following formula using the geometric mean theorem and Thales theorem. */ + float v = len_squared_v3(p) / (2 * interpf(p1_dir, p2_dir, u)); + if (v < 0 || !isfinite(v)) { + /* No limit to the radius from this segment. */ + return 1e16f; + } + return v; +} + +static float calc_radius_limit( + bGPDstroke *gps, bGPDspoint *points, float dir[3], int spread, const int i) +{ + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; + bGPDspoint *pt = &points[i]; + + /* NOTE this part is the second performance critical part. Improvements are welcome. */ + float radius_limit = 1e16f; + float p1[3], p2[3]; + if (is_cyclic) { + if (gps->totpoints / 2 < spread) { + spread = gps->totpoints / 2; + } + const int start = i + gps->totpoints; + for (int j = -spread; j <= spread; j++) { + j += (j == 0); + const int i1 = (start + j) % gps->totpoints; + const int i2 = (start + j + (j > 0) - (j < 0)) % gps->totpoints; + sub_v3_v3v3(p1, &points[i1].x, &pt->x); + sub_v3_v3v3(p2, &points[i2].x, &pt->x); + float r = calc_min_radius_v3v3(p1, p2, dir); + radius_limit = min_ff(radius_limit, r); + } + } + else { + const int start = max_ii(-spread, 1 - i); + const int end = min_ii(spread, gps->totpoints - 2 - i); + for (int j = start; j <= end; j++) { + if (j == 0) { + continue; + } + const int i1 = i + j; + const int i2 = i + j + (j > 0) - (j < 0); + sub_v3_v3v3(p1, &points[i1].x, &pt->x); + sub_v3_v3v3(p2, &points[i2].x, &pt->x); + float r = calc_min_radius_v3v3(p1, p2, dir); + radius_limit = min_ff(radius_limit, r); + } + } + return radius_limit; +} + +static void apply_stroke_envelope( + bGPDstroke *gps, int spread, const int def_nr, const bool invert_vg, const float thickness) +{ + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; + if (is_cyclic) { + const int half = gps->totpoints / 2; + spread = abs(((spread + half) % gps->totpoints) - half); + } + else { + spread = min_ii(spread, gps->totpoints - 1); + } + + const int spread_left = (spread + 2) / 2; + const int spread_right = (spread + 1) / 2; + + /* Copy the point data. Only need positions, but extracting them + * is probably just as expensive as a full copy. */ + bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points); + + /* Deform the stroke to match the envelope shape. */ + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + + /* Verify in vertex group. */ + float weight = get_modifier_point_weight(dvert, invert_vg, def_nr); + if (weight < 0.0f) { + continue; + } + + int index1 = i - spread_left; + int index2 = i + spread_right; + CLAMP(index1, 0, gps->totpoints - 1); + CLAMP(index2, 0, gps->totpoints - 1); + + bGPDspoint *point = &gps->points[i]; + point->pressure *= interpf(thickness, 1.0f, weight); + + float closest[3]; + float closest2[3]; + copy_v3_v3(closest2, &point->x); + float dist = 0.0f; + float dist2 = 0.0f; + /* Create plane from point and neighbors and intersect that with the line. */ + float v1[3], v2[3], plane_no[3]; + sub_v3_v3v3( + v1, + &old_points[is_cyclic ? (i - 1 + gps->totpoints) % gps->totpoints : max_ii(0, i - 1)].x, + &old_points[i].x); + sub_v3_v3v3( + v2, + &old_points[is_cyclic ? (i + 1) % gps->totpoints : min_ii(gps->totpoints - 1, i + 1)].x, + &old_points[i].x); + normalize_v3(v1); + normalize_v3(v2); + sub_v3_v3v3(plane_no, v1, v2); + if (normalize_v3(plane_no) == 0.0f) { + continue; + } + /* Now find the intersections with the plane. */ + /* NOTE this part is the first performance critical part. Improvements are welcome. */ + float tmp_closest[3]; + for (int j = -spread_right; j <= spread_left; j++) { + const int i1 = is_cyclic ? (i + j - spread_left + gps->totpoints) % gps->totpoints : + max_ii(0, i + j - spread_left); + const int i2 = is_cyclic ? (i + j + spread_right) % gps->totpoints : + min_ii(gps->totpoints - 1, i + j + spread_right); + /*bool side = dot_v3v3(&old_points[i1].x, plane_no) < dot_v3v3(plane_no, &old_points[i2].x); + if (side) { + continue; + }*/ + float lambda = line_plane_factor_v3( + &point->x, plane_no, &old_points[i1].x, &old_points[i2].x); + if (lambda <= 0.0f || lambda >= 1.0f) { + continue; + } + interp_v3_v3v3(tmp_closest, &old_points[i1].x, &old_points[i2].x, lambda); + + float dir[3]; + sub_v3_v3v3(dir, tmp_closest, &point->x); + float d = len_v3(dir); + /* Use a formula to find the diameter of the circle that would touch the line. */ + float cos_angle = fabsf(dot_v3v3(plane_no, &old_points[i1].x) - + dot_v3v3(plane_no, &old_points[i2].x)) / + len_v3v3(&old_points[i1].x, &old_points[i2].x); + d *= 2 * cos_angle / (1 + cos_angle); + float to_closest[3]; + sub_v3_v3v3(to_closest, closest, &point->x); + if (dist == 0.0f) { + dist = d; + copy_v3_v3(closest, tmp_closest); + } + else if (dot_v3v3(to_closest, dir) >= 0) { + if (d > dist) { + dist = d; + copy_v3_v3(closest, tmp_closest); + } + } + else { + if (d > dist2) { + dist2 = d; + copy_v3_v3(closest2, tmp_closest); + } + } + } + if (dist == 0.0f) { + copy_v3_v3(closest, &point->x); + } + if (dist2 == 0.0f) { + copy_v3_v3(closest2, &point->x); + } + dist = dist + dist2; + + if (dist < FLT_EPSILON) { + continue; + } + + float use_dist = dist; + + /* Apply radius limiting to not cross existing lines. */ + float dir[3], new_center[3]; + interp_v3_v3v3(new_center, closest2, closest, 0.5f); + sub_v3_v3v3(dir, new_center, &point->x); + if (normalize_v3(dir) != 0.0f && (is_cyclic || (i > 0 && i < gps->totpoints - 1))) { + const float max_radius = calc_radius_limit(gps, old_points, dir, spread, i); + use_dist = min_ff(use_dist, 2 * max_radius); + } + + float fac = use_dist * weight; + /* The 50 is an internal constant for the default pixel size. The result can be messed up if + * #bGPdata.pixfactor is not default, but I think modifiers shouldn't access that. */ + point->pressure += fac * 50.0f * GP_DEFAULT_PIX_FACTOR; + interp_v3_v3v3(&point->x, &point->x, new_center, fac / len_v3v3(closest, closest2)); + } + + MEM_freeN(old_points); +} + +/** + * Apply envelope effect to the stroke. + */ +static void deformStroke(GpencilModifierData *md, + Depsgraph *UNUSED(depsgraph), + Object *ob, + bGPDlayer *gpl, + bGPDframe *UNUSED(gpf), + bGPDstroke *gps) +{ + EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md; + if (mmd->mode != GP_ENVELOPE_DEFORM) { + return; + } + const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, + mmd->material, + mmd->pass_index, + mmd->layer_pass, + 3, + gpl, + gps, + mmd->flag & GP_ENVELOPE_INVERT_LAYER, + mmd->flag & GP_ENVELOPE_INVERT_PASS, + mmd->flag & GP_ENVELOPE_INVERT_LAYERPASS, + mmd->flag & GP_ENVELOPE_INVERT_MATERIAL)) { + return; + } + + if (mmd->spread <= 0) { + return; + } + + apply_stroke_envelope( + gps, mmd->spread, def_nr, (mmd->flag & GP_ENVELOPE_INVERT_VGROUP) != 0, mmd->thickness); +} + +static void add_stroke(Object *ob, + bGPDstroke *gps, + const int point_index, + const int connection_index, + const int size, + const int mat_nr, + const float thickness, + const float strength, + ListBase *results) +{ + bGPdata *gpd = ob->data; + bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, size, gps->thickness); + + const int size1 = size == 4 ? 2 : 1; + const int size2 = size - size1; + + memcpy(&gps_dst->points[0], &gps->points[connection_index], size1 * sizeof(bGPDspoint)); + memcpy(&gps_dst->points[size1], &gps->points[point_index], size2 * sizeof(bGPDspoint)); + + for (int i = 0; i < size; i++) { + gps_dst->points[i].pressure *= thickness; + gps_dst->points[i].strength *= strength; + memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime)); + } + + if (gps->dvert != NULL) { + gps_dst->dvert = MEM_malloc_arrayN(size, sizeof(MDeformVert), __func__); + BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], size1); + BKE_defvert_array_copy(&gps_dst->dvert[size1], &gps->dvert[point_index], size2); + } + + BLI_addtail(results, gps_dst); + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gpd, gps_dst); +} + +static void add_stroke_cyclic(Object *ob, + bGPDstroke *gps, + const int point_index, + const int connection_index, + const int mat_nr, + const float thickness, + const float strength, + ListBase *results) +{ + bGPdata *gpd = ob->data; + bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 4, gps->thickness); + + int connection_index2 = (connection_index + 1) % gps->totpoints; + int point_index2 = (point_index + 1) % gps->totpoints; + + gps_dst->points[0] = gps->points[connection_index]; + gps_dst->points[1] = gps->points[connection_index2]; + gps_dst->points[2] = gps->points[point_index]; + gps_dst->points[3] = gps->points[point_index2]; + for (int i = 0; i < 4; i++) { + gps_dst->points[i].pressure *= thickness; + gps_dst->points[i].strength *= strength; + memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime)); + } + + if (gps->dvert != NULL) { + gps_dst->dvert = MEM_malloc_arrayN(4, sizeof(MDeformVert), __func__); + BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], 1); + BKE_defvert_array_copy(&gps_dst->dvert[1], &gps->dvert[connection_index2], 1); + BKE_defvert_array_copy(&gps_dst->dvert[2], &gps->dvert[point_index], 1); + BKE_defvert_array_copy(&gps_dst->dvert[3], &gps->dvert[point_index2], 1); + } + + BLI_addtail(results, gps_dst); + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gpd, gps_dst); +} + +static void add_stroke_simple(Object *ob, + bGPDstroke *gps, + const int point_index, + const int connection_index, + const int mat_nr, + const float thickness, + const float strength, + ListBase *results) +{ + bGPdata *gpd = ob->data; + bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 2, gps->thickness); + + gps_dst->points[0] = gps->points[connection_index]; + gps_dst->points[0].pressure *= thickness; + gps_dst->points[0].strength *= strength; + memset(&gps_dst->points[0].runtime, 0, sizeof(bGPDspoint_Runtime)); + gps_dst->points[1] = gps->points[point_index]; + gps_dst->points[1].pressure *= thickness; + gps_dst->points[1].strength *= strength; + memset(&gps_dst->points[1].runtime, 0, sizeof(bGPDspoint_Runtime)); + + if (gps->dvert != NULL) { + gps_dst->dvert = MEM_malloc_arrayN(2, sizeof(MDeformVert), __func__); + BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], 1); + BKE_defvert_array_copy(&gps_dst->dvert[1], &gps->dvert[point_index], 1); + } + + BLI_addtail(results, gps_dst); + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gpd, gps_dst); +} + +static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +{ + EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md; + ListBase duplicates = {0}; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, + mmd->material, + mmd->pass_index, + mmd->layer_pass, + 3, + gpl, + gps, + mmd->flag & GP_ENVELOPE_INVERT_LAYER, + mmd->flag & GP_ENVELOPE_INVERT_PASS, + mmd->flag & GP_ENVELOPE_INVERT_LAYERPASS, + mmd->flag & GP_ENVELOPE_INVERT_MATERIAL)) { + continue; + } + + const int mat_nr = mmd->mat_nr < 0 ? gps->mat_nr : min_ii(mmd->mat_nr, ob->totcol - 1); + if (mmd->mode == GP_ENVELOPE_FILLS) { + if (gps->flag & GP_STROKE_CYCLIC) { + for (int i = 0; i < gps->totpoints; i++) { + const int connection_index = (i + mmd->spread) % gps->totpoints; + add_stroke_cyclic( + ob, gps, i, connection_index, mat_nr, mmd->thickness, mmd->strength, &duplicates); + } + } + else { + for (int i = 1; i < gps->totpoints - 1 && i < mmd->spread + 1; i++) { + add_stroke(ob, gps, i, 0, 3, mat_nr, mmd->thickness, mmd->strength, &duplicates); + } + for (int i = 0; i < gps->totpoints - 1; i++) { + const int connection_index = min_ii(i + mmd->spread, gps->totpoints - 1); + const int size = i == gps->totpoints - 2 ? 2 : + connection_index < gps->totpoints - 1 ? 4 : + 3; + add_stroke(ob, + gps, + i, + connection_index, + size, + mat_nr, + mmd->thickness, + mmd->strength, + &duplicates); + } + } + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); + } + else { + BLI_assert(mmd->mode == GP_ENVELOPE_SEGMENTS); + if (gps->flag & GP_STROKE_CYCLIC) { + for (int i = 0; i < gps->totpoints; i++) { + const int connection_index = (i + 1 + mmd->spread) % gps->totpoints; + add_stroke_simple( + ob, gps, i, connection_index, mat_nr, mmd->thickness, mmd->strength, &duplicates); + } + } + else { + for (int i = -mmd->spread; i < gps->totpoints - 1; i++) { + const int connection_index = min_ii(i + 1 + mmd->spread, gps->totpoints - 1); + add_stroke_simple(ob, + gps, + max_ii(0, i), + connection_index, + mat_nr, + mmd->thickness, + mmd->strength, + &duplicates); + } + } + } + } + if (!BLI_listbase_is_empty(&duplicates)) { + /* Add strokes to the start of the stroke list to ensure the new lines are drawn underneath the + * original line. */ + BLI_movelisttolist_reverse(&gpf->strokes, &duplicates); + } +} + +/** + * Apply envelope effect to the strokes. + */ +static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) +{ + EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md; + if (mmd->mode == GP_ENVELOPE_DEFORM || mmd->spread <= 0) { + return; + } + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl); + if (gpf == NULL) { + continue; + } + generate_geometry(md, ob, gpl, gpf); + } +} + +static void bakeModifier(struct Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) +{ + EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md; + if (mmd->mode == GP_ENVELOPE_DEFORM) { + generic_bake_deform_stroke(depsgraph, md, ob, false, deformStroke); + } + else { + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + generate_geometry(md, ob, gpl, gpf); + } + } + } +} + +static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md; + + walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); + + uiItemR(layout, ptr, "spread", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "thickness", 0, NULL, ICON_NONE); + + const int mode = RNA_enum_get(ptr, "mode"); + if (mode != GP_ENVELOPE_DEFORM) { + uiItemR(layout, ptr, "strength", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "mat_nr", 0, NULL, ICON_NONE); + } + + gpencil_modifier_panel_end(layout, ptr); +} + +static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + gpencil_modifier_masking_panel_draw(panel, true, true); +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = gpencil_modifier_panel_register( + region_type, eGpencilModifierType_Envelope, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Envelope = { + /* name */ "Envelope", + /* structName */ "EnvelopeGpencilModifierData", + /* structSize */ sizeof(EnvelopeGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ bakeModifier, + /* remapTime */ NULL, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, + /* panelRegister */ panelRegister, +}; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 2f648d7ed4b..c96321b67aa 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1753,6 +1753,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu obi->original_me, &((struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, })); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c index 93a3b33e713..4ea17b25995 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c @@ -244,4 +244,6 @@ void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb) total += sum_this; sum_this = 0; count_this = 0; + + (void)total; /* Ignored. */ } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 3ad43ef05af..1840b5447e3 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -2,16 +2,17 @@ # Copyright 2006 Blender Foundation. All rights reserved. # WITH_OPENGL limits the visibility of the opengl headers to just GPU and bg_gpu, -# to more easily highlight codepadths in other libraries that need to be refactored, +# to more easily highlight code-paths in other libraries that need to be refactored, # bf_gpu is allowed to have opengl regardless of this option. -if(NOT WITH_OPENGL) +if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND) add_definitions(-DWITH_OPENGL) endif() set(INC . intern + metal opengl ../blenkernel ../blenlib @@ -71,32 +72,13 @@ set(SRC intern/gpu_shader_interface.cc intern/gpu_shader_log.cc intern/gpu_state.cc + intern/gpu_storage_buffer.cc intern/gpu_texture.cc intern/gpu_uniform_buffer.cc intern/gpu_vertex_buffer.cc intern/gpu_vertex_format.cc intern/gpu_viewport.c - opengl/gl_backend.cc - opengl/gl_batch.cc - opengl/gl_compute.cc - opengl/gl_context.cc - opengl/gl_debug.cc - opengl/gl_debug_layer.cc - opengl/gl_drawlist.cc - opengl/gl_framebuffer.cc - opengl/gl_immediate.cc - opengl/gl_index_buffer.cc - opengl/gl_query.cc - opengl/gl_shader.cc - opengl/gl_shader_interface.cc - opengl/gl_shader_log.cc - opengl/gl_state.cc - opengl/gl_texture.cc - opengl/gl_uniform_buffer.cc - opengl/gl_vertex_array.cc - opengl/gl_vertex_buffer.cc - GPU_batch.h GPU_batch_presets.h GPU_batch_utils.h @@ -122,6 +104,7 @@ set(SRC GPU_shader.h GPU_shader_shared.h GPU_state.h + GPU_storage_buffer.h GPU_texture.h GPU_uniform_buffer.h GPU_vertex_buffer.h @@ -151,10 +134,35 @@ set(SRC intern/gpu_shader_interface.hh intern/gpu_shader_private.hh intern/gpu_state_private.hh + intern/gpu_storage_buffer_private.hh intern/gpu_texture_private.hh intern/gpu_uniform_buffer_private.hh intern/gpu_vertex_buffer_private.hh intern/gpu_vertex_format_private.h +) + +set(OPENGL_SRC + + opengl/gl_backend.cc + opengl/gl_batch.cc + opengl/gl_compute.cc + opengl/gl_context.cc + opengl/gl_debug.cc + opengl/gl_debug_layer.cc + opengl/gl_drawlist.cc + opengl/gl_framebuffer.cc + opengl/gl_immediate.cc + opengl/gl_index_buffer.cc + opengl/gl_query.cc + opengl/gl_shader.cc + opengl/gl_shader_interface.cc + opengl/gl_shader_log.cc + opengl/gl_state.cc + opengl/gl_storage_buffer.cc + opengl/gl_texture.cc + opengl/gl_uniform_buffer.cc + opengl/gl_vertex_array.cc + opengl/gl_vertex_buffer.cc opengl/gl_backend.hh opengl/gl_batch.hh @@ -170,12 +178,29 @@ set(SRC opengl/gl_shader.hh opengl/gl_shader_interface.hh opengl/gl_state.hh + opengl/gl_storage_buffer.hh opengl/gl_texture.hh opengl/gl_uniform_buffer.hh opengl/gl_vertex_array.hh opengl/gl_vertex_buffer.hh ) +set(METAL_SRC + metal/mtl_backend.mm + + metal/mtl_backend.hh + metal/mtl_capabilities.hh +) + +# Select Backend source based on availability +if(WITH_OPENGL) + list(APPEND SRC ${OPENGL_SRC}) +endif() + +if(WITH_METAL_BACKEND) + list(APPEND SRC ${METAL_SRC}) +endif() + set(LIB ${BLENDER_GL_LIBRARIES} ) @@ -188,6 +213,7 @@ endif() set(GLSL_SRC GPU_shader_shared.h + shaders/opengl/glsl_shader_defines.glsl shaders/gpu_shader_depth_only_frag.glsl shaders/gpu_shader_uniform_color_frag.glsl @@ -386,6 +412,8 @@ list(APPEND SRC ${glsl_source_list_file}) list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_SHADER_CREATE_INFOS + ../draw/engines/gpencil/shaders/infos/gpencil_info.hh + ../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh ../draw/engines/workbench/shaders/infos/workbench_composite_info.hh ../draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh ../draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh @@ -398,9 +426,9 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/workbench/shaders/infos/workbench_volume_info.hh ../draw/engines/image/shaders/infos/engine_image_info.hh ../draw/intern/shaders/draw_fullscreen_info.hh + ../draw/intern/shaders/draw_hair_refine_info.hh ../draw/intern/shaders/draw_object_infos_info.hh ../draw/intern/shaders/draw_view_info.hh - ../draw/intern/shaders/draw_hair_refine_info.hh shaders/infos/gpu_clip_planes_info.hh shaders/infos/gpu_shader_2D_area_borders_info.hh diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 672139aa407..0d0542aa528 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -29,6 +29,7 @@ int GPU_max_batch_indices(void); int GPU_max_batch_vertices(void); int GPU_max_vertex_attribs(void); int GPU_max_varying_floats(void); +int GPU_max_shader_storage_buffer_bindings(void); int GPU_extensions_len(void); const char *GPU_extension_get(int i); diff --git a/source/blender/gpu/GPU_compute.h b/source/blender/gpu/GPU_compute.h index 437b0223303..6dfd6f73ae8 100644 --- a/source/blender/gpu/GPU_compute.h +++ b/source/blender/gpu/GPU_compute.h @@ -9,6 +9,7 @@ #include "BLI_sys_types.h" #include "GPU_shader.h" +#include "GPU_storage_buffer.h" #ifdef __cplusplus extern "C" { @@ -19,6 +20,8 @@ void GPU_compute_dispatch(GPUShader *shader, uint groups_y_len, uint groups_z_len); +void GPU_compute_dispatch_indirect(GPUShader *shader, GPUStorageBuf *indirect_buf); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index 85433913456..f3b7f8c29bf 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -11,18 +11,15 @@ #include "GPU_batch.h" #include "GPU_common.h" +#include "GPU_platform.h" #ifdef __cplusplus extern "C" { #endif -typedef enum eGPUBackendType { - GPU_BACKEND_NONE = 0, - GPU_BACKEND_OPENGL, -} eGPUBackendType; - void GPU_backend_init(eGPUBackendType backend); void GPU_backend_exit(void); +bool GPU_backend_supported(eGPUBackendType type); eGPUBackendType GPU_backend_get_type(void); @@ -49,6 +46,14 @@ GPUContext *GPU_context_active_get(void); void GPU_context_main_lock(void); void GPU_context_main_unlock(void); +/* GPU Begin/end work blocks */ +void GPU_render_begin(void); +void GPU_render_end(void); + +/* For operations which need to run exactly once per frame -- even if there are no render updates. + */ +void GPU_render_step(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h index d5a0fcfa921..d999216c7ff 100644 --- a/source/blender/gpu/GPU_platform.h +++ b/source/blender/gpu/GPU_platform.h @@ -12,6 +12,13 @@ /* GPU platform support */ +typedef enum eGPUBackendType { + GPU_BACKEND_NONE = 0, + GPU_BACKEND_OPENGL = 1 << 0, + GPU_BACKEND_METAL = 1 << 1, + GPU_BACKEND_ANY = 0xFFFFFFFFu +} eGPUBackendType; + /* GPU Types */ typedef enum eGPUDeviceType { GPU_DEVICE_NVIDIA = (1 << 0), @@ -51,8 +58,13 @@ extern "C" { #endif /* GPU Types */ - +/* TODO: Verify all use-cases of GPU_type_matches to determine which graphics API it should apply + * to, and replace with `GPU_type_matches_ex` where appropriate. */ bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver); +bool GPU_type_matches_ex(eGPUDeviceType device, + eGPUOSType os, + eGPUDriverType driver, + eGPUBackendType backend); eGPUSupportLevel GPU_platform_support_level(void); const char *GPU_platform_vendor(void); diff --git a/source/blender/gpu/GPU_storage_buffer.h b/source/blender/gpu/GPU_storage_buffer.h new file mode 100644 index 00000000000..1478d490e23 --- /dev/null +++ b/source/blender/gpu/GPU_storage_buffer.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + * + * Storage buffers API. Used to handle many way bigger buffers than Uniform buffers update at once. + * Make sure that the data structure is compatible with what the implementation expect. + * (see "7.8 Shader Buffer Variables and Shader Storage Blocks" from the OpenGL spec for more info + * about std430 layout) + * Rule of thumb: Padding to 16bytes, don't use vec3. + */ + +#pragma once + +#include "GPU_texture.h" +#include "GPU_vertex_buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ListBase; + +/** Opaque type hiding blender::gpu::StorageBuf. */ +typedef struct GPUStorageBuf GPUStorageBuf; + +GPUStorageBuf *GPU_storagebuf_create_ex(size_t size, + const void *data, + GPUUsageType usage, + const char *name); + +#define GPU_storagebuf_create(size) \ + GPU_storagebuf_create_ex(size, NULL, GPU_USAGE_DYNAMIC, __func__); + +void GPU_storagebuf_free(GPUStorageBuf *ssbo); + +void GPU_storagebuf_update(GPUStorageBuf *ssbo, const void *data); + +void GPU_storagebuf_bind(GPUStorageBuf *ssbo, int slot); +void GPU_storagebuf_unbind(GPUStorageBuf *ssbo); +void GPU_storagebuf_unbind_all(void); + +void GPU_storagebuf_clear(GPUStorageBuf *ssbo, + eGPUTextureFormat internal_format, + eGPUDataFormat data_format, + void *data); +void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 734d407d011..37edc2abeb2 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -230,6 +230,7 @@ GPUTexture *GPU_texture_create_error(int dimension, bool array); * Create an alias of the source texture data. * If \a src is freed, the texture view will continue to be valid. * If \a mip_start or \a mip_len is bigger than available mips they will be clamped. + * If \a cube_as_array is true, then the texture cube (array) becomes a 2D array texture. * TODO(@fclem): Target conversion is not implemented yet. */ GPUTexture *GPU_texture_create_view(const char *name, @@ -238,7 +239,8 @@ GPUTexture *GPU_texture_create_view(const char *name, int mip_start, int mip_len, int layer_start, - int layer_len); + int layer_len, + bool cube_as_array); void GPU_texture_update_mipmap(GPUTexture *tex, int miplvl, @@ -299,6 +301,11 @@ void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter); void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter); void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp); void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4]); +/** + * Set depth stencil texture sampling behavior. Can work on texture views. + * If stencil sampling is enabled, an unsigned integer sampler is required. + */ +void GPU_texture_stencil_texture_mode_set(GPUTexture *tex, bool use_stencil); /** * Return the number of dimensions of the texture ignoring dimension of layers (1, 2 or 3). @@ -308,6 +315,8 @@ int GPU_texture_dimensions(const GPUTexture *tex); int GPU_texture_width(const GPUTexture *tex); int GPU_texture_height(const GPUTexture *tex); +int GPU_texture_layer_count(const GPUTexture *tex); +int GPU_texture_mip_count(const GPUTexture *tex); int GPU_texture_orig_width(const GPUTexture *tex); int GPU_texture_orig_height(const GPUTexture *tex); void GPU_texture_orig_size_set(GPUTexture *tex, int w, int h); diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 2b6e485f152..6e07e6c3229 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -9,6 +9,8 @@ #pragma once +#include "GPU_vertex_buffer.h" + namespace blender { namespace gpu { @@ -22,6 +24,7 @@ class QueryPool; class Shader; class Texture; class UniformBuf; +class StorageBuf; class VertBuf; class GPUBackend { @@ -32,6 +35,7 @@ class GPUBackend { virtual void samplers_update() = 0; virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0; + virtual void compute_dispatch_indirect(StorageBuf *indirect_buf) = 0; virtual Context *context_alloc(void *ghost_window) = 0; @@ -43,7 +47,14 @@ class GPUBackend { virtual Shader *shader_alloc(const char *name) = 0; virtual Texture *texture_alloc(const char *name) = 0; virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0; + virtual StorageBuf *storagebuf_alloc(int size, GPUUsageType usage, const char *name) = 0; virtual VertBuf *vertbuf_alloc() = 0; + + /* Render Frame Coordination -- + * Used for performing per-frame actions globally */ + virtual void render_begin() = 0; + virtual void render_end() = 0; + virtual void render_step() = 0; }; } // namespace gpu diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index 66e3a71317a..b750dacaca6 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -157,6 +157,11 @@ bool GPU_shader_image_load_store_support() return GCaps.shader_image_load_store_support; } +int GPU_max_shader_storage_buffer_bindings() +{ + return GCaps.max_shader_storage_buffer_bindings; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 458047ea6ad..4a951eb8458 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -20,11 +20,13 @@ namespace blender::gpu { */ struct GPUCapabilities { int max_texture_size = 0; + int max_texture_3d_size = 0; int max_texture_layers = 0; int max_textures = 0; int max_textures_vert = 0; int max_textures_geom = 0; int max_textures_frag = 0; + int max_samplers = 0; int max_work_group_count[3] = {0, 0, 0}; int max_work_group_size[3] = {0, 0, 0}; int max_uniforms_vert = 0; @@ -33,6 +35,7 @@ struct GPUCapabilities { int max_batch_vertices = 0; int max_vertex_attribs = 0; int max_varying_floats = 0; + int max_shader_storage_buffer_bindings = 0; int extensions_len = 0; const char *(*extension_get)(int); @@ -40,6 +43,8 @@ struct GPUCapabilities { bool compute_shader_support = false; bool shader_storage_buffer_objects_support = false; bool shader_image_load_store_support = false; + bool transform_feedback_support = false; + /* OpenGL related workarounds. */ bool mip_render_workaround = false; bool depth_blitting_workaround = false; @@ -47,6 +52,10 @@ struct GPUCapabilities { bool broken_amd_driver = false; bool use_hq_normals_workaround = false; /* Vulkan related workarounds. */ + + /* Metal related workarounds. */ + /* Minimum per-vertex stride in bytes (For a vertex buffer). */ + int minimum_per_vertex_stride = 1; }; extern GPUCapabilities GCaps; diff --git a/source/blender/gpu/intern/gpu_compute.cc b/source/blender/gpu/intern/gpu_compute.cc index e99455b756a..b45cf8211cb 100644 --- a/source/blender/gpu/intern/gpu_compute.cc +++ b/source/blender/gpu/intern/gpu_compute.cc @@ -7,10 +7,7 @@ #include "GPU_compute.h" #include "gpu_backend.hh" - -#ifdef __cplusplus -extern "C" { -#endif +#include "gpu_storage_buffer_private.hh" void GPU_compute_dispatch(GPUShader *shader, uint groups_x_len, @@ -22,6 +19,12 @@ void GPU_compute_dispatch(GPUShader *shader, gpu_backend.compute_dispatch(groups_x_len, groups_y_len, groups_z_len); } -#ifdef __cplusplus +void GPU_compute_dispatch_indirect(GPUShader *shader, GPUStorageBuf *indirect_buf_) +{ + blender::gpu::GPUBackend &gpu_backend = *blender::gpu::GPUBackend::get(); + blender::gpu::StorageBuf *indirect_buf = reinterpret_cast<blender::gpu::StorageBuf *>( + indirect_buf_); + + GPU_shader_bind(shader); + gpu_backend.compute_dispatch_indirect(indirect_buf); } -#endif diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 60e95e09a99..c6eaf7defdc 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -13,7 +13,9 @@ */ /* TODO: Create cmake option. */ -#define WITH_OPENGL_BACKEND 1 +#if WITH_OPENGL +# define WITH_OPENGL_BACKEND 1 +#endif #include "BLI_assert.h" #include "BLI_utildefines.h" @@ -32,6 +34,9 @@ # include "gl_backend.hh" # include "gl_context.hh" #endif +#ifdef WITH_METAL_BACKEND +# include "mtl_backend.hh" +#endif #include <mutex> #include <vector> @@ -141,21 +146,76 @@ void GPU_context_main_unlock() /** \} */ /* -------------------------------------------------------------------- */ +/** \name GPU Begin/end work blocks + * + * Used to explicitly define a per-frame block within which GPU work will happen. + * Used for global autoreleasepool flushing in Metal + * \{ */ + +void GPU_render_begin() +{ + GPUBackend *backend = GPUBackend::get(); + BLI_assert(backend); + backend->render_begin(); +} +void GPU_render_end() +{ + GPUBackend *backend = GPUBackend::get(); + BLI_assert(backend); + backend->render_end(); +} +void GPU_render_step() +{ + GPUBackend *backend = GPUBackend::get(); + BLI_assert(backend); + backend->render_step(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Backend selection * \{ */ static GPUBackend *g_backend; +bool GPU_backend_supported(eGPUBackendType type) +{ + switch (type) { + case GPU_BACKEND_OPENGL: +#ifdef WITH_OPENGL_BACKEND + return true; +#else + return false; +#endif + case GPU_BACKEND_METAL: +#ifdef WITH_METAL_BACKEND + return MTLBackend::metal_is_supported(); +#else + return false; +#endif + default: + BLI_assert(false && "No backend specified"); + return false; + } +} + void GPU_backend_init(eGPUBackendType backend_type) { BLI_assert(g_backend == nullptr); + BLI_assert(GPU_backend_supported(backend_type)); switch (backend_type) { -#if WITH_OPENGL_BACKEND +#ifdef WITH_OPENGL_BACKEND case GPU_BACKEND_OPENGL: g_backend = new GLBackend; break; #endif +#ifdef WITH_METAL_BACKEND + case GPU_BACKEND_METAL: + g_backend = new MTLBackend; + break; +#endif default: BLI_assert(0); break; @@ -172,9 +232,18 @@ void GPU_backend_exit() eGPUBackendType GPU_backend_get_type() { + +#ifdef WITH_OPENGL_BACKEND if (g_backend && dynamic_cast<GLBackend *>(g_backend) != nullptr) { return GPU_BACKEND_OPENGL; } +#endif + +#ifdef WITH_METAL_BACKEND + if (g_backend && dynamic_cast<MTLBackend *>(g_backend) != nullptr) { + return GPU_BACKEND_METAL; + } +#endif return GPU_BACKEND_NONE; } diff --git a/source/blender/gpu/intern/gpu_platform.cc b/source/blender/gpu/intern/gpu_platform.cc index 969a3033c6f..d108dd468a0 100644 --- a/source/blender/gpu/intern/gpu_platform.cc +++ b/source/blender/gpu/intern/gpu_platform.cc @@ -65,6 +65,7 @@ void GPUPlatformGlobal::init(eGPUDeviceType gpu_device, eGPUOSType os_type, eGPUDriverType driver_type, eGPUSupportLevel gpu_support_level, + eGPUBackendType backend, const char *vendor_str, const char *renderer_str, const char *version_str) @@ -83,6 +84,7 @@ void GPUPlatformGlobal::init(eGPUDeviceType gpu_device, this->version = BLI_strdup(version_str); this->support_key = create_key(gpu_support_level, vendor_str, renderer_str, version_str); this->gpu_name = create_gpu_name(vendor_str, renderer_str, version_str); + this->backend = backend; } void GPUPlatformGlobal::clear() @@ -143,8 +145,17 @@ const char *GPU_platform_gpu_name() bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver) { + return GPU_type_matches_ex(device, os, driver, GPU_BACKEND_ANY); +} + +bool GPU_type_matches_ex(eGPUDeviceType device, + eGPUOSType os, + eGPUDriverType driver, + eGPUBackendType backend) +{ BLI_assert(GPG.initialized); - return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver); + return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver) && + (GPG.backend & backend); } /** \} */ diff --git a/source/blender/gpu/intern/gpu_platform_private.hh b/source/blender/gpu/intern/gpu_platform_private.hh index a6c400ad319..6e6c24a8662 100644 --- a/source/blender/gpu/intern/gpu_platform_private.hh +++ b/source/blender/gpu/intern/gpu_platform_private.hh @@ -23,12 +23,14 @@ class GPUPlatformGlobal { char *version = nullptr; char *support_key = nullptr; char *gpu_name = nullptr; + eGPUBackendType backend = GPU_BACKEND_NONE; public: void init(eGPUDeviceType gpu_device, eGPUOSType os_type, eGPUDriverType driver_type, eGPUSupportLevel gpu_support_level, + eGPUBackendType backend, const char *vendor_str, const char *renderer_str, const char *version_str); diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index ac8e98a4a21..b434cfbbb0e 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -88,6 +88,16 @@ static void standard_defines(Vector<const char *> &sources) else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) { sources.append("#define OS_UNIX\n"); } + /* API Definition */ + eGPUBackendType backend = GPU_backend_get_type(); + switch (backend) { + case GPU_BACKEND_OPENGL: + sources.append("#define GPU_OPENGL\n"); + break; + default: + BLI_assert(false && "Invalid GPU Backend Type"); + break; + } if (GPU_crappy_amd_driver()) { sources.append("#define GPU_DEPRECATED_AMD_DRIVER\n"); diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index ec4ff7bc399..aef1984687d 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -62,6 +62,10 @@ void ShaderCreateInfo::finalize() pass_resources_.extend(info.pass_resources_); typedef_sources_.extend_non_duplicates(info.typedef_sources_); + if (info.early_fragment_test_) { + early_fragment_test_ = true; + } + validate(info); auto assert_no_overlap = [&](const bool test, const StringRefNull error) { diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index da63d372669..9984295457c 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -271,6 +271,8 @@ struct ShaderCreateInfo { bool finalized_ = false; /** If true, all resources will have an automatic location assigned. */ bool auto_resource_location_ = false; + /** If true, force depth and stencil tests to always happen before fragment shader invocation. */ + bool early_fragment_test_ = false; /** * Maximum length of all the resource names including each null terminator. * Only for names used by gpu::ShaderInterface. @@ -522,6 +524,16 @@ struct ShaderCreateInfo { } /** + * Force fragment tests before fragment shader invocation. + * IMPORTANT: This is incompatible with using the gl_FragDepth output. + */ + Self &early_fragment_test(bool enable) + { + early_fragment_test_ = enable; + return *(Self *)this; + } + + /** * Only needed if geometry shader is enabled. * IMPORTANT: Input and output instance name will have respectively "_in" and "_out" suffix * appended in the geometry shader IF AND ONLY IF the vertex_out interface instance name matches diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 5b7df035acd..2c6845334d3 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -8,6 +8,7 @@ * shader files. */ +#include <iomanip> #include <iostream> #include "BLI_map.hh" @@ -136,15 +137,14 @@ struct GPUSource { int64_t line_end = input.find("\n", offset); int64_t line_start = input.rfind("\n", offset) + 1; int64_t char_number = offset - line_start + 1; - char line_prefix[16] = ""; - SNPRINTF(line_prefix, "%5ld | ", line_number); /* TODO Use clog. */ std::cout << fullpath << ":" << line_number << ":" << char_number; std::cout << " error: " << message << "\n"; - std::cout << line_prefix << input.substr(line_start, line_end - line_start) << "\n"; + std::cout << std::setw(5) << line_number << " | " + << input.substr(line_start, line_end - line_start) << "\n"; std::cout << " | "; for (int64_t i = 0; i < char_number - 1; i++) { std::cout << " "; @@ -189,7 +189,7 @@ struct GPUSource { { const StringRefNull input = source; std::string output; - int64_t cursor = 0; + int64_t cursor = -1; int64_t last_pos = 0; const bool is_cpp = filename.endswith(".hh"); @@ -204,10 +204,14 @@ struct GPUSource { } while (true) { - cursor = find_keyword(input, "enum ", cursor); + cursor = find_keyword(input, "enum ", cursor + 1); if (cursor == -1) { break; } + /* Skip matches like `typedef enum myEnum myType;` */ + if (cursor >= 8 && input.substr(cursor - 8, 8) == "typedef ") { + continue; + } /* Output anything between 2 enums blocks. */ output += input.substr(last_pos, cursor - last_pos); @@ -361,6 +365,7 @@ void gpu_shader_dependency_init() errors += value->init_dependencies(*g_sources); } BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting"); + UNUSED_VARS_NDEBUG(errors); } void gpu_shader_dependency_exit() diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc index d67dd56b45b..83fc34a3278 100644 --- a/source/blender/gpu/intern/gpu_shader_log.cc +++ b/source/blender/gpu/intern/gpu_shader_log.cc @@ -78,6 +78,9 @@ void Shader::print_log(Span<const char *> sources, } #endif } + if (sources_end_line.size() == 0) { + sources_end_line.append(0); + } char *log_line = log, *line_end; diff --git a/source/blender/gpu/intern/gpu_storage_buffer.cc b/source/blender/gpu/intern/gpu_storage_buffer.cc new file mode 100644 index 00000000000..806acad90fe --- /dev/null +++ b/source/blender/gpu/intern/gpu_storage_buffer.cc @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" +#include <cstring> + +#include "BLI_blenlib.h" +#include "BLI_math_base.h" + +#include "gpu_backend.hh" +#include "gpu_node_graph.h" + +#include "GPU_material.h" +#include "GPU_vertex_buffer.h" /* For GPUUsageType. */ + +#include "GPU_storage_buffer.h" +#include "gpu_storage_buffer_private.hh" + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +namespace blender::gpu { + +StorageBuf::StorageBuf(size_t size, const char *name) +{ + /* Make sure that UBO is padded to size of vec4 */ + BLI_assert((size % 16) == 0); + + size_in_bytes_ = size; + + BLI_strncpy(name_, name, sizeof(name_)); +} + +StorageBuf::~StorageBuf() +{ + MEM_SAFE_FREE(data_); +} + +} // namespace blender::gpu + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +using namespace blender::gpu; + +GPUStorageBuf *GPU_storagebuf_create_ex(size_t size, + const void *data, + GPUUsageType usage, + const char *name) +{ + StorageBuf *ssbo = GPUBackend::get()->storagebuf_alloc(size, usage, name); + /* Direct init. */ + if (data != nullptr) { + ssbo->update(data); + } + return wrap(ssbo); +} + +void GPU_storagebuf_free(GPUStorageBuf *ssbo) +{ + delete unwrap(ssbo); +} + +void GPU_storagebuf_update(GPUStorageBuf *ssbo, const void *data) +{ + unwrap(ssbo)->update(data); +} + +void GPU_storagebuf_bind(GPUStorageBuf *ssbo, int slot) +{ + unwrap(ssbo)->bind(slot); +} + +void GPU_storagebuf_unbind(GPUStorageBuf *ssbo) +{ + unwrap(ssbo)->unbind(); +} + +void GPU_storagebuf_unbind_all() +{ + /* FIXME */ +} + +void GPU_storagebuf_clear(GPUStorageBuf *ssbo, + eGPUTextureFormat internal_format, + eGPUDataFormat data_format, + void *data) +{ + unwrap(ssbo)->clear(internal_format, data_format, data); +} + +void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo) +{ + uint32_t data = 0u; + GPU_storagebuf_clear(ssbo, GPU_R32UI, GPU_DATA_UINT, &data); +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_storage_buffer_private.hh b/source/blender/gpu/intern/gpu_storage_buffer_private.hh new file mode 100644 index 00000000000..06e7c2c0e9d --- /dev/null +++ b/source/blender/gpu/intern/gpu_storage_buffer_private.hh @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_sys_types.h" + +struct GPUStorageBuf; + +namespace blender { +namespace gpu { + +#ifdef DEBUG +# define DEBUG_NAME_LEN 64 +#else +# define DEBUG_NAME_LEN 8 +#endif + +/** + * Implementation of Storage Buffers. + * Base class which is then specialized for each implementation (GL, VK, ...). + */ +class StorageBuf { + protected: + /** Data size in bytes. */ + size_t size_in_bytes_; + /** Continuous memory block to copy to GPU. This data is owned by the StorageBuf. */ + void *data_ = NULL; + /** Debugging name */ + char name_[DEBUG_NAME_LEN]; + + public: + StorageBuf(size_t size, const char *name); + virtual ~StorageBuf(); + + virtual void update(const void *data) = 0; + virtual void bind(int slot) = 0; + virtual void unbind() = 0; + virtual void clear(eGPUTextureFormat internal_format, + eGPUDataFormat data_format, + void *data) = 0; +}; + +/* Syntactic sugar. */ +static inline GPUStorageBuf *wrap(StorageBuf *vert) +{ + return reinterpret_cast<GPUStorageBuf *>(vert); +} +static inline StorageBuf *unwrap(GPUStorageBuf *vert) +{ + return reinterpret_cast<StorageBuf *>(vert); +} +static inline const StorageBuf *unwrap(const GPUStorageBuf *vert) +{ + return reinterpret_cast<const StorageBuf *>(vert); +} + +#undef DEBUG_NAME_LEN + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 9e6a6f75391..d78dc845074 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -136,13 +136,16 @@ bool Texture::init_view(const GPUTexture *src_, int mip_start, int mip_len, int layer_start, - int layer_len) + int layer_len, + bool cube_as_array) { const Texture *src = unwrap(src_); w_ = src->w_; h_ = src->h_; d_ = src->d_; - switch (type_) { + layer_start = min_ii(layer_start, src->layer_count() - 1); + layer_len = min_ii(layer_len, (src->layer_count() - layer_start)); + switch (src->type_) { case GPU_TEXTURE_1D_ARRAY: h_ = layer_len; break; @@ -163,6 +166,10 @@ bool Texture::init_view(const GPUTexture *src_, format_flag_ = to_format_flag(format); /* For now always copy the target. Target aliasing could be exposed later. */ type_ = src->type_; + if (cube_as_array) { + BLI_assert(type_ & GPU_TEXTURE_CUBE); + type_ = (type_ & ~GPU_TEXTURE_CUBE) | GPU_TEXTURE_2D_ARRAY; + } sampler_state = src->sampler_state; return this->init_internal(src_, mip_start, layer_start); } @@ -385,12 +392,13 @@ GPUTexture *GPU_texture_create_view(const char *name, int mip_start, int mip_len, int layer_start, - int layer_len) + int layer_len, + bool cube_as_array) { BLI_assert(mip_len > 0); BLI_assert(layer_len > 0); Texture *view = GPUBackend::get()->texture_alloc(name); - view->init_view(src, format, mip_start, mip_len, layer_start, layer_len); + view->init_view(src, format, mip_start, mip_len, layer_start, layer_len, cube_as_array); return wrap(view); } @@ -548,6 +556,12 @@ void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4]) reinterpret_cast<Texture *>(tex)->swizzle_set(swizzle); } +void GPU_texture_stencil_texture_mode_set(GPUTexture *tex, bool use_stencil) +{ + BLI_assert(GPU_texture_stencil(tex) || !use_stencil); + reinterpret_cast<Texture *>(tex)->stencil_texture_mode_set(use_stencil); +} + void GPU_texture_free(GPUTexture *tex_) { Texture *tex = reinterpret_cast<Texture *>(tex_); @@ -573,13 +587,13 @@ int GPU_texture_dimensions(const GPUTexture *tex_) if (type & GPU_TEXTURE_1D) { return 1; } - else if (type & GPU_TEXTURE_2D) { + if (type & GPU_TEXTURE_2D) { return 2; } - else if (type & GPU_TEXTURE_3D) { + if (type & GPU_TEXTURE_3D) { return 3; } - else if (type & GPU_TEXTURE_CUBE) { + if (type & GPU_TEXTURE_CUBE) { return 2; } /* GPU_TEXTURE_BUFFER */ @@ -596,6 +610,16 @@ int GPU_texture_height(const GPUTexture *tex) return reinterpret_cast<const Texture *>(tex)->height_get(); } +int GPU_texture_layer_count(const GPUTexture *tex) +{ + return reinterpret_cast<const Texture *>(tex)->layer_count(); +} + +int GPU_texture_mip_count(const GPUTexture *tex) +{ + return reinterpret_cast<const Texture *>(tex)->mip_count(); +} + int GPU_texture_orig_width(const GPUTexture *tex) { return reinterpret_cast<const Texture *>(tex)->src_w; diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index ec11fab421c..109e60e19a6 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -111,12 +111,14 @@ class Texture { int mip_start, int mip_len, int layer_start, - int layer_len); + int layer_len, + bool cube_as_array); virtual void generate_mipmap() = 0; virtual void copy_to(Texture *tex) = 0; virtual void clear(eGPUDataFormat format, const void *data) = 0; virtual void swizzle_set(const char swizzle_mask[4]) = 0; + virtual void stencil_texture_mode_set(bool use_stencil) = 0; virtual void mip_range_set(int min, int max) = 0; virtual void *read(int mip, eGPUDataFormat format) = 0; @@ -208,6 +210,11 @@ class Texture { } } + int mip_count() const + { + return mipmaps_; + } + eGPUTextureFormat format_get() const { return format_; diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index b5a572bccbe..7bffafd7f9d 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -505,7 +505,9 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, bool display_colorspace, bool do_overlay_merge) { - if (viewport->color_render_tx == NULL) { + const int view = 0; + + if (viewport->color_render_tx[view] == NULL) { return; } @@ -527,7 +529,7 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, }; gpu_viewport_draw_colormanaged( - viewport, 0, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); + viewport, view, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); /* This one is from the offscreen. Don't free it with the viewport. */ viewport->depth_tx = NULL; diff --git a/source/blender/gpu/metal/mtl_backend.hh b/source/blender/gpu/metal/mtl_backend.hh new file mode 100644 index 00000000000..78f638d23f5 --- /dev/null +++ b/source/blender/gpu/metal/mtl_backend.hh @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_vector.hh" + +#include "gpu_backend.hh" +#include "mtl_capabilities.hh" + +namespace blender { +namespace gpu { + +class Batch; +class DrawList; +class FrameBuffer; +class IndexBuf; +class QueryPool; +class Shader; +class Texture; +class UniformBuf; +class VertBuf; +class MTLContext; + +class MTLBackend : public GPUBackend { + friend class MTLContext; + + public: + /* Capabilities. */ + static MTLCapabilities capabilities; + + inline ~MTLBackend() + { + MTLBackend::platform_exit(); + } + + static bool metal_is_supported(); + inline static MTLBackend *get() + { + return static_cast<MTLBackend *>(GPUBackend::get()); + } + + void samplers_update() override; + inline void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override + { + /* Placeholder */ + } + + /* MTL Allocators need to be implemented in separate .mm files, due to allocation of Objective-C + * objects. */ + Context *context_alloc(void *ghost_window) override; + Batch *batch_alloc() override; + DrawList *drawlist_alloc(int list_length) override; + FrameBuffer *framebuffer_alloc(const char *name) override; + IndexBuf *indexbuf_alloc() override; + QueryPool *querypool_alloc() override; + Shader *shader_alloc(const char *name) override; + Texture *texture_alloc(const char *name) override; + UniformBuf *uniformbuf_alloc(int size, const char *name) override; + VertBuf *vertbuf_alloc() override; + + /* Render Frame Coordination. */ + void render_begin() override; + void render_end() override; + void render_step() override; + bool is_inside_render_boundary(); + + private: + static void platform_init(MTLContext *ctx); + static void platform_exit(); + + static void capabilities_init(MTLContext *ctx); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm new file mode 100644 index 00000000000..e1da371bd0b --- /dev/null +++ b/source/blender/gpu/metal/mtl_backend.mm @@ -0,0 +1,408 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "gpu_backend.hh" +#include "mtl_backend.hh" + +#include "gpu_capabilities_private.hh" +#include "gpu_platform_private.hh" + +#include <Cocoa/Cocoa.h> +#include <Metal/Metal.h> +#include <QuartzCore/QuartzCore.h> + +namespace blender { +namespace gpu { + +/* Global per-thread AutoReleasePool. */ +thread_local NSAutoreleasePool *g_autoreleasepool = nil; +thread_local int g_autoreleasepool_depth = 0; + +/* -------------------------------------------------------------------- */ +/** \name Metal Backend + * \{ */ + +void MTLBackend::samplers_update(){ + /* Placeholder -- Handled in MTLContext. */ +}; + +Context *MTLBackend::context_alloc(void *ghost_window) +{ + /* TODO(Metal): Implement MTLContext. */ + return nullptr; +}; + +Batch *MTLBackend::batch_alloc() +{ + /* TODO(Metal): Implement MTLBatch. */ + return nullptr; +}; + +DrawList *MTLBackend::drawlist_alloc(int list_length) +{ + /* TODO(Metal): Implement MTLDrawList. */ + return nullptr; +}; + +FrameBuffer *MTLBackend::framebuffer_alloc(const char *name) +{ + /* TODO(Metal): Implement MTLFrameBuffer. */ + return nullptr; +}; + +IndexBuf *MTLBackend::indexbuf_alloc() +{ + /* TODO(Metal): Implement MTLIndexBuf. */ + return nullptr; +}; + +QueryPool *MTLBackend::querypool_alloc() +{ + /* TODO(Metal): Implement MTLQueryPool. */ + return nullptr; +}; + +Shader *MTLBackend::shader_alloc(const char *name) +{ + /* TODO(Metal): Implement MTLShader. */ + return nullptr; +}; + +Texture *MTLBackend::texture_alloc(const char *name) +{ + /* TODO(Metal): Implement MTLTexture. */ + return nullptr; +} + +UniformBuf *MTLBackend::uniformbuf_alloc(int size, const char *name) +{ + /* TODO(Metal): Implement MTLUniformBuf. */ + return nullptr; +}; + +VertBuf *MTLBackend::vertbuf_alloc() +{ + /* TODO(Metal): Implement MTLVertBuf. */ + return nullptr; +} + +void MTLBackend::render_begin() +{ + /* All Rendering must occur within a render boundary */ + /* Track a call-count for nested calls, used to ensure we are inside an + * autoreleasepool from all rendering path. */ + BLI_assert(g_autoreleasepool_depth >= 0); + + if (g_autoreleasepool == nil) { + g_autoreleasepool = [[NSAutoreleasePool alloc] init]; + } + g_autoreleasepool_depth++; + BLI_assert(g_autoreleasepool_depth > 0); +} + +void MTLBackend::render_end() +{ + /* If call-count reaches zero, drain auto release pool. + * Ensures temporary objects are freed within a frame's lifetime. */ + BLI_assert(g_autoreleasepool != nil); + g_autoreleasepool_depth--; + BLI_assert(g_autoreleasepool_depth >= 0); + + if (g_autoreleasepool_depth == 0) { + [g_autoreleasepool drain]; + g_autoreleasepool = nil; + } +} + +void MTLBackend::render_step() +{ + /* Placeholder */ +} + +bool MTLBackend::is_inside_render_boundary() +{ + return (g_autoreleasepool != nil); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Platform + * \{ */ + +/* For Metal, platform_init needs to be called after MTLContext initialization. */ +void MTLBackend::platform_init(MTLContext *ctx) +{ + if (GPG.initialized) { + return; + } + + eGPUDeviceType device = GPU_DEVICE_UNKNOWN; + eGPUOSType os = GPU_OS_ANY; + eGPUDriverType driver = GPU_DRIVER_ANY; + eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED; + + BLI_assert(ctx); + id<MTLDevice> mtl_device = nil; /*ctx->device; TODO(Metal): Implement MTLContext. */ + BLI_assert(device); + + NSString *gpu_name = [mtl_device name]; + const char *vendor = [gpu_name UTF8String]; + const char *renderer = "Metal API"; + const char *version = "1.2"; + printf("METAL API - DETECTED GPU: %s\n", vendor); + + /* macOS is the only supported platform, but check to ensure we are not building with Metal + * enablement on another platform. */ +#ifdef _WIN32 + os = GPU_OS_WIN; +#elif defined(__APPLE__) + os = GPU_OS_MAC; +#else + os = GPU_OS_UNIX; +#endif + + BLI_assert(os == GPU_OS_MAC && "Platform must be macOS"); + + /* Determine Vendor from name. */ + if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) { + device = GPU_DEVICE_ATI; + driver = GPU_DRIVER_OFFICIAL; + } + else if (strstr(vendor, "NVIDIA")) { + device = GPU_DEVICE_NVIDIA; + driver = GPU_DRIVER_OFFICIAL; + } + else if (strstr(vendor, "Intel")) { + device = GPU_DEVICE_INTEL; + driver = GPU_DRIVER_OFFICIAL; + } + else if (strstr(vendor, "Apple") || strstr(vendor, "APPLE")) { + /* Apple Silicon. */ + device = GPU_DEVICE_APPLE; + driver = GPU_DRIVER_OFFICIAL; + } + else if (strstr(renderer, "Apple Software Renderer")) { + device = GPU_DEVICE_SOFTWARE; + driver = GPU_DRIVER_SOFTWARE; + } + else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) { + device = GPU_DEVICE_SOFTWARE; + driver = GPU_DRIVER_SOFTWARE; + } + else { + printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n"); + printf("Detected configuration:\n"); + printf("Vendor: %s\n", vendor); + printf("Renderer: %s\n", renderer); + } + + GPG.init(device, os, driver, support_level, GPU_BACKEND_METAL, vendor, renderer, version); +} + +void MTLBackend::platform_exit() +{ + BLI_assert(GPG.initialized); + GPG.clear(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Capabilities + * \{ */ +MTLCapabilities MTLBackend::capabilities = {}; + +static const char *mtl_extensions_get_null(int i) +{ + return nullptr; +} + +bool supports_barycentric_whitelist(id<MTLDevice> device) +{ + NSString *gpu_name = [device name]; + BLI_assert([gpu_name length]); + const char *vendor = [gpu_name UTF8String]; + + /* Verify GPU support. */ + bool supported_gpu = [device supportsFamily:MTLGPUFamilyMac2]; + bool should_support_barycentrics = false; + + /* Known good configs. */ + if (strstr(vendor, "AMD") || strstr(vendor, "Apple") || strstr(vendor, "APPLE")) { + should_support_barycentrics = true; + } + + /* Explicit support for Intel-based platforms. */ + if ((strstr(vendor, "Intel") || strstr(vendor, "INTEL"))) { + should_support_barycentrics = true; + } + return supported_gpu && should_support_barycentrics; +} + +bool MTLBackend::metal_is_supported() +{ + /* Device compatibility information using Metal Feature-set tables. + * See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ + + NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; + + /* Metal Viewport requires macOS Version 10.15 onwards. */ + bool supported_os_version = version.majorVersion >= 11 || + (version.majorVersion == 10 ? version.minorVersion >= 15 : false); + if (!supported_os_version) { + printf( + "OS Version too low to run minimum required metal version. Required at least 10.15, got " + "%ld.%ld \n", + (long)version.majorVersion, + (long)version.minorVersion); + return false; + } + + if (@available(macOS 10.15, *)) { + id<MTLDevice> device = MTLCreateSystemDefaultDevice(); + + /* Debug: Enable low power GPU with Environment Var: METAL_FORCE_INTEL. */ + static const char *forceIntelStr = getenv("METAL_FORCE_INTEL"); + bool forceIntel = forceIntelStr ? (atoi(forceIntelStr) != 0) : false; + + if (forceIntel) { + NSArray<id<MTLDevice>> *allDevices = MTLCopyAllDevices(); + for (id<MTLDevice> _device in allDevices) { + if (_device.lowPower) { + device = _device; + } + } + } + + /* Metal Viewport requires argument buffer tier-2 support and Barycentric Coordinates. + * These are available on most hardware configurations supporting Metal 2.2. */ + bool supports_argument_buffers_tier2 = ([device argumentBuffersSupport] == + MTLArgumentBuffersTier2); + bool supports_barycentrics = [device supportsShaderBarycentricCoordinates] || + supports_barycentric_whitelist(device); + bool supported_metal_version = [device supportsFamily:MTLGPUFamilyMac2]; + + bool result = supports_argument_buffers_tier2 && supports_barycentrics && + supported_os_version && supported_metal_version; + + if (!supports_argument_buffers_tier2) { + printf("[Metal] Device does not support argument buffers tier 2\n"); + } + if (!supports_barycentrics) { + printf("[Metal] Device does not support barycentrics coordinates\n"); + } + if (!supported_metal_version) { + printf("[Metal] Device does not support metal 2.2 or higher\n"); + } + + if (result) { + printf("Device with name %s supports metal minimum requirements\n", + [[device name] UTF8String]); + } + + return result; + } + return false; +} + +void MTLBackend::capabilities_init(MTLContext *ctx) +{ + BLI_assert(ctx); + id<MTLDevice> device = nil; /*ctx->device TODO(Metal): Implement MTLContext. */ + BLI_assert(device); + + /* Initialize Capabilities. */ + MTLBackend::capabilities.supports_argument_buffers_tier2 = ([device argumentBuffersSupport] == + MTLArgumentBuffersTier2); + MTLBackend::capabilities.supports_family_mac1 = [device supportsFamily:MTLGPUFamilyMac1]; + MTLBackend::capabilities.supports_family_mac2 = [device supportsFamily:MTLGPUFamilyMac2]; + MTLBackend::capabilities.supports_family_mac_catalyst1 = [device + supportsFamily:MTLGPUFamilyMacCatalyst1]; + MTLBackend::capabilities.supports_family_mac_catalyst2 = [device + supportsFamily:MTLGPUFamilyMacCatalyst2]; + + /* Common Global Capabilities. */ + GCaps.max_texture_size = ([device supportsFamily:MTLGPUFamilyApple3] || + MTLBackend::capabilities.supports_family_mac1) ? + 16384 : + 8192; + GCaps.max_texture_3d_size = 2048; + GCaps.max_texture_layers = 2048; + GCaps.max_textures = (MTLBackend::capabilities.supports_family_mac1) ? + 128 : + (([device supportsFamily:MTLGPUFamilyApple4]) ? 96 : 31); + if (GCaps.max_textures <= 32) { + BLI_assert(false); + } + GCaps.max_samplers = (MTLBackend::capabilities.supports_argument_buffers_tier2) ? 1024 : 16; + + GCaps.max_textures_vert = GCaps.max_textures; + GCaps.max_textures_geom = 0; /* N/A geometry shaders not supported. */ + GCaps.max_textures_frag = GCaps.max_textures; + + /* Conservative uniform data limit is 4KB per-stage -- This is the limit of setBytes. + * MTLBuffer path is also supported but not as efficient. */ + GCaps.max_uniforms_vert = 1024; + GCaps.max_uniforms_frag = 1024; + + GCaps.max_batch_indices = 1 << 31; + GCaps.max_batch_vertices = 1 << 31; + GCaps.max_vertex_attribs = 31; + GCaps.max_varying_floats = 60; + + /* Feature support */ + GCaps.mem_stats_support = false; + GCaps.shader_image_load_store_support = ([device supportsFamily:MTLGPUFamilyApple3] || + MTLBackend::capabilities.supports_family_mac1 || + MTLBackend::capabilities.supports_family_mac2); + GCaps.compute_shader_support = false; /* TODO(Metal): Add compute support. */ + GCaps.shader_storage_buffer_objects_support = + false; /* TODO(Metal): implement Storage Buffer support.*/ + + /* Maximum buffer bindings: 31. Consider required slot for uniforms/UBOs/Vertex attributes. + * Can use argument buffers if a higher limit is required. */ + GCaps.max_shader_storage_buffer_bindings = 24; + + if (GCaps.compute_shader_support) { + GCaps.max_work_group_count[0] = 65535; + GCaps.max_work_group_count[1] = 65535; + GCaps.max_work_group_count[2] = 65535; + + /* In Metal, total_thread_count is 512 or 1024, such that + * threadgroup `width*height*depth <= total_thread_count` */ + unsigned int max_threads_per_threadgroup_per_dim = + ([device supportsFamily:MTLGPUFamilyApple4] || + MTLBackend::capabilities.supports_family_mac1) ? + 1024 : + 512; + GCaps.max_work_group_size[0] = max_threads_per_threadgroup_per_dim; + GCaps.max_work_group_size[1] = max_threads_per_threadgroup_per_dim; + GCaps.max_work_group_size[2] = max_threads_per_threadgroup_per_dim; + } + + GCaps.transform_feedback_support = true; + + /* OPENGL Related workarounds -- none needed for Metal. */ + GCaps.extensions_len = 0; + GCaps.extension_get = mtl_extensions_get_null; + GCaps.mip_render_workaround = false; + GCaps.depth_blitting_workaround = false; + GCaps.use_main_context_workaround = false; + GCaps.broken_amd_driver = false; + + /* Metal related workarounds. */ + /* Minimum per-vertex stride is 4 bytes in Metal. + * A bound vertex buffer must contribute at least 4 bytes per vertex. */ + GCaps.minimum_per_vertex_stride = 4; +} + +/** \} */ + +} // gpu +} // blender diff --git a/source/blender/gpu/metal/mtl_capabilities.hh b/source/blender/gpu/metal/mtl_capabilities.hh new file mode 100644 index 00000000000..5563008e87d --- /dev/null +++ b/source/blender/gpu/metal/mtl_capabilities.hh @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +namespace blender { +namespace gpu { + +/*** Derived from: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf ***/ +/** Upper Bound/Fixed Limits **/ + +#define METAL_MAX_TEXTURE_SLOTS 128 +#define METAL_MAX_SAMPLER_SLOTS METAL_MAX_TEXTURE_SLOTS +#define METAL_MAX_UNIFORM_BUFFER_BINDINGS 31 +#define METAL_MAX_VERTEX_INPUT_ATTRIBUTES 31 +#define METAL_MAX_UNIFORMS_PER_BLOCK 64 + +/* Context-specific limits -- populated in 'MTLBackend::platform_init' */ +typedef struct MTLCapabilities { + + /* Variable Limits & feature sets. */ + int max_color_render_targets = 4; /* Minimum = 4 */ + int buffer_alignment_for_textures = 256; /* Upper bound = 256 bytes */ + int minimum_buffer_offset_alignment = 256; /* Upper bound = 256 bytes */ + + /* Capabilities */ + bool supports_vertex_amplification = false; + bool supports_texture_swizzle = true; + bool supports_cubemaps = true; + bool supports_layered_rendering = true; + bool supports_memory_barriers = false; + bool supports_sampler_border_color = false; + bool supports_argument_buffers_tier2 = false; + + /* GPU Family */ + bool supports_family_mac1 = false; + bool supports_family_mac2 = false; + bool supports_family_mac_catalyst1 = false; + bool supports_family_mac_catalyst2 = false; + +} MTLCapabilities; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index f0d7a23ef82..2c9cbdb99d8 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -140,7 +140,7 @@ void GLBackend::platform_init() } } - GPG.init(device, os, driver, support_level, vendor, renderer, version); + GPG.init(device, os, driver, support_level, GPU_BACKEND_OPENGL, vendor, renderer, version); } void GLBackend::platform_exit() @@ -418,6 +418,12 @@ static void detect_workarounds() (strstr(renderer, "HD Graphics 4400") || strstr(renderer, "HD Graphics 4600"))) { GCaps.shader_storage_buffer_objects_support = false; } + + /* Metal-related Workarounds. */ + + /* Minimum Per-Vertex stride is 1 byte for OpenGL. */ + GCaps.minimum_per_vertex_stride = 1; + } // namespace blender::gpu /** Internal capabilities. */ @@ -426,6 +432,8 @@ GLint GLContext::max_cubemap_size = 0; GLint GLContext::max_texture_3d_size = 0; GLint GLContext::max_ubo_binds = 0; GLint GLContext::max_ubo_size = 0; +GLint GLContext::max_ssbo_binds = 0; +GLint GLContext::max_ssbo_size = 0; /** Extensions. */ @@ -442,6 +450,7 @@ bool GLContext::native_barycentric_support = false; bool GLContext::multi_bind_support = false; bool GLContext::multi_draw_indirect_support = false; bool GLContext::shader_draw_parameters_support = false; +bool GLContext::stencil_texturing_support = false; bool GLContext::texture_cube_map_array_support = false; bool GLContext::texture_filter_anisotropic_support = false; bool GLContext::texture_gather_support = false; @@ -485,13 +494,17 @@ void GLBackend::capabilities_init() glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]); glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]); glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); + glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, + &GCaps.max_shader_storage_buffer_bindings); } GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size); glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size); glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GLContext::max_ubo_binds); + glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &GLContext::max_ssbo_binds); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &GLContext::max_ubo_size); + glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &GLContext::max_ssbo_size); GLContext::base_instance_support = GLEW_ARB_base_instance; GLContext::clear_texture_support = GLEW_ARB_clear_texture; GLContext::copy_image_support = GLEW_ARB_copy_image; @@ -505,6 +518,7 @@ void GLBackend::capabilities_init() GLContext::multi_bind_support = GLEW_ARB_multi_bind; GLContext::multi_draw_indirect_support = GLEW_ARB_multi_draw_indirect; GLContext::shader_draw_parameters_support = GLEW_ARB_shader_draw_parameters; + GLContext::stencil_texturing_support = GLEW_VERSION_4_3; GLContext::texture_cube_map_array_support = GLEW_ARB_texture_cube_map_array; GLContext::texture_filter_anisotropic_support = GLEW_EXT_texture_filter_anisotropic; GLContext::texture_gather_support = GLEW_ARB_texture_gather; diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 9d637d7b6af..29249111294 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -19,6 +19,7 @@ #include "gl_index_buffer.hh" #include "gl_query.hh" #include "gl_shader.hh" +#include "gl_storage_buffer.hh" #include "gl_texture.hh" #include "gl_uniform_buffer.hh" #include "gl_vertex_buffer.hh" @@ -101,6 +102,11 @@ class GLBackend : public GPUBackend { return new GLUniformBuf(size, name); }; + StorageBuf *storagebuf_alloc(int size, GPUUsageType usage, const char *name) override + { + return new GLStorageBuf(size, usage, name); + }; + VertBuf *vertbuf_alloc() override { return new GLVertBuf(); @@ -117,6 +123,24 @@ class GLBackend : public GPUBackend { GLCompute::dispatch(groups_x_len, groups_y_len, groups_z_len); } + void compute_dispatch_indirect(StorageBuf *indirect_buf) override + { + GLContext::get()->state_manager_active_get()->apply_state(); + + dynamic_cast<GLStorageBuf *>(indirect_buf)->bind_as(GL_DISPATCH_INDIRECT_BUFFER); + /* This barrier needs to be here as it only work on the currently bound indirect buffer. */ + glMemoryBarrier(GL_DRAW_INDIRECT_BUFFER); + + glDispatchComputeIndirect((GLintptr)0); + /* Unbind. */ + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); + } + + /* Render Frame Coordination */ + void render_begin(void) override{}; + void render_end(void) override{}; + void render_step(void) override{}; + private: static void platform_init(); static void platform_exit(); diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc index 1913174eaef..2fbf23c227d 100644 --- a/source/blender/gpu/opengl/gl_compute.cc +++ b/source/blender/gpu/opengl/gl_compute.cc @@ -16,8 +16,6 @@ void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len) { GL_CHECK_RESOURCES("Compute"); - GLContext::get()->state_manager->apply_state(); - glDispatchCompute(group_x_len, group_y_len, group_z_len); } diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index 369b667cbcc..c333c8a4afd 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -45,6 +45,8 @@ class GLContext : public Context { static GLint max_texture_3d_size; static GLint max_ubo_size; static GLint max_ubo_binds; + static GLint max_ssbo_size; + static GLint max_ssbo_binds; /** Extensions. */ @@ -61,6 +63,7 @@ class GLContext : public Context { static bool multi_bind_support; static bool multi_draw_indirect_support; static bool shader_draw_parameters_support; + static bool stencil_texturing_support; static bool texture_cube_map_array_support; static bool texture_filter_anisotropic_support; static bool texture_gather_support; diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 256702b60c5..5a55a2e8020 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -24,6 +24,8 @@ using namespace blender; using namespace blender::gpu; using namespace blender::gpu::shader; +extern "C" char datatoc_glsl_shader_defines_glsl[]; + /* -------------------------------------------------------------------- */ /** \name Creation / Destruction * \{ */ @@ -174,6 +176,10 @@ static const char *to_string(const eGPUTextureFormat &type) return "r16f"; case GPU_R16: return "r16"; + case GPU_R11F_G11F_B10F: + return "r11f_g11f_b10f"; + case GPU_RGB10_A2: + return "rgb10_a2"; default: return "unknown"; } @@ -576,6 +582,9 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n"; } } + if (info.early_fragment_test_) { + ss << "layout(early_fragment_tests) in;\n"; + } ss << "\n/* Outputs. */\n"; for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) { ss << "layout(location = " << output.index; @@ -667,7 +676,7 @@ std::string GLShader::compute_layout_declare(const ShaderCreateInfo &info) const ss << ", local_size_y = " << info.compute_layout_.local_size_y; } if (info.compute_layout_.local_size_z != -1) { - ss << ", local_size_y = " << info.compute_layout_.local_size_z; + ss << ", local_size_z = " << info.compute_layout_.local_size_z; } ss << ") in;\n"; ss << "\n"; @@ -753,7 +762,7 @@ bool GLShader::do_geometry_shader_injection(const shader::ShaderCreateInfo *info static char *glsl_patch_default_get() { /** Used for shader patching. Init once. */ - static char patch[1024] = "\0"; + static char patch[2048] = "\0"; if (patch[0] != '\0') { return patch; } @@ -820,6 +829,9 @@ static char *glsl_patch_default_get() STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", GLContext::derivative_signs[0]); STR_CONCATF(patch, slen, "#define DFDY_SIGN %1.1f\n", GLContext::derivative_signs[1]); + /* GLSL Backend Lib. */ + STR_CONCAT(patch, slen, datatoc_glsl_shader_defines_glsl); + BLI_assert(slen < sizeof(patch)); return patch; } @@ -836,6 +848,10 @@ static char *glsl_patch_compute_get() /* Version need to go first. */ STR_CONCAT(patch, slen, "#version 430\n"); STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n"); + + /* Array compat. */ + STR_CONCAT(patch, slen, "#define array(_type) _type[]\n"); + BLI_assert(slen < sizeof(patch)); return patch; } diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index 86281d231a8..9c21d0c6230 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -72,6 +72,11 @@ class GLShader : public Shader { /** DEPRECATED: Kept only because of BGL API. */ int program_handle_get() const override; + bool is_compute() const + { + return compute_shader_ != 0; + } + private: char *glsl_patch_get(GLenum gl_stage); diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc new file mode 100644 index 00000000000..109bb65fcb7 --- /dev/null +++ b/source/blender/gpu/opengl/gl_storage_buffer.cc @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "gpu_backend.hh" +#include "gpu_context_private.hh" + +#include "gl_backend.hh" +#include "gl_debug.hh" +#include "gl_storage_buffer.hh" +#include "gl_vertex_buffer.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLStorageBuf::GLStorageBuf(size_t size, GPUUsageType usage, const char *name) + : StorageBuf(size, name) +{ + usage_ = usage; + /* Do not create UBO GL buffer here to allow allocation from any thread. */ + BLI_assert(size <= GLContext::max_ssbo_size); +} + +GLStorageBuf::~GLStorageBuf() +{ + GLContext::buf_free(ssbo_id_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data upload / update + * \{ */ + +void GLStorageBuf::init() +{ + BLI_assert(GLContext::get()); + + glGenBuffers(1, &ssbo_id_); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_); + glBufferData(GL_SHADER_STORAGE_BUFFER, size_in_bytes_, nullptr, to_gl(this->usage_)); + + debug::object_label(GL_SHADER_STORAGE_BUFFER, ssbo_id_, name_); +} + +void GLStorageBuf::update(const void *data) +{ + if (ssbo_id_ == 0) { + this->init(); + } + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, size_in_bytes_, data); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Usage + * \{ */ + +void GLStorageBuf::bind(int slot) +{ + if (slot >= GLContext::max_ssbo_binds) { + fprintf( + stderr, + "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.", + name_, + slot, + GLContext::max_ssbo_binds); + return; + } + + if (ssbo_id_ == 0) { + this->init(); + } + + if (data_ != nullptr) { + this->update(data_); + MEM_SAFE_FREE(data_); + } + + slot_ = slot; + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot_, ssbo_id_); + +#ifdef DEBUG + BLI_assert(slot < 16); + /* TODO */ + // GLContext::get()->bound_ssbo_slots |= 1 << slot; +#endif +} + +void GLStorageBuf::bind_as(GLenum target) +{ + BLI_assert_msg(ssbo_id_ != 0, + "Trying to use storage buf as indirect buffer but buffer was never filled."); + glBindBuffer(target, ssbo_id_); +} + +void GLStorageBuf::unbind() +{ +#ifdef DEBUG + /* NOTE: This only unbinds the last bound slot. */ + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot_, 0); + /* Hope that the context did not change. */ + /* TODO */ + // GLContext::get()->bound_ssbo_slots &= ~(1 << slot_); +#endif + slot_ = 0; +} + +void GLStorageBuf::clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) +{ + if (ssbo_id_ == 0) { + this->init(); + } + + if (GLContext::direct_state_access_support) { + glClearNamedBufferData(ssbo_id_, + to_gl_internal_format(internal_format), + to_gl_data_format(internal_format), + to_gl(data_format), + data); + } + else { + /* WATCH(@fclem): This should be ok since we only use clear outside of drawing functions. */ + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_); + glClearBufferData(GL_SHADER_STORAGE_BUFFER, + to_gl_internal_format(internal_format), + to_gl_data_format(internal_format), + to_gl(data_format), + data); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } +} + +/** \} */ + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_storage_buffer.hh b/source/blender/gpu/opengl/gl_storage_buffer.hh new file mode 100644 index 00000000000..c808a0bdda1 --- /dev/null +++ b/source/blender/gpu/opengl/gl_storage_buffer.hh @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_storage_buffer_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +/** + * Implementation of Storage Buffers using OpenGL. + */ +class GLStorageBuf : public StorageBuf { + private: + /** Slot to which this UBO is currently bound. -1 if not bound. */ + int slot_ = -1; + /** OpenGL Object handle. */ + GLuint ssbo_id_ = 0; + /** Usage type. */ + GPUUsageType usage_; + + public: + GLStorageBuf(size_t size, GPUUsageType usage, const char *name); + ~GLStorageBuf(); + + void update(const void *data) override; + void bind(int slot) override; + void unbind() override; + void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) override; + + /* Special internal function to bind SSBOs to indirect argument targets. */ + void bind_as(GLenum target); + + private: + void init(); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLStorageBuf"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index 0a5c7f8e79e..b78e30e8b4c 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -453,6 +453,19 @@ void GLTexture::swizzle_set(const char swizzle[4]) } } +void GLTexture::stencil_texture_mode_set(bool use_stencil) +{ + BLI_assert(GLContext::stencil_texturing_support); + GLint value = use_stencil ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT; + if (GLContext::direct_state_access_support) { + glTextureParameteri(tex_id_, GL_DEPTH_STENCIL_TEXTURE_MODE, value); + } + else { + GLContext::state_manager_active_get()->texture_bind_temp(this); + glTexParameteri(target_, GL_DEPTH_STENCIL_TEXTURE_MODE, value); + } +} + void GLTexture::mip_range_set(int min, int max) { BLI_assert(min <= max && min >= 0 && max <= mipmaps_); @@ -556,7 +569,7 @@ void GLTexture::samplers_update() float max_anisotropy = 1.0f; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); - float aniso_filter = max_ff(max_anisotropy, U.anisotropic_filter); + float aniso_filter = min_ff(max_anisotropy, U.anisotropic_filter); for (int i = 0; i <= GPU_SAMPLER_ICON - 1; i++) { eGPUSamplerState state = static_cast<eGPUSamplerState>(i); @@ -688,6 +701,11 @@ void GLTexture::check_feedback_loop() if (GPU_mip_render_workaround()) { return; } + /* Do not check if using compute shader. */ + GLShader *sh = dynamic_cast<GLShader *>(Context::get()->shader); + if (sh && sh->is_compute()) { + return; + } GLFrameBuffer *fb = static_cast<GLFrameBuffer *>(GLContext::get()->active_fb); for (int i = 0; i < ARRAY_SIZE(fb_); i++) { if (fb_[i] == fb) { diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index d4d024f5e3e..2dde8d6c86b 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -57,6 +57,7 @@ class GLTexture : public Texture { void copy_to(Texture *dst) override; void clear(eGPUDataFormat format, const void *data) override; void swizzle_set(const char swizzle_mask[4]) override; + void stencil_texture_mode_set(bool use_stencil) override; void mip_range_set(int min, int max) override; void *read(int mip, eGPUDataFormat type) override; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl index 9851e08fe2e..353bf1481da 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl @@ -23,15 +23,15 @@ void main() /* Use pos to select the right swizzle (instead of gl_VertexID) * in order to workaround an OSX driver bug. */ - if (pos == vec2(0.0, 0.0)) { + if (all(equal(pos, vec2(0.0, 0.0)))) { rect.xy = rect.xz; tex.xy = tex.xz; } - else if (pos == vec2(0.0, 1.0)) { + else if (all(equal(pos, vec2(0.0, 1.0)))) { rect.xy = rect.xw; tex.xy = tex.xw; } - else if (pos == vec2(1.0, 1.0)) { + else if (all(equal(pos, vec2(1.0, 1.0)))) { rect.xy = rect.yw; tex.xy = tex.yw; } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl index d9a5aeeef46..903c602c5d6 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl @@ -15,6 +15,32 @@ void main() { vec2 uv; vec2 co; + +#ifdef GPU_METAL +/* Metal API does not support Triangle fan primitive topology. + * When this shader is called using Triangle-Strip, vertex ID's + * are in a different order. */ +# define GPU_PRIM_TRI_STRIP +#endif + +#ifdef GPU_PRIM_TRI_STRIP + if (gl_VertexID == 0) { + co = rect_geom.xw; + uv = rect_icon.xw; + } + else if (gl_VertexID == 1) { + co = rect_geom.xy; + uv = rect_icon.xy; + } + else if (gl_VertexID == 2) { + co = rect_geom.zw; + uv = rect_icon.zw; + } + else { + co = rect_geom.zy; + uv = rect_icon.zy; + } +#else if (gl_VertexID == 0) { co = rect_geom.xy; uv = rect_icon.xy; @@ -31,6 +57,7 @@ void main() co = rect_geom.zy; uv = rect_icon.zy; } +#endif gl_Position = ModelViewProjectionMatrix * vec4(co, 0.0f, 1.0f); texCoord_interp = uv; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl index 80b93baf20a..3a39cd8b847 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl @@ -53,7 +53,7 @@ flat out float lineWidth; noperspective out float butCo; flat out float discardFac; -# ifdef OS_MAC +# if defined(OS_MAC) && defined(GPU_OPENGL) in float dummy; # endif #endif diff --git a/source/blender/gpu/shaders/gpu_shader_depth_only_frag.glsl b/source/blender/gpu/shaders/gpu_shader_depth_only_frag.glsl index 1b9fd48c77a..59efdd8d538 100644 --- a/source/blender/gpu/shaders/gpu_shader_depth_only_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_depth_only_frag.glsl @@ -1,6 +1,6 @@ void main() { - // no color output, only depth (line below is implicit) + /* No color output, only depth (line below is implicit). */ // gl_FragDepth = gl_FragCoord.z; } diff --git a/source/blender/gpu/shaders/gpu_shader_image_overlays_merge_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_overlays_merge_frag.glsl index 2314dbbc5d5..d77f073b7de 100644 --- a/source/blender/gpu/shaders/gpu_shader_image_overlays_merge_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_image_overlays_merge_frag.glsl @@ -32,8 +32,8 @@ void linearrgb_to_srgb(vec4 col_from, out vec4 col_to) void main() { - fragColor = texture(image_texture, texCoord_interp.st); - vec4 overlay_col = texture(overlays_texture, texCoord_interp.st); + fragColor = texture(image_texture, texCoord_interp.xy); + vec4 overlay_col = texture(overlays_texture, texCoord_interp.xy); if (overlay) { fragColor = clamp(fragColor, 0.0, 1.0); diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl index 4ef3ff1a8d0..617c02ac079 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl @@ -43,7 +43,7 @@ bool test(int bit) vec2 line_thresholds(float width) { - return vec2(max(0, width - line_falloff), width); + return vec2(max(0.0, width - line_falloff), width); } void main() diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl index c339d3cbabb..f958a81b1eb 100644 --- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl @@ -52,7 +52,7 @@ bool is_inside_box(ivec2 v) float texture_1D_custom_bilinear_filter(vec2 uv) { - vec2 texel_2d = uv * glyph_dim + 0.5; + vec2 texel_2d = uv * vec2(glyph_dim) + vec2(0.5); ivec2 texel_2d_near = ivec2(texel_2d) - 1; int frag_offset = glyph_offset + texel_2d_near.y * glyph_dim.x + texel_2d_near.x; @@ -100,7 +100,7 @@ void main() fragColor.a = texture_1D_custom_bilinear_filter(texCoord_interp); } else { - vec2 texel = 1.0 / glyph_dim; + vec2 texel = 1.0 / vec2(glyph_dim); fragColor.a = 0.0; if (interp_size == 1) { diff --git a/source/blender/gpu/shaders/gpu_shader_text_vert.glsl b/source/blender/gpu/shaders/gpu_shader_text_vert.glsl index 5b01fea5266..63d73733c2f 100644 --- a/source/blender/gpu/shaders/gpu_shader_text_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_text_vert.glsl @@ -28,8 +28,9 @@ void main() vec2 interp_offset = float(interp_size) / abs(pos.zw - pos.xy); texCoord_interp = mix(-interp_offset, 1.0 + interp_offset, quad); - vec2 final_pos = mix( - pos.xy + ivec2(-interp_size, interp_size), pos.zw + ivec2(interp_size, -interp_size), quad); + vec2 final_pos = mix(vec2(ivec2(pos.xy) + ivec2(-interp_size, interp_size)), + vec2(ivec2(pos.zw) + ivec2(interp_size, -interp_size)), + quad); gl_Position = ModelViewProjectionMatrix * vec4(final_pos, 0.0, 1.0); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_light_falloff.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_light_falloff.glsl index f3eae653f95..92762db5ff4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_light_falloff.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_light_falloff.glsl @@ -1,7 +1,10 @@ -void node_light_falloff( - float strength, float tsmooth, out float quadratic, out float linear, out float constant) +void node_light_falloff(float strength, + float tsmooth, + out float quadratic, + out float linear, + out float falloff_constant) { quadratic = strength; linear = strength; - constant = strength; + falloff_constant = strength; } diff --git a/source/blender/gpu/shaders/opengl/glsl_shader_defines.glsl b/source/blender/gpu/shaders/opengl/glsl_shader_defines.glsl new file mode 100644 index 00000000000..a5fce2e71c3 --- /dev/null +++ b/source/blender/gpu/shaders/opengl/glsl_shader_defines.glsl @@ -0,0 +1,17 @@ +/* Backend Functions. */ +#define select(A, B, mask) mix(A, B, mask) + +bool is_zero(vec2 A) +{ + return all(equal(A, vec2(0.0))); +} + +bool is_zero(vec3 A) +{ + return all(equal(A, vec3(0.0))); +} + +bool is_zero(vec4 A) +{ + return all(equal(A, vec4(0.0))); +} diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index 470dfb01bdd..b9f7dd98073 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -1512,17 +1512,17 @@ static IK_Scene *convert_tree( iktarget->bldepsgraph = depsgraph; condata = (bKinematicConstraint *)iktarget->blenderConstraint->data; pchan = tree->pchan[iktarget->channel]; - unsigned int controltype, bonecnt; - double bonelen; + unsigned int controltype, bone_count; + double bone_length; float mat[4][4]; /* add the end effector * estimate the average bone length, used to clamp feedback error */ - for (bonecnt = 0, bonelen = 0.0f, a = iktarget->channel; a >= 0; - a = tree->parent[a], bonecnt++) { - bonelen += ikscene->blScale * tree->pchan[a]->bone->length; + for (bone_count = 0, bone_length = 0.0f, a = iktarget->channel; a >= 0; + a = tree->parent[a], bone_count++) { + bone_length += ikscene->blScale * tree->pchan[a]->bone->length; } - bonelen /= bonecnt; + bone_length /= bone_count; /* store the rest pose of the end effector to compute enforce target */ copy_m4_m4(mat, pchan->bone->arm_mat); @@ -1567,7 +1567,7 @@ static IK_Scene *convert_tree( } } if (controltype) { - iktarget->constraint = new iTaSC::CopyPose(controltype, controltype, bonelen); + iktarget->constraint = new iTaSC::CopyPose(controltype, controltype, bone_length); /* set the gain */ if (controltype & iTaSC::CopyPose::CTL_POSITION) { iktarget->constraint->setControlParameter( @@ -1599,7 +1599,7 @@ static IK_Scene *convert_tree( } break; case CONSTRAINT_IK_DISTANCE: - iktarget->constraint = new iTaSC::Distance(bonelen); + iktarget->constraint = new iTaSC::Distance(bone_length); iktarget->constraint->setControlParameter( iTaSC::Distance::ID_DISTANCE, iTaSC::ACT_VALUE, condata->dist); iktarget->constraint->registerCallback(distance_callback, iktarget); diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index 294aa8bbb9c..e46326467cc 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -57,6 +57,7 @@ set(SRC IMB_imbuf_types.h IMB_metadata.h IMB_moviecache.h + IMB_openexr.h IMB_thumbs.h intern/IMB_allocimbuf.h intern/IMB_anim.h @@ -173,6 +174,19 @@ if(WITH_IMAGE_HDR) add_definitions(-DWITH_HDR) endif() +if(WITH_IMAGE_WEBP) + list(APPEND SRC + intern/webp.c + ) + list(APPEND INC_SYS + ${WEBP_INCLUDE_DIRS} + ) + list(APPEND LIB + ${WEBP_LIBRARIES} + ) + add_definitions(-DWITH_WEBP) +endif() + list(APPEND INC ../../../intern/opencolorio ) diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index 785fa2b198a..7cf2c02e657 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -19,6 +19,7 @@ extern "C" { struct ColorManagedColorspaceSettings; struct ColorManagedDisplaySettings; struct ColorManagedViewSettings; +struct ColorManagedOutputSettings; struct ColormanageProcessor; struct EnumPropertyItem; struct ImBuf; @@ -52,6 +53,7 @@ bool IMB_colormanagement_space_is_data(struct ColorSpace *colorspace); bool IMB_colormanagement_space_is_scene_linear(struct ColorSpace *colorspace); bool IMB_colormanagement_space_is_srgb(struct ColorSpace *colorspace); bool IMB_colormanagement_space_name_is_data(const char *name); +bool IMB_colormanagement_space_name_is_scene_linear(const char *name); /** * Convert a float RGB triplet to the correct luminance weighted average. @@ -238,23 +240,10 @@ void IMB_colormanagement_imbuf_make_display_space( * in image format write callback and if float_colorspace is not NULL, no color * space transformation should be applied on this buffer. */ -struct ImBuf *IMB_colormanagement_imbuf_for_write( - struct ImBuf *ibuf, - bool save_as_render, - bool allocate_result, - const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings, - struct ImageFormatData *image_format_data); - -void IMB_colormanagement_buffer_make_display_space( - float *buffer, - unsigned char *display_buffer, - int width, - int height, - int channels, - float dither, - const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings); +struct ImBuf *IMB_colormanagement_imbuf_for_write(struct ImBuf *ibuf, + bool save_as_render, + bool allocate_result, + const struct ImageFormatData *image_format); /** \} */ diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 8929a467670..0390df06052 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -965,13 +965,13 @@ void IMB_stereo3d_write_dimensions( char mode, bool is_squeezed, size_t width, size_t height, size_t *r_width, size_t *r_height); void IMB_stereo3d_read_dimensions( char mode, bool is_squeezed, size_t width, size_t height, size_t *r_width, size_t *r_height); -int *IMB_stereo3d_from_rect(struct ImageFormatData *im_format, +int *IMB_stereo3d_from_rect(const struct ImageFormatData *im_format, size_t x, size_t y, size_t channels, int *rect_left, int *rect_right); -float *IMB_stereo3d_from_rectf(struct ImageFormatData *im_format, +float *IMB_stereo3d_from_rectf(const struct ImageFormatData *im_format, size_t x, size_t y, size_t channels, @@ -980,13 +980,13 @@ float *IMB_stereo3d_from_rectf(struct ImageFormatData *im_format, /** * Left/right are always float. */ -struct ImBuf *IMB_stereo3d_ImBuf(struct ImageFormatData *im_format, +struct ImBuf *IMB_stereo3d_ImBuf(const struct ImageFormatData *im_format, struct ImBuf *ibuf_left, struct ImBuf *ibuf_right); /** * Reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right). */ -void IMB_ImBufFromStereo3d(struct Stereo3dFormat *s3d, +void IMB_ImBufFromStereo3d(const struct Stereo3dFormat *s3d, struct ImBuf *ibuf_stereo, struct ImBuf **r_ibuf_left, struct ImBuf **r_ibuf_right); diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 98b7cc6e87f..934163846e4 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -80,6 +80,9 @@ enum eImbFileType { #ifdef WITH_DDS IMB_FTYPE_DDS = 13, #endif +#ifdef WITH_WEBP + IMB_FTYPE_WEBP = 14, +#endif }; /* Only for readability. */ diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/IMB_openexr.h index f5a2f983b18..32f393fc017 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/IMB_openexr.h @@ -2,12 +2,12 @@ * Copyright 2006 Blender Foundation. All rights reserved. */ /** \file - * \ingroup openexr + * \ingroup imbuf */ #pragma once -/* Experiment with more advanced EXR API. */ +/* API for reading and writing multilayer EXR files. */ /* XXX layer+pass name max 64? */ /* This api also supports max 8 channels per pass now. easy to fix! */ @@ -44,12 +44,12 @@ void IMB_exr_add_channel(void *handle, * Read from file. */ bool IMB_exr_begin_read( - void *handle, const char *filename, int *width, int *height, bool parse_channels); + void *handle, const char *filepath, int *width, int *height, bool parse_channels); /** * Used for output files (from #RenderResult) (single and multi-layer, single and multi-view). */ bool IMB_exr_begin_write(void *handle, - const char *filename, + const char *filepath, int width, int height, int compress, @@ -59,7 +59,7 @@ bool IMB_exr_begin_write(void *handle, * (FSA and Save Buffers). */ void IMB_exrtile_begin_write( - void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley); + void *handle, const char *filepath, int mipmap, int width, int height, int tilex, int tiley); /** * Still clumsy name handling, layers/channels can be ordered as list in list later. diff --git a/source/blender/imbuf/intern/IMB_colormanagement_intern.h b/source/blender/imbuf/intern/IMB_colormanagement_intern.h index 42941ace40b..c89b15480a2 100644 --- a/source/blender/imbuf/intern/IMB_colormanagement_intern.h +++ b/source/blender/imbuf/intern/IMB_colormanagement_intern.h @@ -33,6 +33,9 @@ typedef struct ColorSpace { struct OCIO_ConstCPUProcessorRcPtr *to_scene_linear; struct OCIO_ConstCPUProcessorRcPtr *from_scene_linear; + char (*aliases)[MAX_COLORSPACE_NAME]; + int num_aliases; + bool is_invertible; bool is_data; diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 035c5b10c60..31f8b3a9505 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -238,3 +238,16 @@ void imb_loadtiletiff( bool imb_savetiff(struct ImBuf *ibuf, const char *filepath, int flags); /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: TIFF (#IMB_FTYPE_WEBP) + * \{ */ + +bool imb_is_a_webp(const unsigned char *buf, size_t size); +struct ImBuf *imb_loadwebp(const unsigned char *mem, + size_t size, + int flags, + char colorspace[IM_MAX_SPACE]); +bool imb_savewebp(struct ImBuf *ibuf, const char *name, int flags); + +/** \} */ diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 15046ea54e1..193fda01816 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -37,6 +37,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_main.h" #include "RNA_define.h" @@ -503,7 +504,18 @@ static void colormanage_load_config(OCIO_ConstConfigRcPtr *config) is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace); is_data = OCIO_colorSpaceIsData(ocio_colorspace); - colormanage_colorspace_add(name, description, is_invertible, is_data); + ColorSpace *colorspace = colormanage_colorspace_add(name, description, is_invertible, is_data); + + colorspace->num_aliases = OCIO_colorSpaceGetNumAliases(ocio_colorspace); + if (colorspace->num_aliases > 0) { + colorspace->aliases = MEM_callocN(sizeof(*colorspace->aliases) * colorspace->num_aliases, + "ColorSpace aliases"); + for (int i = 0; i < colorspace->num_aliases; i++) { + BLI_strncpy(colorspace->aliases[i], + OCIO_colorSpaceGetAlias(ocio_colorspace, i), + MAX_COLORSPACE_NAME); + } + } OCIO_colorSpaceRelease(ocio_colorspace); } @@ -586,6 +598,7 @@ static void colormanage_free_config(void) } /* free color space itself */ + MEM_SAFE_FREE(colorspace->aliases); MEM_freeN(colorspace); colorspace = colorspace_next; @@ -1393,6 +1406,12 @@ bool IMB_colormanagement_space_name_is_data(const char *name) return (colorspace && colorspace->is_data); } +bool IMB_colormanagement_space_name_is_scene_linear(const char *name) +{ + ColorSpace *colorspace = colormanage_colorspace_get_named(name); + return (colorspace && IMB_colormanagement_space_is_scene_linear(colorspace)); +} + const float *IMB_colormanagement_get_xyz_to_rgb() { return &imbuf_xyz_to_rgb[0][0]; @@ -2430,15 +2449,12 @@ void IMB_colormanagement_imbuf_make_display_space( ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, - ImageFormatData *image_format_data) + const ImageFormatData *image_format) { ImBuf *colormanaged_ibuf = ibuf; - bool do_colormanagement; - bool is_movie = BKE_imtype_is_movie(image_format_data->imtype); - bool requires_linear_float = BKE_imtype_requires_linear_float(image_format_data->imtype); - bool do_alpha_under = image_format_data->planes != R_IMF_PLANES_RGBA; + const bool is_movie = BKE_imtype_is_movie(image_format->imtype); + const bool requires_linear_float = BKE_imtype_requires_linear_float(image_format->imtype); + const bool do_alpha_under = image_format->planes != R_IMF_PLANES_RGBA; if (ibuf->rect_float && ibuf->rect && (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0) { @@ -2446,9 +2462,13 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID); } - do_colormanagement = save_as_render && (is_movie || !requires_linear_float); + const bool do_colormanagement_display = save_as_render && (is_movie || !requires_linear_float); + const bool do_colormanagement_linear = save_as_render && requires_linear_float && + image_format->linear_colorspace_settings.name[0] && + !IMB_colormanagement_space_name_is_scene_linear( + image_format->linear_colorspace_settings.name); - if (do_colormanagement || do_alpha_under) { + if (do_colormanagement_display || do_colormanagement_linear || do_alpha_under) { if (allocate_result) { colormanaged_ibuf = IMB_dupImBuf(ibuf); } @@ -2501,15 +2521,16 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, } } - if (do_colormanagement) { + if (do_colormanagement_display) { + /* Color management with display and view transform. */ bool make_byte = false; /* for proper check whether byte buffer is required by a format or not * should be pretty safe since this image buffer is supposed to be used for * saving only and ftype would be overwritten a bit later by BKE_imbuf_write */ - colormanaged_ibuf->ftype = BKE_image_imtype_to_ftype(image_format_data->imtype, - &colormanaged_ibuf->foptions); + colormanaged_ibuf->ftype = BKE_imtype_to_ftype(image_format->imtype, + &colormanaged_ibuf->foptions); /* if file format isn't able to handle float buffer itself, * we need to allocate byte buffer and store color managed @@ -2523,16 +2544,39 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, } /* perform color space conversions */ - colormanagement_imbuf_make_display_space( - colormanaged_ibuf, view_settings, display_settings, make_byte); + colormanagement_imbuf_make_display_space(colormanaged_ibuf, + &image_format->view_settings, + &image_format->display_settings, + make_byte); if (colormanaged_ibuf->rect_float) { /* float buffer isn't linear anymore, * image format write callback should check for this flag and assume * no space conversion should happen if ibuf->float_colorspace != NULL */ - colormanaged_ibuf->float_colorspace = display_transform_get_colorspace(view_settings, - display_settings); + colormanaged_ibuf->float_colorspace = display_transform_get_colorspace( + &image_format->view_settings, &image_format->display_settings); + } + } + else if (do_colormanagement_linear) { + /* Color management transform to another linear color space. */ + if (!colormanaged_ibuf->rect_float) { + IMB_float_from_rect(colormanaged_ibuf); + imb_freerectImBuf(colormanaged_ibuf); + } + + if (colormanaged_ibuf->rect_float) { + const char *from_colorspace = (ibuf->float_colorspace) ? ibuf->float_colorspace->name : + global_role_scene_linear; + const char *to_colorspace = image_format->linear_colorspace_settings.name; + + IMB_colormanagement_transform(colormanaged_ibuf->rect_float, + colormanaged_ibuf->x, + colormanaged_ibuf->y, + colormanaged_ibuf->channels, + from_colorspace, + to_colorspace, + false); } } @@ -2543,45 +2587,6 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, return colormanaged_ibuf; } -void IMB_colormanagement_buffer_make_display_space( - float *buffer, - unsigned char *display_buffer, - int width, - int height, - int channels, - float dither, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings) -{ - ColormanageProcessor *cm_processor; - size_t float_buffer_size = ((size_t)width) * height * channels * sizeof(float); - float *display_buffer_float = MEM_mallocN(float_buffer_size, "byte_buffer_make_display_space"); - - /* TODO(sergey): Convert float directly to byte buffer. */ - - memcpy(display_buffer_float, buffer, float_buffer_size); - - cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); - - processor_transform_apply_threaded( - NULL, display_buffer_float, width, height, channels, cm_processor, true, false); - - IMB_buffer_byte_from_float(display_buffer, - display_buffer_float, - channels, - dither, - IB_PROFILE_SRGB, - IB_PROFILE_SRGB, - true, - width, - height, - width, - width); - - MEM_freeN(display_buffer_float); - IMB_colormanagement_processor_free(cm_processor); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3063,6 +3068,12 @@ ColorSpace *colormanage_colorspace_get_named(const char *name) if (STREQ(colorspace->name, name)) { return colorspace; } + + for (int i = 0; i < colorspace->num_aliases; i++) { + if (STREQ(colorspace->aliases[i], name)) { + return colorspace; + } + } } return NULL; diff --git a/source/blender/imbuf/intern/dds/Stream.cpp b/source/blender/imbuf/intern/dds/Stream.cpp index bc7239dbd1b..34f3654aa3f 100644 --- a/source/blender/imbuf/intern/dds/Stream.cpp +++ b/source/blender/imbuf/intern/dds/Stream.cpp @@ -12,14 +12,14 @@ static const char *msg_error_seek = "DDS: trying to seek beyond end of stream (corrupt file?)"; static const char *msg_error_read = "DDS: trying to read beyond end of stream (corrupt file?)"; -inline bool is_read_within_bounds(const Stream &mem, unsigned int cnt) +inline bool is_read_within_bounds(const Stream &mem, unsigned int count) { if (mem.pos >= mem.size) { /* No more data remained in the memory buffer. */ return false; } - if (cnt > mem.size - mem.pos) { + if (count > mem.size - mem.pos) { /* Reading past the memory bounds. */ return false; } @@ -83,15 +83,15 @@ unsigned int mem_read(Stream &mem, unsigned char &i) return 1; } -unsigned int mem_read(Stream &mem, unsigned char *i, unsigned int cnt) +unsigned int mem_read(Stream &mem, unsigned char *i, unsigned int count) { - if (!is_read_within_bounds(mem, cnt)) { + if (!is_read_within_bounds(mem, count)) { mem.set_failed(msg_error_read); return 0; } - memcpy(i, mem.mem + mem.pos, cnt); - mem.pos += cnt; - return cnt; + memcpy(i, mem.mem + mem.pos, count); + mem.pos += count; + return count; } void Stream::set_failed(const char *msg) diff --git a/source/blender/imbuf/intern/dds/Stream.h b/source/blender/imbuf/intern/dds/Stream.h index 3e17b143629..d90440f47cd 100644 --- a/source/blender/imbuf/intern/dds/Stream.h +++ b/source/blender/imbuf/intern/dds/Stream.h @@ -24,4 +24,4 @@ unsigned int mem_read(Stream &mem, unsigned long long &i); unsigned int mem_read(Stream &mem, unsigned int &i); unsigned int mem_read(Stream &mem, unsigned short &i); unsigned int mem_read(Stream &mem, unsigned char &i); -unsigned int mem_read(Stream &mem, unsigned char *i, unsigned int cnt); +unsigned int mem_read(Stream &mem, unsigned char *i, unsigned int count); diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 60442f97885..548bc9e120c 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -197,6 +197,20 @@ const ImFileType IMB_FILE_TYPES[] = { .default_save_role = COLOR_ROLE_DEFAULT_FLOAT, }, #endif +#ifdef WITH_WEBP + { + .init = NULL, + .exit = NULL, + .is_a = imb_is_a_webp, + .load = imb_loadwebp, + .load_filepath = NULL, + .save = imb_savewebp, + .load_tile = NULL, + .flag = 0, + .filetype = IMB_FTYPE_WEBP, + .default_save_role = COLOR_ROLE_DEFAULT_BYTE, + }, +#endif {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, }; diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index c1e00642a6d..84bed479577 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -1314,14 +1314,14 @@ static void index_rebuild_fallback(FallbackIndexBuilderContext *context, short *do_update, float *progress) { - int cnt = IMB_anim_get_duration(context->anim, IMB_TC_NONE); + int count = IMB_anim_get_duration(context->anim, IMB_TC_NONE); int i, pos; struct anim *anim = context->anim; - for (pos = 0; pos < cnt; pos++) { + for (pos = 0; pos < count; pos++) { struct ImBuf *ibuf = IMB_anim_absolute(anim, pos, IMB_TC_NONE, IMB_PROXY_NONE); struct ImBuf *tmp_ibuf = IMB_dupImBuf(ibuf); - float next_progress = (float)pos / (float)cnt; + float next_progress = (float)pos / (float)count; if (*progress != next_progress) { *progress = next_progress; diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index 86321d6432c..eb0a2c4a47f 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -105,7 +105,7 @@ static int expandrow2( float *optr, const float *optr_end, const uchar *iptr, const uchar *iptr_end, int z); static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n); static void interleaverow2(float *lptr, const uchar *cptr, int z, int n); -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int cnt); +static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len); static void lumrow(const uchar *rgbptr, uchar *lumptr, int n); /* @@ -892,7 +892,7 @@ static void lumrow(const uchar *rgbptr, uchar *lumptr, int n) } } -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int cnt) +static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len) { uchar *iptr, *ibufend, *sptr, *optr; short todo, cc; @@ -900,7 +900,7 @@ static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int cnt) lbuf += z; iptr = lbuf; - ibufend = iptr + cnt * 4; + ibufend = iptr + row_len * 4; optr = rlebuf; while (iptr < ibufend) { diff --git a/source/blender/imbuf/intern/openexr/CMakeLists.txt b/source/blender/imbuf/intern/openexr/CMakeLists.txt index 08b17764d12..c34a97f6837 100644 --- a/source/blender/imbuf/intern/openexr/CMakeLists.txt +++ b/source/blender/imbuf/intern/openexr/CMakeLists.txt @@ -17,10 +17,8 @@ set(INC_SYS ) set(SRC - openexr_api.h - openexr_multi.h - openexr_api.cpp + openexr_api.h ) set(LIB diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 418a4724c00..9948aaac5da 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -78,6 +78,7 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) } #include "BLI_blenlib.h" #include "BLI_math_color.h" +#include "BLI_string_utils.h" #include "BLI_threads.h" #include "BKE_idprop.h" @@ -89,8 +90,7 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_metadata.h" - -#include "openexr_multi.h" +#include "IMB_openexr.h" using namespace Imf; using namespace Imath; @@ -155,15 +155,15 @@ class IMemStream : public Imf::IStream { class IFileStream : public Imf::IStream { public: - IFileStream(const char *filename) : IStream(filename) + IFileStream(const char *filepath) : IStream(filepath) { /* utf-8 file path support on windows */ #if defined(WIN32) - wchar_t *wfilename = alloc_utf16_from_8(filename, 0); - ifs.open(wfilename, std::ios_base::binary); - free(wfilename); + wchar_t *wfilepath = alloc_utf16_from_8(filepath, 0); + ifs.open(wfilepath, std::ios_base::binary); + free(wfilepath); #else - ifs.open(filename, std::ios_base::binary); + ifs.open(filepath, std::ios_base::binary); #endif if (!ifs) { @@ -261,15 +261,15 @@ class OMemStream : public OStream { class OFileStream : public OStream { public: - OFileStream(const char *filename) : OStream(filename) + OFileStream(const char *filepath) : OStream(filepath) { /* utf-8 file path support on windows */ #if defined(WIN32) - wchar_t *wfilename = alloc_utf16_from_8(filename, 0); - ofs.open(wfilename, std::ios_base::binary); - free(wfilename); + wchar_t *wfilepath = alloc_utf16_from_8(filepath, 0); + ofs.open(wfilepath, std::ios_base::binary); + free(wfilepath); #else - ofs.open(filename, std::ios_base::binary); + ofs.open(filepath, std::ios_base::binary); #endif if (!ofs) { @@ -834,7 +834,7 @@ void IMB_exr_add_channel(void *handle, } bool IMB_exr_begin_write(void *handle, - const char *filename, + const char *filepath, int width, int height, int compress, @@ -872,7 +872,7 @@ bool IMB_exr_begin_write(void *handle, /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ try { - data->ofile_stream = new OFileStream(filename); + data->ofile_stream = new OFileStream(filepath); data->ofile = new OutputFile(*(data->ofile_stream), header); } catch (const std::exception &exc) { @@ -889,7 +889,7 @@ bool IMB_exr_begin_write(void *handle, } void IMB_exrtile_begin_write( - void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) + void *handle, const char *filepath, int mipmap, int width, int height, int tilex, int tiley) { ExrHandle *data = (ExrHandle *)handle; Header header(width, height); @@ -941,7 +941,7 @@ void IMB_exrtile_begin_write( /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ try { - data->ofile_stream = new OFileStream(filename); + data->ofile_stream = new OFileStream(filepath); data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size()); } catch (const std::exception &) { @@ -954,19 +954,19 @@ void IMB_exrtile_begin_write( } bool IMB_exr_begin_read( - void *handle, const char *filename, int *width, int *height, const bool parse_channels) + void *handle, const char *filepath, int *width, int *height, const bool parse_channels) { ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; /* 32 is arbitrary, but zero length files crashes exr. */ - if (!(BLI_exists(filename) && BLI_file_size(filename) > 32)) { + if (!(BLI_exists(filepath) && BLI_file_size(filepath) > 32)) { return false; } /* avoid crash/abort when we don't have permission to write here */ try { - data->ifile_stream = new IFileStream(filename); + data->ifile_stream = new IFileStream(filepath); data->ifile = new MultiPartInputFile(*(data->ifile_stream)); } catch (const std::exception &) { diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 4ca4a8313b1..f8f204af70c 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -6,7 +6,8 @@ */ #include "openexr_api.h" -#include "openexr_multi.h" + +#include "IMB_openexr.h" void *IMB_exr_get_handle(void) { @@ -28,7 +29,7 @@ void IMB_exr_add_channel(void * /*handle*/, } bool IMB_exr_begin_read(void * /*handle*/, - const char * /*filename*/, + const char * /*filepath*/, int * /*width*/, int * /*height*/, const bool /*add_channels*/) @@ -36,7 +37,7 @@ bool IMB_exr_begin_read(void * /*handle*/, return false; } bool IMB_exr_begin_write(void * /*handle*/, - const char * /*filename*/, + const char * /*filepath*/, int /*width*/, int /*height*/, int /*compress*/, @@ -45,7 +46,7 @@ bool IMB_exr_begin_write(void * /*handle*/, return false; } void IMB_exrtile_begin_write(void * /*handle*/, - const char * /*filename*/, + const char * /*filepath*/, int /*mipmap*/, int /*width*/, int /*height*/, diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index 3786f1c5754..aa07edf5c3a 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -308,7 +308,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, static int fwritecolrs( FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan) { - int beg, c2, cnt = 0; + int beg, c2, count = 0; fCOLOR fcol; RGBE rgbe, *rgbe_scan; @@ -347,14 +347,14 @@ static int fwritecolrs( putc((unsigned char)(width & 255), file); /* put components separately */ for (size_t i = 0; i < 4; i++) { - for (size_t j = 0; j < width; j += cnt) { /* find next run */ - for (beg = j; beg < width; beg += cnt) { - for (cnt = 1; (cnt < 127) && ((beg + cnt) < width) && - (rgbe_scan[beg + cnt][i] == rgbe_scan[beg][i]); - cnt++) { + for (size_t j = 0; j < width; j += count) { /* find next run */ + for (beg = j; beg < width; beg += count) { + for (count = 1; (count < 127) && ((beg + count) < width) && + (rgbe_scan[beg + count][i] == rgbe_scan[beg][i]); + count++) { /* pass */ } - if (cnt >= MINRUN) { + if (count >= MINRUN) { break; /* long enough */ } } @@ -378,12 +378,12 @@ static int fwritecolrs( putc(rgbe_scan[j++][i], file); } } - if (cnt >= MINRUN) { /* write out run */ - putc((unsigned char)(128 + cnt), file); + if (count >= MINRUN) { /* write out run */ + putc((unsigned char)(128 + count), file); putc(rgbe_scan[beg][i], file); } else { - cnt = 0; + count = 0; } } } diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index c081ffb981a..52756891f21 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -28,8 +28,10 @@ /* prototypes */ struct Stereo3DData; -static void imb_stereo3d_write_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); -static void imb_stereo3d_read_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); +static void imb_stereo3d_write_doit(struct Stereo3DData *s3d_data, + const struct Stereo3dFormat *s3d); +static void imb_stereo3d_read_doit(struct Stereo3DData *s3d_data, + const struct Stereo3dFormat *s3d); typedef struct Stereo3DData { struct { @@ -46,7 +48,7 @@ typedef struct Stereo3DData { /** \name Local Functions * \{ */ -static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +static void imb_stereo3d_write_anaglyph(const Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) { int x, y; size_t width = s3d->x; @@ -144,7 +146,7 @@ static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyp } } -static void imb_stereo3d_write_interlace(Stereo3DData *s3d, +static void imb_stereo3d_write_interlace(const Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) { @@ -402,7 +404,7 @@ static void imb_stereo3d_write_interlace(Stereo3DData *s3d, } /* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ -static void imb_stereo3d_write_sidebyside(Stereo3DData *s3d, const bool crosseyed) +static void imb_stereo3d_write_sidebyside(const Stereo3DData *s3d, const bool crosseyed) { int y; size_t width = s3d->x; @@ -450,7 +452,7 @@ static void imb_stereo3d_write_sidebyside(Stereo3DData *s3d, const bool crosseye } /* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ -static void imb_stereo3d_write_topbottom(Stereo3DData *s3d) +static void imb_stereo3d_write_topbottom(const Stereo3DData *s3d) { int y; size_t width = s3d->x; @@ -565,7 +567,7 @@ void IMB_stereo3d_read_dimensions(const char mode, * \{ */ static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, - Stereo3dFormat *s3d, + const Stereo3dFormat *s3d, const size_t x, const size_t y) { @@ -581,7 +583,7 @@ static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, } static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf, - Stereo3dFormat *s3d, + const Stereo3dFormat *s3d, const size_t x, const size_t y) { @@ -597,7 +599,7 @@ static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf, } static void imb_stereo3d_squeeze_rectf( - float *rectf, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) + float *rectf, const Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) { ImBuf *ibuf; size_t width, height; @@ -631,7 +633,7 @@ static void imb_stereo3d_squeeze_rectf( } static void imb_stereo3d_squeeze_rect( - int *rect, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) + int *rect, const Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) { ImBuf *ibuf; size_t width, height; @@ -693,7 +695,7 @@ static void imb_stereo3d_data_init(Stereo3DData *s3d_data, s3d_data->rectf.stereo = rectf_stereo; } -int *IMB_stereo3d_from_rect(ImageFormatData *im_format, +int *IMB_stereo3d_from_rect(const ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, @@ -717,7 +719,7 @@ int *IMB_stereo3d_from_rect(ImageFormatData *im_format, return r_rect; } -float *IMB_stereo3d_from_rectf(ImageFormatData *im_format, +float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, @@ -741,7 +743,7 @@ float *IMB_stereo3d_from_rectf(ImageFormatData *im_format, return r_rectf; } -ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) +ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) { ImBuf *ibuf_stereo = NULL; Stereo3DData s3d_data = {{NULL}}; @@ -776,7 +778,7 @@ ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *i return ibuf_stereo; } -static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, const Stereo3dFormat *s3d) { switch (s3d->display_mode) { case S3D_DISPLAY_ANAGLYPH: @@ -803,7 +805,7 @@ static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) /** \name Reading Stereo ImBuf's * \{ */ -static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +static void imb_stereo3d_read_anaglyph(const Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) { int x, y; size_t width = s3d->x; @@ -901,7 +903,7 @@ static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyph } } -static void imb_stereo3d_read_interlace(Stereo3DData *s3d, +static void imb_stereo3d_read_interlace(const Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) { @@ -1159,7 +1161,7 @@ static void imb_stereo3d_read_interlace(Stereo3DData *s3d, } /* stereo input (s3d->rectf.stereo) is always unsqueezed */ -static void imb_stereo3d_read_sidebyside(Stereo3DData *s3d, const bool crosseyed) +static void imb_stereo3d_read_sidebyside(const Stereo3DData *s3d, const bool crosseyed) { int y; size_t width = s3d->x; @@ -1208,7 +1210,7 @@ static void imb_stereo3d_read_sidebyside(Stereo3DData *s3d, const bool crosseyed } /* stereo input (s3d->rectf.stereo) is always unsqueezed */ -static void imb_stereo3d_read_topbottom(Stereo3DData *s3d) +static void imb_stereo3d_read_topbottom(const Stereo3DData *s3d) { int y; size_t width = s3d->x; @@ -1258,7 +1260,7 @@ static void imb_stereo3d_read_topbottom(Stereo3DData *s3d) /** \name Preparing To Call The Read Functions * \{ */ -void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, +void IMB_ImBufFromStereo3d(const Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d, ImBuf **r_ibuf_left, ImBuf **r_ibuf_right) @@ -1337,7 +1339,7 @@ void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, *r_ibuf_right = ibuf_right; } -static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, const Stereo3dFormat *s3d) { switch (s3d->display_mode) { case S3D_DISPLAY_ANAGLYPH: diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 241f1a736f4..45b50c866fe 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -41,12 +41,12 @@ #define UTIL_DEBUG 0 const char *imb_ext_image[] = { - ".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb", ".rgba", + ".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb", ".rgba", #ifdef WITH_TIFF - ".tif", ".tiff", ".tx", + ".tif", ".tiff", ".tx", #endif #ifdef WITH_OPENJPEG - ".jp2", ".j2c", + ".jp2", ".j2c", #endif #ifdef WITH_HDR ".hdr", @@ -55,13 +55,16 @@ const char *imb_ext_image[] = { ".dds", #endif #ifdef WITH_CINEON - ".dpx", ".cin", + ".dpx", ".cin", #endif #ifdef WITH_OPENEXR ".exr", #endif #ifdef WITH_OPENIMAGEIO - ".psd", ".pdd", ".psb", + ".psd", ".pdd", ".psb", +#endif +#ifdef WITH_WEBP + ".webp", #endif NULL, }; diff --git a/source/blender/imbuf/intern/webp.c b/source/blender/imbuf/intern/webp.c new file mode 100644 index 00000000000..19fe2373ea0 --- /dev/null +++ b/source/blender/imbuf/intern/webp.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup imbuf + */ + +#include <stdio.h> +#include <stdlib.h> +#include <webp/decode.h> +#include <webp/encode.h> + +#include "BLI_fileops.h" +#include "BLI_utildefines.h" + +#include "IMB_colormanagement.h" +#include "IMB_colormanagement_intern.h" +#include "IMB_filetype.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "MEM_guardedalloc.h" + +bool imb_is_a_webp(const unsigned char *buf, size_t size) +{ + if (WebPGetInfo(buf, size, NULL, NULL)) { + return true; + } + return false; +} + +ImBuf *imb_loadwebp(const unsigned char *mem, + size_t size, + int flags, + char colorspace[IM_MAX_SPACE]) +{ + if (!imb_is_a_webp(mem, size)) { + return NULL; + } + + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + + WebPBitstreamFeatures features; + if (WebPGetFeatures(mem, size, &features) != VP8_STATUS_OK) { + fprintf(stderr, "WebP: Failed to parse features\n"); + return NULL; + } + + const int planes = features.has_alpha ? 32 : 24; + ImBuf *ibuf = IMB_allocImBuf(features.width, features.height, planes, 0); + + if (ibuf == NULL) { + fprintf(stderr, "WebP: Failed to allocate image memory\n"); + return NULL; + } + + if ((flags & IB_test) == 0) { + ibuf->ftype = IMB_FTYPE_WEBP; + imb_addrectImBuf(ibuf); + /* Flip the image during decoding to match Blender. */ + unsigned char *last_row = (unsigned char *)(ibuf->rect + (ibuf->y - 1) * ibuf->x); + if (WebPDecodeRGBAInto(mem, size, last_row, (size_t)(ibuf->x) * ibuf->y * 4, -4 * ibuf->x) == + NULL) { + fprintf(stderr, "WebP: Failed to decode image\n"); + } + } + + return ibuf; +} + +bool imb_savewebp(struct ImBuf *ibuf, const char *name, int UNUSED(flags)) +{ + const int bytesperpixel = (ibuf->planes + 7) >> 3; + unsigned char *encoded_data, *last_row; + size_t encoded_data_size; + + if (bytesperpixel == 3) { + /* We must convert the ImBuf RGBA buffer to RGB as WebP expects a RGB buffer. */ + const size_t num_pixels = ibuf->x * ibuf->y; + const uint8_t *rgba_rect = (uint8_t *)ibuf->rect; + uint8_t *rgb_rect = MEM_mallocN(sizeof(uint8_t) * num_pixels * 3, "webp rgb_rect"); + for (int i = 0; i < num_pixels; i++) { + rgb_rect[i * 3 + 0] = rgba_rect[i * 4 + 0]; + rgb_rect[i * 3 + 1] = rgba_rect[i * 4 + 1]; + rgb_rect[i * 3 + 2] = rgba_rect[i * 4 + 2]; + } + + last_row = (unsigned char *)(rgb_rect + (ibuf->y - 1) * ibuf->x * 3); + + if (ibuf->foptions.quality == 100.0f) { + encoded_data_size = WebPEncodeLosslessRGB( + last_row, ibuf->x, ibuf->y, -3 * ibuf->x, &encoded_data); + } + else { + encoded_data_size = WebPEncodeRGB( + last_row, ibuf->x, ibuf->y, -3 * ibuf->x, ibuf->foptions.quality, &encoded_data); + } + MEM_freeN(rgb_rect); + } + else if (bytesperpixel == 4) { + last_row = (unsigned char *)(ibuf->rect + (ibuf->y - 1) * ibuf->x); + + if (ibuf->foptions.quality == 100.0f) { + encoded_data_size = WebPEncodeLosslessRGBA( + last_row, ibuf->x, ibuf->y, -4 * ibuf->x, &encoded_data); + } + else { + encoded_data_size = WebPEncodeRGBA( + last_row, ibuf->x, ibuf->y, -4 * ibuf->x, ibuf->foptions.quality, &encoded_data); + } + } + else { + fprintf(stderr, "WebP: Unsupported bytes per pixel: %d for file: '%s'\n", bytesperpixel, name); + return false; + } + + if (encoded_data != NULL) { + FILE *fp = BLI_fopen(name, "wb"); + if (!fp) { + free(encoded_data); + fprintf(stderr, "WebP: Cannot open file for writing: '%s'\n", name); + return false; + } + fwrite(encoded_data, encoded_data_size, 1, fp); + free(encoded_data); + fclose(fp); + } + + return true; +} diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index a9ade9b02fa..d0c10bd7f76 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -111,7 +111,7 @@ struct Mesh *ABC_read_mesh(struct CacheReader *reader, bool ABC_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, - struct Mesh *existing_mesh, + const struct Mesh *existing_mesh, float time, const char **err_str); diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 11693eeb4de..bbb196dc383 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -158,6 +158,7 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context) BMeshCreateParams bmesh_create_params{}; BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; + bmesh_from_mesh_params.calc_vert_normal = true; BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, nullptr, nullptr, nullptr); diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index ecb1fa0a752..c413fbf0ff9 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -61,7 +61,7 @@ static void get_uvs(const CDStreamConfig &config, MLoop *mloop = config.mloop; if (!config.pack_uvs) { - int cnt = 0; + int count = 0; uvidx.resize(config.totloop); uvs.resize(config.totloop); @@ -70,12 +70,12 @@ static void get_uvs(const CDStreamConfig &config, MPoly ¤t_poly = polygons[i]; MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; - for (int j = 0; j < current_poly.totloop; j++, cnt++) { + for (int j = 0; j < current_poly.totloop; j++, count++) { loopuv--; - uvidx[cnt] = cnt; - uvs[cnt][0] = loopuv->uv[0]; - uvs[cnt][1] = loopuv->uv[1]; + uvidx[count] = count; + uvs[count][0] = loopuv->uv[0]; + uvs[count][1] = loopuv->uv[1]; } } } diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 1d00b55ca78..47f4dd2ea5d 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -658,7 +658,7 @@ bool AbcMeshReader::accepts_object_type( return true; } -bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector &sample_sel) +bool AbcMeshReader::topology_changed(const Mesh *existing_mesh, const ISampleSelector &sample_sel) { IPolyMeshSchema::Sample sample; try { diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index bc853e3b6e4..f97525297b7 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -15,8 +15,6 @@ namespace blender::io::alembic { class AbcMeshReader final : public AbcObjectReader { Alembic::AbcGeom::IPolyMeshSchema m_schema; - CDStreamConfig m_mesh_data; - public: AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings); @@ -32,7 +30,7 @@ class AbcMeshReader final : public AbcObjectReader { const char *velocity_name, float velocity_scale, const char **err_str) override; - bool topology_changed(Mesh *existing_mesh, + bool topology_changed(const Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel) override; private: @@ -49,8 +47,6 @@ class AbcMeshReader final : public AbcObjectReader { class AbcSubDReader final : public AbcObjectReader { Alembic::AbcGeom::ISubDSchema m_schema; - CDStreamConfig m_mesh_data; - public: AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings); diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index 72aec8f4336..dac0890e7c5 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -140,7 +140,7 @@ struct Mesh *AbcObjectReader::read_mesh(struct Mesh *existing_mesh, return existing_mesh; } -bool AbcObjectReader::topology_changed(Mesh * /*existing_mesh*/, +bool AbcObjectReader::topology_changed(const Mesh * /*existing_mesh*/, const Alembic::Abc::ISampleSelector & /*sample_sel*/) { /* The default implementation of read_mesh() just returns the original mesh, so never changes the diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index d33eabfbee5..5898d1bd529 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -139,7 +139,7 @@ class AbcObjectReader { const char *velocity_name, float velocity_scale, const char **err_str); - virtual bool topology_changed(Mesh *existing_mesh, + virtual bool topology_changed(const Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel); /** Reads the object matrix and sets up an object transform if animated. */ diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index f8267d19d8f..c9c982aad6c 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -274,6 +274,7 @@ static std::pair<bool, AbcObjectReader *> visit_object( children_claiming_this_object += child_claims_this_object ? 1 : 0; } BLI_assert(children_claiming_this_object == claiming_child_readers.size()); + UNUSED_VARS_NDEBUG(children_claiming_this_object); AbcObjectReader *reader = nullptr; const MetaData &md = object.getMetaData(); @@ -800,8 +801,11 @@ Mesh *ABC_read_mesh(CacheReader *reader, existing_mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str); } -bool ABC_mesh_topology_changed( - CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str) +bool ABC_mesh_topology_changed(CacheReader *reader, + Object *ob, + const Mesh *existing_mesh, + const float time, + const char **err_str) { AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); if (abc_reader == nullptr) { diff --git a/source/blender/io/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c index 53366a7b0e3..fb42274fef2 100644 --- a/source/blender/io/avi/intern/avi_mjpeg.c +++ b/source/blender/io/avi/intern/avi_mjpeg.c @@ -525,14 +525,14 @@ static boolean jpegmemsrcmgr_fill_input_buffer(j_decompress_ptr dinfo) return true; } -static void jpegmemsrcmgr_skip_input_data(j_decompress_ptr dinfo, long skipcnt) +static void jpegmemsrcmgr_skip_input_data(j_decompress_ptr dinfo, long skip_count) { - if (dinfo->src->bytes_in_buffer < skipcnt) { - skipcnt = dinfo->src->bytes_in_buffer; + if (dinfo->src->bytes_in_buffer < skip_count) { + skip_count = dinfo->src->bytes_in_buffer; } - dinfo->src->next_input_byte += skipcnt; - dinfo->src->bytes_in_buffer -= skipcnt; + dinfo->src->next_input_byte += skip_count; + dinfo->src->bytes_in_buffer -= skip_count; } static void jpegmemsrcmgr_term_source(j_decompress_ptr dinfo) diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index 2c9f16c7624..3b21d423df5 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -120,7 +120,7 @@ void GeometryExporter::operator()(Object *ob) /* skip the basis */ kb = kb->next; for (; kb; kb = kb->next) { - BKE_keyblock_convert_to_mesh(kb, me); + BKE_keyblock_convert_to_mesh(kb, me->mvert, me->totvert); export_key_mesh(ob, me, kb); } } diff --git a/source/blender/io/collada/ImageExporter.cpp b/source/blender/io/collada/ImageExporter.cpp index f57c5dd5e53..1223abbaf95 100644 --- a/source/blender/io/collada/ImageExporter.cpp +++ b/source/blender/io/collada/ImageExporter.cpp @@ -14,6 +14,7 @@ #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_main.h" #include "BKE_mesh.h" @@ -48,7 +49,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) bool is_dirty = BKE_image_is_dirty(image); ImageFormatData imageFormat; - BKE_imbuf_to_image_format(&imageFormat, imbuf); + BKE_image_format_from_imbuf(&imageFormat, imbuf); short image_source = image->source; bool is_generated = image_source == IMA_SRC_GENERATED; diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp index 79ac5be35f8..8d7ada4a600 100644 --- a/source/blender/io/collada/collada_utils.cpp +++ b/source/blender/io/collada/collada_utils.cpp @@ -411,6 +411,7 @@ void bc_triangulate_mesh(Mesh *me) BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default, &bm_create_params); BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; + bm_from_me_params.calc_vert_normal = true; BM_mesh_bm_from_me(bm, me, &bm_from_me_params); BM_mesh_triangulate(bm, quad_method, use_beauty, 4, tag_only, nullptr, nullptr, nullptr); diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h index 6cd9fe087a6..215891e3e48 100644 --- a/source/blender/io/gpencil/gpencil_io.h +++ b/source/blender/io/gpencil/gpencil_io.h @@ -72,11 +72,11 @@ typedef enum eGpencilExportFrame { /** * Main export entry point function. */ -bool gpencil_io_export(const char *filename, struct GpencilIOParams *iparams); +bool gpencil_io_export(const char *filepath, struct GpencilIOParams *iparams); /** * Main import entry point function. */ -bool gpencil_io_import(const char *filename, struct GpencilIOParams *iparams); +bool gpencil_io_import(const char *filepath, struct GpencilIOParams *iparams); #ifdef __cplusplus } diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index 9379e72bdd9..05f1158c57d 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -174,10 +174,10 @@ void GpencilIO::create_object_list() }); } -void GpencilIO::filename_set(const char *filename) +void GpencilIO::filepath_set(const char *filepath) { - BLI_strncpy(filename_, filename, FILE_MAX); - BLI_path_abs(filename_, BKE_main_blendfile_path(bmain_)); + BLI_strncpy(filepath_, filepath, FILE_MAX); + BLI_path_abs(filepath_, BKE_main_blendfile_path(bmain_)); } bool GpencilIO::gpencil_3D_point_to_screen_space(const float3 co, float2 &r_co) diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh index 96ba49c31b1..a89b723ed6c 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh @@ -40,7 +40,7 @@ class GpencilIO { bool invert_axis_[2]; float4x4 diff_mat_; - char filename_[FILE_MAX]; + char filepath_[FILE_MAX]; /* Used for sorting objects. */ struct ObjectZ { @@ -94,9 +94,9 @@ class GpencilIO { void selected_objects_boundbox_get(rctf *boundbox); /** * Set file input_text full path. - * \param filename: Path of the file provided by save dialog. + * \param filepath: Path of the file provided by save dialog. */ - void filename_set(const char *filename); + void filepath_set(const char *filepath); private: float avg_opacity_; diff --git a/source/blender/io/gpencil/intern/gpencil_io_capi.cc b/source/blender/io/gpencil/intern/gpencil_io_capi.cc index 5acac885a38..84b273bc570 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_capi.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_capi.cc @@ -161,32 +161,32 @@ static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter, } #endif -bool gpencil_io_import(const char *filename, GpencilIOParams *iparams) +bool gpencil_io_import(const char *filepath, GpencilIOParams *iparams) { - GpencilImporterSVG importer = GpencilImporterSVG(filename, iparams); + GpencilImporterSVG importer = GpencilImporterSVG(filepath, iparams); return gpencil_io_import_frame(&importer, *iparams); } -bool gpencil_io_export(const char *filename, GpencilIOParams *iparams) +bool gpencil_io_export(const char *filepath, GpencilIOParams *iparams) { Depsgraph *depsgraph_ = CTX_data_depsgraph_pointer(iparams->C); Scene *scene_ = CTX_data_scene(iparams->C); Object *ob = CTX_data_active_object(iparams->C); - UNUSED_VARS(filename, depsgraph_, scene_, ob); + UNUSED_VARS(filepath, depsgraph_, scene_, ob); switch (iparams->mode) { #ifdef WITH_PUGIXML case GP_EXPORT_TO_SVG: { - GpencilExporterSVG exporter = GpencilExporterSVG(filename, iparams); + GpencilExporterSVG exporter = GpencilExporterSVG(filepath, iparams); return gpencil_io_export_frame_svg(&exporter, scene_, iparams, true, true, true); break; } #endif #ifdef WITH_HARU case GP_EXPORT_TO_PDF: { - GpencilExporterPDF exporter = GpencilExporterPDF(filename, iparams); + GpencilExporterPDF exporter = GpencilExporterPDF(filepath, iparams); return gpencil_io_export_pdf(depsgraph_, scene_, ob, &exporter, iparams); break; } diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc index cc3eab02e07..205ab788e6d 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc @@ -43,10 +43,10 @@ static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void *UNU } /* Constructor. */ -GpencilExporterPDF::GpencilExporterPDF(const char *filename, const GpencilIOParams *iparams) +GpencilExporterPDF::GpencilExporterPDF(const char *filepath, const GpencilIOParams *iparams) : GpencilExporter(iparams) { - filename_set(filename); + filepath_set(filepath); invert_axis_[0] = false; invert_axis_[1] = false; @@ -78,16 +78,16 @@ bool GpencilExporterPDF::write() /* TODO: It looks `libharu` does not support unicode. */ #if 0 /* `ifdef WIN32` */ - char filename_cstr[FILE_MAX]; - BLI_strncpy(filename_cstr, filename_, FILE_MAX); + char filepath_cstr[FILE_MAX]; + BLI_strncpy(filepath_cstr, filepath_, FILE_MAX); - UTF16_ENCODE(filename_cstr); - std::wstring wstr(filename_cstr_16); + UTF16_ENCODE(filepath_cstr); + std::wstring wstr(filepath_cstr_16); res = HPDF_SaveToFile(pdf_, wstr.c_str()); - UTF16_UN_ENCODE(filename_cstr); + UTF16_UN_ENCODE(filepath_cstr); #else - res = HPDF_SaveToFile(pdf_, filename_); + res = HPDF_SaveToFile(pdf_, filepath_); #endif return (res == 0) ? true : false; diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh index f6b9fe4fec1..bfec6bc506b 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh @@ -21,7 +21,7 @@ namespace blender::io::gpencil { class GpencilExporterPDF : public GpencilExporter { public: - GpencilExporterPDF(const char *filename, const struct GpencilIOParams *iparams); + GpencilExporterPDF(const char *filepath, const struct GpencilIOParams *iparams); bool new_document(); bool add_newpage(); bool add_body(); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc index f8d30546e39..5d33a2806bd 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc @@ -40,10 +40,10 @@ namespace blender ::io ::gpencil { /* Constructor. */ -GpencilExporterSVG::GpencilExporterSVG(const char *filename, const GpencilIOParams *iparams) +GpencilExporterSVG::GpencilExporterSVG(const char *filepath, const GpencilIOParams *iparams) : GpencilExporter(iparams) { - filename_set(filename); + filepath_set(filepath); invert_axis_[0] = false; invert_axis_[1] = true; @@ -66,16 +66,16 @@ bool GpencilExporterSVG::write() bool result = true; /* Support unicode character paths on Windows. */ #ifdef WIN32 - char filename_cstr[FILE_MAX]; - BLI_strncpy(filename_cstr, filename_, FILE_MAX); + char filepath_cstr[FILE_MAX]; + BLI_strncpy(filepath_cstr, filepath_, FILE_MAX); - UTF16_ENCODE(filename_cstr); - std::wstring wstr(filename_cstr_16); + UTF16_ENCODE(filepath_cstr); + std::wstring wstr(filepath_cstr_16); result = main_doc_.save_file(wstr.c_str()); - UTF16_UN_ENCODE(filename_cstr); + UTF16_UN_ENCODE(filepath_cstr); #else - result = main_doc_.save_file(filename_); + result = main_doc_.save_file(filepath_); #endif return result; diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh b/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh index 2e1cc231e68..7bbc2710693 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh @@ -20,7 +20,7 @@ namespace blender::io::gpencil { class GpencilExporterSVG : public GpencilExporter { public: - GpencilExporterSVG(const char *filename, const struct GpencilIOParams *iparams); + GpencilExporterSVG(const char *filepath, const struct GpencilIOParams *iparams); bool add_newpage(); bool add_body(); bool write(); diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc index bad5c7d6401..06460a1beba 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc @@ -33,17 +33,17 @@ using blender::MutableSpan; namespace blender::io::gpencil { /* Constructor. */ -GpencilImporterSVG::GpencilImporterSVG(const char *filename, const GpencilIOParams *iparams) +GpencilImporterSVG::GpencilImporterSVG(const char *filepath, const GpencilIOParams *iparams) : GpencilImporter(iparams) { - filename_set(filename); + filepath_set(filepath); } bool GpencilImporterSVG::read() { bool result = true; NSVGimage *svg_data = nullptr; - svg_data = nsvgParseFromFile(filename_, "mm", 96.0f); + svg_data = nsvgParseFromFile(filepath_, "mm", 96.0f); if (svg_data == nullptr) { std::cout << " Could not open SVG.\n "; return false; diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh index f1f4e9a6290..d868e672567 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh @@ -21,7 +21,7 @@ namespace blender::io::gpencil { class GpencilImporterSVG : public GpencilImporter { public: - GpencilImporterSVG(const char *filename, const struct GpencilIOParams *iparams); + GpencilImporterSVG(const char *filepath, const struct GpencilIOParams *iparams); bool read(); diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 49d5251198a..e5083700d7d 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -391,7 +391,7 @@ bool USD_import(struct bContext *C, else { /* Fake a job context, so that we don't need NULL pointer checks while importing. */ short stop = 0, do_update = 0; - float progress = 0.f; + float progress = 0.0f; import_startjob(job, &stop, &do_update, &progress); import_endjob(job); diff --git a/source/blender/io/usd/intern/usd_reader_geom.h b/source/blender/io/usd/intern/usd_reader_geom.h index 6dcad2ffac8..b73279250f2 100644 --- a/source/blender/io/usd/intern/usd_reader_geom.h +++ b/source/blender/io/usd/intern/usd_reader_geom.h @@ -24,7 +24,7 @@ class USDGeomReader : public USDXformReader { int read_flag, const char **err_str) = 0; - virtual bool topology_changed(Mesh * /* existing_mesh */, double /* motionSampleTime */) + virtual bool topology_changed(const Mesh * /* existing_mesh */, double /* motionSampleTime */) { return true; } diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 1f97c7b48b3..646d1ba1fde 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -232,7 +232,7 @@ bool USDMeshReader::valid() const return static_cast<bool>(mesh_prim_); } -bool USDMeshReader::topology_changed(Mesh *existing_mesh, const double motionSampleTime) +bool USDMeshReader::topology_changed(const Mesh *existing_mesh, const double motionSampleTime) { /* TODO(makowalski): Is it the best strategy to cache the mesh * geometry in this function? This needs to be revisited. */ diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h index fdd814f56d4..5e33ce8b5e8 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.h +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -53,7 +53,7 @@ class USDMeshReader : public USDGeomReader { int read_flag, const char **err_str) override; - bool topology_changed(Mesh *existing_mesh, double motionSampleTime) override; + bool topology_changed(const Mesh *existing_mesh, double motionSampleTime) override; private: void process_normals_vertex_varying(Mesh *mesh); diff --git a/source/blender/io/usd/intern/usd_reader_volume.cc b/source/blender/io/usd/intern/usd_reader_volume.cc index aec30539ee6..13044de5002 100644 --- a/source/blender/io/usd/intern/usd_reader_volume.cc +++ b/source/blender/io/usd/intern/usd_reader_volume.cc @@ -54,18 +54,6 @@ void USDVolumeReader::read_object_data(Main *bmain, const double motionSampleTim pxr::UsdVolOpenVDBAsset fieldBase(fieldPrim); - pxr::UsdAttribute fieldNameAttr = fieldBase.GetFieldNameAttr(); - - if (fieldNameAttr.IsAuthored()) { - pxr::TfToken fieldName; - fieldNameAttr.Get(&fieldName, motionSampleTime); - - /* A Blender volume creates density by default. */ - if (fieldName != usdtokens::density) { - BKE_volume_grid_add(volume, fieldName.GetString().c_str(), VOLUME_GRID_FLOAT); - } - } - pxr::UsdAttribute filepathAttr = fieldBase.GetFilePathAttr(); if (filepathAttr.IsAuthored()) { diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index 09cb0d07493..29ab0479f6e 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -6,6 +6,7 @@ #include "usd_exporter_context.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_main.h" #include "BKE_node.h" @@ -335,7 +336,7 @@ static std::string get_in_memory_texture_filename(Image *ima) } ImageFormatData imageFormat; - BKE_imbuf_to_image_format(&imageFormat, imbuf); + BKE_image_format_from_imbuf(&imageFormat, imbuf); char file_name[FILE_MAX]; /* Use the image name for the file name. */ @@ -368,7 +369,7 @@ static void export_in_memory_texture(Image *ima, } ImageFormatData imageFormat; - BKE_imbuf_to_image_format(&imageFormat, imbuf); + BKE_image_format_from_imbuf(&imageFormat, imbuf); /* This image in its current state only exists in Blender memory. * So we have to export it. The export will keep the image state intact, diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 5631bdde2f8..acfdaa29b52 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -43,16 +43,33 @@ void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh, const IndexOffsets &offsets, Span<int> vert_indices, Span<int> uv_indices, - Span<int> normal_indices) const + Span<int> normal_indices, + bool flip) const { BLI_assert(vert_indices.size() == uv_indices.size() && vert_indices.size() == normal_indices.size()); + const int vertex_offset = offsets.vertex_offset + 1; + const int uv_offset = offsets.uv_vertex_offset + 1; + const int normal_offset = offsets.normal_offset + 1; + const int n = vert_indices.size(); fh.write<eOBJSyntaxElement::poly_element_begin>(); - for (int j = 0; j < vert_indices.size(); j++) { - fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>( - vert_indices[j] + offsets.vertex_offset + 1, - uv_indices[j] + offsets.uv_vertex_offset + 1, - normal_indices[j] + offsets.normal_offset + 1); + if (!flip) { + for (int j = 0; j < n; ++j) { + fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset, + uv_indices[j] + uv_offset, + normal_indices[j] + normal_offset); + } + } + else { + /* For a transform that is mirrored (negative scale on odd number of axes), + * we want to flip the face index order. Start from the same index, and + * then go backwards. Same logic in other write_*_indices functions below. */ + for (int k = 0; k < n; ++k) { + int j = k == 0 ? 0 : n - k; + fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset, + uv_indices[j] + uv_offset, + normal_indices[j] + normal_offset); + } } fh.write<eOBJSyntaxElement::poly_element_end>(); } @@ -61,14 +78,26 @@ void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh, const IndexOffsets &offsets, Span<int> vert_indices, Span<int> /*uv_indices*/, - Span<int> normal_indices) const + Span<int> normal_indices, + bool flip) const { BLI_assert(vert_indices.size() == normal_indices.size()); + const int vertex_offset = offsets.vertex_offset + 1; + const int normal_offset = offsets.normal_offset + 1; + const int n = vert_indices.size(); fh.write<eOBJSyntaxElement::poly_element_begin>(); - for (int j = 0; j < vert_indices.size(); j++) { - fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + offsets.vertex_offset + 1, - normal_indices[j] + offsets.normal_offset + - 1); + if (!flip) { + for (int j = 0; j < n; ++j) { + fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset, + normal_indices[j] + normal_offset); + } + } + else { + for (int k = 0; k < n; ++k) { + int j = k == 0 ? 0 : n - k; + fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset, + normal_indices[j] + normal_offset); + } } fh.write<eOBJSyntaxElement::poly_element_end>(); } @@ -77,13 +106,26 @@ void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh, const IndexOffsets &offsets, Span<int> vert_indices, Span<int> uv_indices, - Span<int> /*normal_indices*/) const + Span<int> /*normal_indices*/, + bool flip) const { BLI_assert(vert_indices.size() == uv_indices.size()); + const int vertex_offset = offsets.vertex_offset + 1; + const int uv_offset = offsets.uv_vertex_offset + 1; + const int n = vert_indices.size(); fh.write<eOBJSyntaxElement::poly_element_begin>(); - for (int j = 0; j < vert_indices.size(); j++) { - fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + offsets.vertex_offset + 1, - uv_indices[j] + offsets.uv_vertex_offset + 1); + if (!flip) { + for (int j = 0; j < n; ++j) { + fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset, + uv_indices[j] + uv_offset); + } + } + else { + for (int k = 0; k < n; ++k) { + int j = k == 0 ? 0 : n - k; + fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset, + uv_indices[j] + uv_offset); + } } fh.write<eOBJSyntaxElement::poly_element_end>(); } @@ -92,11 +134,22 @@ void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh, const IndexOffsets &offsets, Span<int> vert_indices, Span<int> /*uv_indices*/, - Span<int> /*normal_indices*/) const + Span<int> /*normal_indices*/, + bool flip) const { + const int vertex_offset = offsets.vertex_offset + 1; + const int n = vert_indices.size(); fh.write<eOBJSyntaxElement::poly_element_begin>(); - for (const int vert_index : vert_indices) { - fh.write<eOBJSyntaxElement::vertex_indices>(vert_index + offsets.vertex_offset + 1); + if (!flip) { + for (int j = 0; j < n; ++j) { + fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset); + } + } + else { + for (int k = 0; k < n; ++k) { + int j = k == 0 ? 0 : n - k; + fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset); + } } fh.write<eOBJSyntaxElement::poly_element_end>(); } @@ -121,34 +174,14 @@ void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const fh.write_to_file(outfile_); } -void OBJWriter::write_object_group(FormatHandler<eFileType::OBJ> &fh, - const OBJMesh &obj_mesh_data) const -{ - /* "o object_name" is not mandatory. A valid .OBJ file may contain neither - * "o name" nor "g group_name". */ - BLI_assert(export_params_.export_object_groups); - if (!export_params_.export_object_groups) { - return; - } - const std::string object_name = obj_mesh_data.get_object_name(); - const char *object_mesh_name = obj_mesh_data.get_object_mesh_name(); - const char *object_material_name = obj_mesh_data.get_object_material_name(0); - if (export_params_.export_materials && export_params_.export_material_groups && - object_material_name) { - fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name + "_" + - object_material_name); - } - else { - fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name); - } -} - void OBJWriter::write_object_name(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const { const char *object_name = obj_mesh_data.get_object_name(); if (export_params_.export_object_groups) { - write_object_group(fh, obj_mesh_data); + const std::string object_name = obj_mesh_data.get_object_name(); + const char *mesh_name = obj_mesh_data.get_object_mesh_name(); + fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mesh_name); return; } fh.write<eOBJSyntaxElement::object_name>(object_name); @@ -275,14 +308,20 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh, obj_mesh_data.tot_uv_vertices()); const int tot_polygons = obj_mesh_data.tot_polygons(); - obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int i) { + obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int idx) { + /* Polygon order for writing into the file is not necessarily the same + * as order in the mesh; it will be sorted by material indices. Remap current + * and previous indices here according to the order. */ + int prev_i = obj_mesh_data.remap_poly_index(idx - 1); + int i = obj_mesh_data.remap_poly_index(idx); + Vector<int> poly_vertex_indices = obj_mesh_data.calc_poly_vertex_indices(i); Span<int> poly_uv_indices = obj_mesh_data.calc_poly_uv_indices(i); Vector<int> poly_normal_indices = obj_mesh_data.calc_poly_normal_indices(i); /* Write smoothing group if different from previous. */ { - const int prev_group = get_smooth_group(obj_mesh_data, export_params_, i - 1); + const int prev_group = get_smooth_group(obj_mesh_data, export_params_, prev_i); const int group = get_smooth_group(obj_mesh_data, export_params_, i); if (group != prev_group) { buf.write<eOBJSyntaxElement::smooth_group>(group); @@ -291,8 +330,8 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh, /* Write vertex group if different from previous. */ if (export_params_.export_vertex_groups) { - const int16_t prev_group = i == 0 ? NEGATIVE_INIT : - obj_mesh_data.get_poly_deform_group_index(i - 1); + const int16_t prev_group = idx == 0 ? NEGATIVE_INIT : + obj_mesh_data.get_poly_deform_group_index(prev_i); const int16_t group = obj_mesh_data.get_poly_deform_group_index(i); if (group != prev_group) { buf.write<eOBJSyntaxElement::object_group>( @@ -303,28 +342,33 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh, /* Write material name and material group if different from previous. */ if (export_params_.export_materials && obj_mesh_data.tot_materials() > 0) { - const int16_t prev_mat = i == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(i - 1); + const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(prev_i); const int16_t mat = obj_mesh_data.ith_poly_matnr(i); if (mat != prev_mat) { if (mat == NOT_FOUND) { buf.write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED); } else { - if (export_params_.export_object_groups) { - write_object_group(buf, obj_mesh_data); - } const char *mat_name = matname_fn(mat); if (!mat_name) { mat_name = MATERIAL_GROUP_DISABLED; } + if (export_params_.export_material_groups) { + const std::string object_name = obj_mesh_data.get_object_name(); + fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mat_name); + } buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name); } } } /* Write polygon elements. */ - (this->*poly_element_writer)( - buf, offsets, poly_vertex_indices, poly_uv_indices, poly_normal_indices); + (this->*poly_element_writer)(buf, + offsets, + poly_vertex_indices, + poly_uv_indices, + poly_normal_indices, + obj_mesh_data.is_mirrored_transform()); }); } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 6048c6d8d7b..96f7d434338 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -67,10 +67,6 @@ class OBJWriter : NonMovable, NonCopyable { */ void write_object_name(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const; /** - * Write an object's group with mesh and/or material name appended conditionally. - */ - void write_object_group(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const; - /** * Write file name of Material Library in .OBJ file. */ void write_mtllib_name(const StringRefNull mtl_filepath) const; @@ -115,7 +111,8 @@ class OBJWriter : NonMovable, NonCopyable { const IndexOffsets &offsets, Span<int> vert_indices, Span<int> uv_indices, - Span<int> normal_indices) const; + Span<int> normal_indices, + bool flip) const; /** * \return Writer function with appropriate polygon-element syntax. */ @@ -128,7 +125,8 @@ class OBJWriter : NonMovable, NonCopyable { const IndexOffsets &offsets, Span<int> vert_indices, Span<int> uv_indices, - Span<int> normal_indices) const; + Span<int> normal_indices, + bool flip) const; /** * Write one line of polygon indices as "f v1//vn1 v2//vn2 ...". */ @@ -136,7 +134,8 @@ class OBJWriter : NonMovable, NonCopyable { const IndexOffsets &offsets, Span<int> vert_indices, Span<int> /*uv_indices*/, - Span<int> normal_indices) const; + Span<int> normal_indices, + bool flip) const; /** * Write one line of polygon indices as "f v1/vt1 v2/vt2 ...". */ @@ -144,7 +143,8 @@ class OBJWriter : NonMovable, NonCopyable { const IndexOffsets &offsets, Span<int> vert_indices, Span<int> uv_indices, - Span<int> /*normal_indices*/) const; + Span<int> /*normal_indices*/, + bool flip) const; /** * Write one line of polygon indices as "f v1 v2 ...". */ @@ -152,7 +152,8 @@ class OBJWriter : NonMovable, NonCopyable { const IndexOffsets &offsets, Span<int> vert_indices, Span<int> /*uv_indices*/, - Span<int> /*normal_indices*/) const; + Span<int> /*normal_indices*/, + bool flip) const; }; /** diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index b730c93bb7b..aa63e65b1e8 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -17,6 +17,7 @@ #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_math.h" +#include "BLI_sort.hh" #include "DEG_depsgraph_query.h" @@ -76,6 +77,7 @@ void OBJMesh::clear() uv_coords_.clear_and_make_inline(); loop_to_normal_index_.clear_and_make_inline(); normal_coords_.clear_and_make_inline(); + poly_order_.clear_and_make_inline(); if (poly_smooth_groups_) { MEM_freeN(poly_smooth_groups_); poly_smooth_groups_ = nullptr; @@ -87,8 +89,13 @@ std::pair<Mesh *, bool> OBJMesh::triangulate_mesh_eval() if (export_mesh_eval_->totpoly <= 0) { return {export_mesh_eval_, false}; } - const struct BMeshCreateParams bm_create_params = {0u}; - const struct BMeshFromMeshParams bm_convert_params = {1u, 0, 0, 0}; + const BMeshCreateParams bm_create_params = {false}; + BMeshFromMeshParams bm_convert_params{}; + bm_convert_params.calc_face_normal = true; + bm_convert_params.calc_vert_normal = true; + bm_convert_params.add_key_index = false; + bm_convert_params.use_shapekey = false; + /* Lower threshold where triangulation of a polygon starts, i.e. a quadrilateral will be * triangulated here. */ const int triangulate_min_verts = 4; @@ -123,6 +130,13 @@ void OBJMesh::set_world_axes_transform(const eTransformAxisForward forward, /* mul_m4_m3m4 does not transform last row of obmat, i.e. location data. */ mul_v3_m3v3(world_and_axes_transform_[3], axes_transform, export_object_eval_.obmat[3]); world_and_axes_transform_[3][3] = export_object_eval_.obmat[3][3]; + + /* Normals need inverse transpose of the regular matrix to handle non-uniform scale. */ + float normal_matrix[3][3]; + copy_m3_m4(normal_matrix, world_and_axes_transform_); + invert_m3_m3(world_and_axes_normal_transform_, normal_matrix); + transpose_m3(world_and_axes_normal_transform_); + mirrored_transform_ = determinant_m3_array(world_and_axes_normal_transform_) < 0; } int OBJMesh::tot_vertices() const @@ -186,6 +200,25 @@ void OBJMesh::calc_smooth_groups(const bool use_bitflags) use_bitflags); } +void OBJMesh::calc_poly_order() +{ + const int tot_polys = tot_polygons(); + poly_order_.resize(tot_polys); + for (int i = 0; i < tot_polys; ++i) { + poly_order_[i] = i; + } + const MPoly *mpolys = export_mesh_eval_->mpoly; + /* Sort polygons by their material index. */ + blender::parallel_sort(poly_order_.begin(), poly_order_.end(), [&](int a, int b) { + int mat_a = mpolys[a].mat_nr; + int mat_b = mpolys[b].mat_nr; + if (mat_a != mat_b) { + return mat_a < mat_b; + } + return a < b; + }); +} + const Material *OBJMesh::get_object_material(const int16_t mat_nr) const { /** @@ -196,11 +229,6 @@ const Material *OBJMesh::get_object_material(const int16_t mat_nr) const */ Object *obj = const_cast<Object *>(&export_object_eval_); const Material *r_mat = BKE_object_material_get(obj, mat_nr + 1); -#ifdef DEBUG - if (!r_mat) { - std::cerr << "Material not found for mat_nr = " << mat_nr << std::endl; - } -#endif return r_mat; } @@ -239,8 +267,8 @@ float3 OBJMesh::calc_vertex_coords(const int vert_index, const float scaling_fac { float3 r_coords; copy_v3_v3(r_coords, export_mesh_eval_->mvert[vert_index].co); - mul_v3_fl(r_coords, scaling_factor); mul_m4_v3(world_and_axes_transform_, r_coords); + mul_v3_fl(r_coords, scaling_factor); return r_coords; } @@ -319,7 +347,8 @@ float3 OBJMesh::calc_poly_normal(const int poly_index) const const MLoop &mloop = export_mesh_eval_->mloop[poly.loopstart]; const MVert &mvert = *(export_mesh_eval_->mvert); BKE_mesh_calc_poly_normal(&poly, &mloop, &mvert, r_poly_normal); - mul_mat3_m4_v3(world_and_axes_transform_, r_poly_normal); + mul_m3_v3(world_and_axes_normal_transform_, r_poly_normal); + normalize_v3(r_poly_normal); return r_poly_normal; } @@ -364,7 +393,8 @@ void OBJMesh::store_normal_coords_and_indices() int loop_index = mpoly.loopstart + loop_of_poly; BLI_assert(loop_index < export_mesh_eval_->totloop); copy_v3_v3(loop_normal, lnors[loop_index]); - mul_mat3_m4_v3(world_and_axes_transform_, loop_normal); + mul_m3_v3(world_and_axes_normal_transform_, loop_normal); + normalize_v3(loop_normal); float3 rounded_loop_normal = round_float3_to_n_digits(loop_normal, round_digits); int loop_norm_index = normal_to_index.lookup_default(rounded_loop_normal, -1); if (loop_norm_index == -1) { diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index 5b11c85b7a4..7650a220810 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -59,6 +59,8 @@ class OBJMesh : NonCopyable { * object's world transform matrix. */ float world_and_axes_transform_[4][4]; + float world_and_axes_normal_transform_[3][3]; + bool mirrored_transform_; /** * Total UV vertices in a mesh's texture map. @@ -93,6 +95,10 @@ class OBJMesh : NonCopyable { * Polygon aligned array of their smooth groups. */ int *poly_smooth_groups_ = nullptr; + /** + * Order in which the polygons should be written into the file (sorted by material index). + */ + Vector<int> poly_order_; public: /** @@ -110,6 +116,10 @@ class OBJMesh : NonCopyable { int tot_uv_vertices() const; int tot_normal_indices() const; int tot_edges() const; + bool is_mirrored_transform() const + { + return mirrored_transform_; + } /** * \return Total materials in the object. @@ -212,6 +222,22 @@ class OBJMesh : NonCopyable { */ std::optional<std::array<int, 2>> calc_loose_edge_vert_indices(int edge_index) const; + /** + * Calculate the order in which the polygons should be written into the file (sorted by material + * index). + */ + void calc_poly_order(); + + /** + * Remap polygon index according to polygon writing order. + * When materials are not being written, the polygon order array + * might be empty, in which case remap is a no-op. + */ + int remap_poly_index(int i) const + { + return i < 0 || i >= poly_order_.size() ? i : poly_order_[i]; + } + private: /** * Free the mesh if _the exporter_ created it. diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 2cef5192337..30670d45ef7 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -204,6 +204,9 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me if (export_params.export_smooth_groups) { obj.calc_smooth_groups(export_params.smooth_groups_bitflags); } + if (export_params.export_materials) { + obj.calc_poly_order(); + } if (export_params.export_normals) { obj_writer.write_poly_normals(fh, obj); } diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index cc1cca74a38..b8fecfb25f3 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -405,6 +405,16 @@ TEST_F(obj_exporter_regression_test, vertices) "io_tests/blend_geometry/vertices.blend", "io_tests/obj/vertices.obj", "", _export.params); } +TEST_F(obj_exporter_regression_test, non_uniform_scale) +{ + OBJExportParamsDefault _export; + _export.params.export_materials = false; + compare_obj_export_to_golden("io_tests/blend_geometry/non_uniform_scale.blend", + "io_tests/obj/non_uniform_scale.obj", + "", + _export.params); +} + TEST_F(obj_exporter_regression_test, nurbs_as_nurbs) { OBJExportParamsDefault _export; @@ -465,6 +475,17 @@ TEST_F(obj_exporter_regression_test, cube_normal_edit) _export.params); } +TEST_F(obj_exporter_regression_test, cubes_positioned) +{ + OBJExportParamsDefault _export; + _export.params.export_materials = false; + _export.params.scaling_factor = 2.0f; + compare_obj_export_to_golden("io_tests/blend_geometry/cubes_positioned.blend", + "io_tests/obj/cubes_positioned.obj", + "", + _export.params); +} + TEST_F(obj_exporter_regression_test, suzanne_all_data) { OBJExportParamsDefault _export; @@ -490,4 +511,17 @@ TEST_F(obj_exporter_regression_test, all_objects) _export.params); } +TEST_F(obj_exporter_regression_test, all_objects_mat_groups) +{ + OBJExportParamsDefault _export; + _export.params.forward_axis = OBJ_AXIS_Y_FORWARD; + _export.params.up_axis = OBJ_AXIS_Z_UP; + _export.params.export_smooth_groups = true; + _export.params.export_material_groups = true; + compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend", + "io_tests/obj/all_objects_mat_groups.obj", + "io_tests/obj/all_objects_mat_groups.mtl", + _export.params); +} + } // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index f8ffe6a0a47..783e79898ce 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -460,8 +460,9 @@ typedef enum eBrushCurvesSculptTool { CURVES_SCULPT_TOOL_COMB = 0, CURVES_SCULPT_TOOL_DELETE = 1, CURVES_SCULPT_TOOL_SNAKE_HOOK = 2, - CURVES_SCULPT_TOOL_TEST1 = 3, - CURVES_SCULPT_TOOL_TEST2 = 4, + CURVES_SCULPT_TOOL_ADD = 3, + CURVES_SCULPT_TOOL_TEST1 = 4, + CURVES_SCULPT_TOOL_TEST2 = 5, } eBrushCurvesSculptTool; /** When #BRUSH_ACCUMULATE is used */ @@ -602,10 +603,10 @@ typedef enum eBlurKernelType { } eBlurKernelType; /* Brush.falloff_shape */ -enum { +typedef enum eBrushFalloffShape { PAINT_FALLOFF_SHAPE_SPHERE = 0, PAINT_FALLOFF_SHAPE_TUBE = 1, -}; +} eBrushFalloffShape; #define MAX_BRUSH_PIXEL_RADIUS 500 #define GP_MAX_BRUSH_PIXEL_RADIUS 1000 diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index fe80220b1dd..564f34b1f72 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -137,6 +137,11 @@ typedef struct BrushGpencilSettings { struct Material *material; } BrushGpencilSettings; +typedef struct BrushCurvesSculptSettings { + /* Number of curves added by the add brush. */ + int add_amount; +} BrushCurvesSculptSettings; + typedef struct Brush { ID id; @@ -256,7 +261,7 @@ typedef struct Brush { char gpencil_sculpt_tool; /** Active grease pencil weight tool. */ char gpencil_weight_tool; - /** Active curves sculpt tool. */ + /** Active curves sculpt tool (#eBrushCurvesSculptTool). */ char curves_sculpt_tool; char _pad1[5]; @@ -360,7 +365,7 @@ typedef struct Brush { float mask_stencil_dimension[2]; struct BrushGpencilSettings *gpencil_settings; - + struct BrushCurvesSculptSettings *curves_sculpt_settings; } Brush; /* Struct to hold palette colors for sorting. */ diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 2f9f63cb966..56e223cda5f 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -52,7 +52,7 @@ typedef struct CustomDataLayer { typedef struct CustomDataExternal { /** FILE_MAX. */ - char filename[1024]; + char filepath[1024]; } CustomDataExternal; /** diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 3e06259dfd5..a87e7a7c397 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -366,5 +366,14 @@ .smooth_step = 1, \ } +#define _DNA_DEFAULT_EnvelopeGpencilModifierData \ + { \ + .spread = 10, \ + .mode = GP_ENVELOPE_SEGMENTS, \ + .mat_nr = -1, \ + .thickness = 1.0f, \ + .strength = 1.0f, \ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 0539b84e093..1054c309ee5 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -46,6 +46,7 @@ typedef enum GpencilModifierType { eGpencilModifierType_Dash = 22, eGpencilModifierType_WeightAngle = 23, eGpencilModifierType_Shrinkwrap = 24, + eGpencilModifierType_Envelope = 25, /* Keep last. */ NUM_GREASEPENCIL_MODIFIER_TYPES, } GpencilModifierType; @@ -1127,6 +1128,46 @@ typedef enum eShrinkwrapGpencil_Flag { GP_SHRINKWRAP_INVERT_VGROUP = (1 << 6), } eShrinkwrapGpencil_Flag; +typedef struct EnvelopeGpencilModifierData { + GpencilModifierData modifier; + /** Material for filtering. */ + struct Material *material; + /** Layer name. */ + char layername[64]; + /** Optional vertexgroup name, MAX_VGROUP_NAME. */ + char vgname[64]; + /** Custom index for passes. */ + int pass_index; + /** Several flags. */ + int flag; + int mode; + /** Material for the new strokes. */ + int mat_nr; + /** Thickness multiplier for the new strokes. */ + float thickness; + /** Strength multiplier for the new strokes. */ + float strength; + /** Custom index for passes. */ + int layer_pass; + /* Length of the envelope effect. */ + int spread; +} EnvelopeGpencilModifierData; + +typedef enum eEnvelopeGpencil_Flag { + GP_ENVELOPE_INVERT_LAYER = (1 << 0), + GP_ENVELOPE_INVERT_PASS = (1 << 1), + GP_ENVELOPE_INVERT_VGROUP = (1 << 2), + GP_ENVELOPE_INVERT_LAYERPASS = (1 << 3), + GP_ENVELOPE_INVERT_MATERIAL = (1 << 4), +} eEnvelopeGpencil_Flag; + +/* Texture->mode */ +typedef enum eEnvelopeGpencil_Mode { + GP_ENVELOPE_DEFORM = 0, + GP_ENVELOPE_SEGMENTS = 1, + GP_ENVELOPE_FILLS = 2, +} eEnvelopeGpencil_Mode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index fe3613548a6..8a30a7e8c99 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -25,8 +25,6 @@ struct MDeformVert; #define GP_DEFAULT_GRID_LINES 4 #define GP_MAX_INPUT_SAMPLES 10 -#define GP_MATERIAL_BUFFER_LEN 256 - #define GP_DEFAULT_CURVE_RESOLUTION 32 #define GP_DEFAULT_CURVE_ERROR 0.1f #define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2 diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 465ec6e7db5..7b6fe3773f8 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -113,7 +113,7 @@ typedef struct ViewLayerEEVEE { int _pad[1]; } ViewLayerEEVEE; -/* AOV Renderpass definition. */ +/** AOV Render-pass definition. */ typedef struct ViewLayerAOV { struct ViewLayerAOV *next, *prev; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 5bd44868741..99d7e15fa7a 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -761,7 +761,7 @@ typedef struct NodeImageLayer { /* index in the Image->layers->passes lists */ int pass_index DNA_DEPRECATED; /* render pass name */ - /** Amount defined in openexr_multi.h. */ + /** Amount defined in IMB_openexr.h. */ char pass_name[64]; } NodeImageLayer; @@ -1210,53 +1210,6 @@ typedef struct NodeMapRange { char _pad[5]; } NodeMapRange; -typedef struct NodeAttributeClamp { - /* CustomDataType. */ - uint8_t data_type; - - /* NodeClampOperation. */ - uint8_t operation; -} NodeAttributeClamp; - -typedef struct NodeAttributeCompare { - /* FloatCompareOperation. */ - uint8_t operation; - - /* GeometryNodeAttributeInputMode */ - uint8_t input_type_a; - uint8_t input_type_b; - - char _pad[5]; -} NodeAttributeCompare; - -typedef struct NodeAttributeMapRange { - /* GeometryNodeAttributeDataType */ - uint8_t data_type; - - /* NodeMapRangeType. */ - uint8_t interpolation_type; -} NodeAttributeMapRange; - -typedef struct NodeAttributeMath { - /* NodeMathOperation. */ - uint8_t operation; - - /* GeometryNodeAttributeInputMode */ - uint8_t input_type_a; - uint8_t input_type_b; - uint8_t input_type_c; -} NodeAttributeMath; - -typedef struct NodeAttributeMix { - /* e.g. MA_RAMP_BLEND. */ - uint8_t blend_type; - - /* GeometryNodeAttributeInputMode */ - uint8_t input_type_factor; - uint8_t input_type_a; - uint8_t input_type_b; -} NodeAttributeMix; - typedef struct NodeRandomValue { /* CustomDataType. */ uint8_t data_type; @@ -1269,51 +1222,6 @@ typedef struct NodeAccumulateField { uint8_t domain; } NodeAccumulateField; -typedef struct NodeAttributeRandomize { - /* CustomDataType. */ - uint8_t data_type; - /* AttributeDomain. */ - uint8_t domain; - /* GeometryNodeAttributeRandomizeMode. */ - uint8_t operation; - char _pad[1]; -} NodeAttributeRandomize; - -typedef struct NodeAttributeVectorMath { - /* NodeVectorMathOperation */ - uint8_t operation; - - /* GeometryNodeAttributeInputMode */ - uint8_t input_type_a; - uint8_t input_type_b; - uint8_t input_type_c; -} NodeAttributeVectorMath; - -typedef struct NodeAttributeVectorRotate { - /* GeometryNodeAttributeVectorRotateMode */ - uint8_t mode; - - /* GeometryNodeAttributeInputMode */ - uint8_t input_type_vector; - uint8_t input_type_center; - uint8_t input_type_axis; - uint8_t input_type_angle; - uint8_t input_type_rotation; - char _pad[2]; -} NodeAttributeVectorRotate; - -typedef struct NodeAttributeColorRamp { - ColorBand color_ramp; -} NodeAttributeColorRamp; - -typedef struct NodeAttributeCurveMap { - /* CustomDataType. */ - uint8_t data_type; - char _pad[7]; - CurveMapping *curve_vec; - CurveMapping *curve_rgb; -} NodeAttributeCurveMap; - typedef struct NodeInputBool { uint8_t boolean; } NodeInputBool; @@ -1334,40 +1242,6 @@ typedef struct NodeInputString { char *string; } NodeInputString; -typedef struct NodeGeometryRotatePoints { - /* GeometryNodeRotatePointsType */ - uint8_t type; - /* GeometryNodeRotatePointsSpace */ - uint8_t space; - - /* GeometryNodeAttributeInputMode */ - uint8_t input_type_axis; - uint8_t input_type_angle; - uint8_t input_type_rotation; - char _pad[3]; -} NodeGeometryRotatePoints; - -typedef struct NodeGeometryAlignRotationToVector { - /* GeometryNodeAlignRotationToVectorAxis */ - uint8_t axis; - /* GeometryNodeAlignRotationToVectorPivotAxis */ - uint8_t pivot_axis; - - /* GeometryNodeAttributeInputMode */ - uint8_t input_type_factor; - uint8_t input_type_vector; -} NodeGeometryAlignRotationToVector; - -typedef struct NodeGeometryPointScale { - /* GeometryNodeAttributeInputMode */ - uint8_t input_type; -} NodeGeometryPointScale; - -typedef struct NodeGeometryPointTranslate { - /* GeometryNodeAttributeInputMode */ - uint8_t input_type; -} NodeGeometryPointTranslate; - typedef struct NodeGeometryExtrudeMesh { /* GeometryNodeExtrudeMeshMode */ uint8_t mode; @@ -1378,13 +1252,6 @@ typedef struct NodeGeometryObjectInfo { uint8_t transform_space; } NodeGeometryObjectInfo; -typedef struct NodeGeometryPointInstance { - /* GeometryNodePointInstanceType. */ - uint8_t instance_type; - /* GeometryNodePointInstanceFlag. */ - uint8_t flag; -} NodeGeometryPointInstance; - typedef struct NodeGeometryPointsToVolume { /* GeometryNodePointsToVolumeResolutionMode */ uint8_t resolution_mode; @@ -1397,11 +1264,6 @@ typedef struct NodeGeometryCollectionInfo { uint8_t transform_space; } NodeGeometryCollectionInfo; -typedef struct NodeGeometryAttributeProximity { - /* GeometryNodeAttributeProximityTargetType. */ - uint8_t target_geometry_element; -} NodeGeometryAttributeProximity; - typedef struct NodeGeometryProximity { /* GeometryNodeProximityTargetType. */ uint8_t target_element; @@ -1412,27 +1274,6 @@ typedef struct NodeGeometryVolumeToMesh { uint8_t resolution_mode; } NodeGeometryVolumeToMesh; -typedef struct NodeAttributeCombineXYZ { - /* GeometryNodeAttributeInputMode. */ - uint8_t input_type_x; - uint8_t input_type_y; - uint8_t input_type_z; - - char _pad[1]; -} NodeAttributeCombineXYZ; - -typedef struct NodeAttributeSeparateXYZ { - /* GeometryNodeAttributeInputMode. */ - uint8_t input_type; -} NodeAttributeSeparateXYZ; - -typedef struct NodeAttributeConvert { - /* CustomDataType. */ - int8_t data_type; - /* AttributeDomain. */ - int8_t domain; -} NodeAttributeConvert; - typedef struct NodeGeometrySubdivisionSurface { /* eSubsurfUVSmooth. */ uint8_t uv_smooth; @@ -1521,11 +1362,6 @@ typedef struct NodeGeometryCurveResample { uint8_t mode; } NodeGeometryCurveResample; -typedef struct NodeGeometryCurveSubdivide { - /* GeometryNodeAttributeInputMode (integer or attribute). */ - uint8_t cuts_type; -} NodeGeometryCurveSubdivide; - typedef struct NodeGeometryCurveFillet { /* GeometryNodeCurveFilletMode. */ uint8_t mode; @@ -1546,13 +1382,6 @@ typedef struct NodeGeometryCurveSample { uint8_t mode; } NodeGeometryCurveSample; -typedef struct NodeGeometryAttributeTransfer { - /* AttributeDomain. */ - int8_t domain; - /* GeometryNodeAttributeTransferMapMode. */ - uint8_t mapping; -} NodeGeometryAttributeTransfer; - typedef struct NodeGeometryTransferAttribute { /* CustomDataType. */ int8_t data_type; @@ -2080,12 +1909,6 @@ typedef enum NodeShaderOutputTarget { /* Geometry Nodes */ -typedef enum GeometryNodeAttributeProximityTargetType { - GEO_NODE_PROXIMITY_TARGET_POINTS = 0, - GEO_NODE_PROXIMITY_TARGET_EDGES = 1, - GEO_NODE_PROXIMITY_TARGET_FACES = 2, -} GeometryNodeAttributeProximityTargetType; - typedef enum GeometryNodeProximityTargetType { GEO_NODE_PROX_TARGET_POINTS = 0, GEO_NODE_PROX_TARGET_EDGES = 1, @@ -2134,30 +1957,6 @@ typedef enum GeometryNodeTriangulateQuads { GEO_NODE_TRIANGULATE_QUAD_LONGEDGE = 4, } GeometryNodeTriangulateQuads; -typedef enum GeometryNodePointInstanceType { - GEO_NODE_POINT_INSTANCE_TYPE_OBJECT = 0, - GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1, - GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY = 2, -} GeometryNodePointInstanceType; - -typedef enum GeometryNodePointInstanceFlag { - GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION = (1 << 0), -} GeometryNodePointInstanceFlag; - -typedef enum GeometryNodeAttributeInputMode { - GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE = 0, - GEO_NODE_ATTRIBUTE_INPUT_FLOAT = 1, - GEO_NODE_ATTRIBUTE_INPUT_VECTOR = 2, - GEO_NODE_ATTRIBUTE_INPUT_COLOR = 3, - GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN = 4, - GEO_NODE_ATTRIBUTE_INPUT_INTEGER = 5, -} GeometryNodeAttributeInputMode; - -typedef enum GeometryNodePointDistributeMode { - GEO_NODE_POINT_DISTRIBUTE_RANDOM = 0, - GEO_NODE_POINT_DISTRIBUTE_POISSON = 1, -} GeometryNodePointDistributeMode; - typedef enum GeometryNodeDistributePointsOnFacesMode { GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM = 0, GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1, @@ -2169,54 +1968,16 @@ typedef enum GeometryNodeExtrudeMeshMode { GEO_NODE_EXTRUDE_MESH_FACES = 2, } GeometryNodeExtrudeMeshMode; -typedef enum GeometryNodeRotatePointsType { - GEO_NODE_POINT_ROTATE_TYPE_EULER = 0, - GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1, -} GeometryNodeRotatePointsType; - -typedef enum FunctionNodeRotatePointsType { +typedef enum FunctionNodeRotateEulerType { FN_NODE_ROTATE_EULER_TYPE_EULER = 0, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE = 1, -} FunctionNodeRotatePointsType; - -typedef enum GeometryNodeAttributeVectorRotateMode { - GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0, - GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1, - GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y = 2, - GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z = 3, - GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ = 4, -} GeometryNodeAttributeVectorRotateMode; - -typedef enum GeometryNodeAttributeRandomizeMode { - GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE = 0, - GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD = 1, - GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT = 2, - GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY = 3, -} GeometryNodeAttributeRandomizeMode; - -typedef enum GeometryNodeRotatePointsSpace { - GEO_NODE_POINT_ROTATE_SPACE_OBJECT = 0, - GEO_NODE_POINT_ROTATE_SPACE_POINT = 1, -} GeometryNodeRotatePointsSpace; +} FunctionNodeRotateEulerType; typedef enum FunctionNodeRotateEulerSpace { FN_NODE_ROTATE_EULER_SPACE_OBJECT = 0, FN_NODE_ROTATE_EULER_SPACE_LOCAL = 1, } FunctionNodeRotateEulerSpace; -typedef enum GeometryNodeAlignRotationToVectorAxis { - GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X = 0, - GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Y = 1, - GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Z = 2, -} GeometryNodeAlignRotationToVectorAxis; - -typedef enum GeometryNodeAlignRotationToVectorPivotAxis { - GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO = 0, - GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_X = 1, - GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Y = 2, - GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Z = 3, -} GeometryNodeAlignRotationToVectorPivotAxis; - typedef enum NodeAlignEulerToVectorAxis { FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_X = 0, FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Y = 1, @@ -2295,11 +2056,6 @@ typedef enum GeometryNodeCurveFilletMode { GEO_NODE_CURVE_FILLET_POLY = 1, } GeometryNodeCurveFilletMode; -typedef enum GeometryNodeAttributeTransferMapMode { - GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, - GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST = 1, -} GeometryNodeAttributeTransferMapMode; - typedef enum GeometryNodeAttributeTransferMode { GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, diff --git a/source/blender/makesdna/DNA_particle_defaults.h b/source/blender/makesdna/DNA_particle_defaults.h index 138aa6f1f98..108252a5e6c 100644 --- a/source/blender/makesdna/DNA_particle_defaults.h +++ b/source/blender/makesdna/DNA_particle_defaults.h @@ -56,8 +56,8 @@ .rotmode = PART_ROT_VEL, \ .avemode = PART_AVE_VELOCITY, \ \ - .child_nbr = 10, \ - .ren_child_nbr = 100, \ + .child_percent = 10, \ + .child_render_percent = 100, \ .childrad = 0.2f, \ .childflat = 0.0f, \ .clumppow = 0.0f, \ diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index 397385c0f5b..f3d342e4849 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -226,7 +226,7 @@ typedef struct ParticleSettings { /* children */ int child_flag; char _pad3[4]; - int child_nbr, ren_child_nbr; + int child_percent, child_render_percent; float parents, childsize, childrandsize; float childrad, childflat; /* clumping */ diff --git a/source/blender/makesdna/DNA_pointcache_types.h b/source/blender/makesdna/DNA_pointcache_types.h index b247176f537..1133237199b 100644 --- a/source/blender/makesdna/DNA_pointcache_types.h +++ b/source/blender/makesdna/DNA_pointcache_types.h @@ -131,7 +131,7 @@ enum { PTCACHE_FRAMES_SKIPPED = 1 << 8, PTCACHE_EXTERNAL = 1 << 9, PTCACHE_READ_INFO = 1 << 10, - /** Don't use the filename of the blend-file the data is linked from (write a local cache). */ + /** Don't use the file-path of the blend-file the data is linked from (write a local cache). */ PTCACHE_IGNORE_LIBPATH = 1 << 11, /** * High resolution cache is saved for smoke for backwards compatibility, diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 2f8d1c5eafe..e6b2f2769b8 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -429,8 +429,11 @@ typedef struct ImageFormatData { Stereo3dFormat stereo3d_format; /* color management */ + char color_management; + char _pad1[7]; ColorManagedViewSettings view_settings; ColorManagedDisplaySettings display_settings; + ColorManagedColorspaceSettings linear_colorspace_settings; } ImageFormatData; /** #ImageFormatData.imtype */ @@ -462,12 +465,13 @@ typedef struct ImageFormatData { #define R_IMF_IMTYPE_XVID 32 #define R_IMF_IMTYPE_THEORA 33 #define R_IMF_IMTYPE_PSD 34 +#define R_IMF_IMTYPE_WEBP 35 #define R_IMF_IMTYPE_INVALID 255 /** #ImageFormatData.flag */ -#define R_IMF_FLAG_ZBUF (1 << 0) /* was R_OPENEXR_ZBUF */ -#define R_IMF_FLAG_PREVIEW_JPG (1 << 1) /* was R_PREVIEW_JPG */ +#define R_IMF_FLAG_ZBUF (1 << 0) +#define R_IMF_FLAG_PREVIEW_JPG (1 << 1) /* Return values from #BKE_imtype_valid_depths, note this is depths per channel. */ /** #ImageFormatData.depth */ @@ -526,6 +530,10 @@ enum { R_IMF_TIFF_CODEC_NONE = 3, }; +/** #ImageFormatData.color_management */ +#define R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE 0 +#define R_IMF_COLOR_MANAGEMENT_OVERRIDE 1 + typedef struct BakeData { struct ImageFormatData im_format; @@ -992,11 +1000,23 @@ typedef struct Sculpt { struct Object *gravity_object; } Sculpt; +typedef enum CurvesSculptFlag { + CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 0), + CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 1), +} CurvesSculptFlag; + typedef struct CurvesSculpt { Paint paint; /** Minimum distance between newly added curves on a surface. */ float distance; - char _pad1[4]; + + /** CurvesSculptFlag. */ + uint32_t flag; + + /** Length of newly added curves when it is not interpolated from other curves. */ + float curve_length; + + char _pad[4]; } CurvesSculpt; typedef struct UvSculpt { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index e6c163d94ba..f538917143b 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -309,7 +309,6 @@ typedef enum eSpaceOutliner_Flag { typedef enum eSpaceOutliner_Filter { SO_FILTER_SEARCH = (1 << 0), /* Run-time flag. */ SO_FILTER_CLEARED_1 = (1 << 1), - SO_FILTER_NO_LIB_OVERRIDE = SO_FILTER_CLEARED_1, /* re-use */ SO_FILTER_NO_OBJECT = (1 << 2), SO_FILTER_NO_OB_CONTENT = (1 << 3), /* Not only mesh, but modifiers, constraints, ... */ SO_FILTER_NO_CHILDREN = (1 << 4), @@ -345,7 +344,7 @@ typedef enum eSpaceOutliner_Filter { #define SO_FILTER_ANY \ (SO_FILTER_NO_OB_CONTENT | SO_FILTER_NO_CHILDREN | SO_FILTER_OB_TYPE | SO_FILTER_OB_STATE | \ - SO_FILTER_NO_COLLECTION | SO_FILTER_NO_VIEW_LAYERS | SO_FILTER_NO_LIB_OVERRIDE) + SO_FILTER_NO_COLLECTION | SO_FILTER_NO_VIEW_LAYERS) /** #SpaceOutliner.filter_state */ typedef enum eSpaceOutliner_StateFilter { @@ -782,7 +781,7 @@ typedef struct FileSelectParams { char _pad1[2]; /* short */ - /** XXXXX for now store type here, should be moved to the operator. */ + /** XXX: for now store type here, should be moved to the operator. */ short type; /* eFileSelectType */ /** Settings for filter, hiding dots files. */ short flag; @@ -797,7 +796,7 @@ typedef struct FileSelectParams { /** Filter when (flags & FILE_FILTER) is true. */ int filter; - /** Max number of levels in dirtree to show at once, 0 to disable recursion. */ + /** Max number of levels in directory tree to show at once, 0 to disable recursion. */ short recursion_level; char _pad4[2]; @@ -1141,8 +1140,8 @@ typedef struct FileDirEntry { # typedef struct FileDirEntryArr { ListBase entries; - int nbr_entries; - int nbr_entries_filtered; + int entries_num; + int entries_filtered_num; /** FILE_MAX. */ char root[1024]; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 5784d437048..5fb8d0328f1 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -637,7 +637,6 @@ typedef struct UserDef_Experimental { char use_undo_legacy; char no_override_auto_resync; char use_cycles_debug; - char use_geometry_nodes_legacy; char show_asset_debug_info; char no_asset_indexing; char SANITIZE_AFTER_HERE; @@ -651,7 +650,9 @@ typedef struct UserDef_Experimental { char use_extended_asset_browser; char use_override_templates; char use_named_attribute_nodes; - char _pad[1]; + char use_select_nearest_on_first_click; + char enable_eevee_next; + // char _pad[0]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index e507831424f..2afaf04a8d7 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -122,10 +122,10 @@ set(SRC ../DNA_camera_defaults.h ../DNA_collection_defaults.h ../DNA_curve_defaults.h + ../DNA_curves_defaults.h ../DNA_defaults.h ../DNA_fluid_defaults.h ../DNA_gpencil_modifier_defaults.h - ../DNA_curves_defaults.h ../DNA_image_defaults.h ../DNA_lattice_defaults.h ../DNA_light_defaults.h diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index b7f942e2502..197a863db72 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -316,6 +316,7 @@ SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierSegment); SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(EnvelopeGpencilModifierData); #undef SDNA_DEFAULT_DECL_STRUCT @@ -555,6 +556,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(DashGpencilModifierData), SDNA_DEFAULT_DECL(DashGpencilModifierSegment), SDNA_DEFAULT_DECL(ShrinkwrapGpencilModifierData), + SDNA_DEFAULT_DECL(EnvelopeGpencilModifierData), }; #undef SDNA_DEFAULT_DECL #undef SDNA_DEFAULT_DECL_EX diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 7b155df3084..b2896c80db3 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -61,6 +61,7 @@ DNA_STRUCT_RENAME_ELEM(Curve, ext1, extrude) DNA_STRUCT_RENAME_ELEM(Curve, ext2, bevel_radius) DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Curve, width, offset) +DNA_STRUCT_RENAME_ELEM(CustomDataExternal, filename, filepath) DNA_STRUCT_RENAME_ELEM(Editing, over_border, overlay_frame_rect) DNA_STRUCT_RENAME_ELEM(Editing, over_cfra, overlay_frame_abs) DNA_STRUCT_RENAME_ELEM(Editing, over_flag, overlay_frame_flag) @@ -86,9 +87,11 @@ DNA_STRUCT_RENAME_ELEM(Object, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(Object, dupfacesca, instance_faces_scale) DNA_STRUCT_RENAME_ELEM(Object, restrictflag, visibility_flag) DNA_STRUCT_RENAME_ELEM(Object, size, scale) +DNA_STRUCT_RENAME_ELEM(ParticleSettings, child_nbr, child_percent) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) +DNA_STRUCT_RENAME_ELEM(ParticleSettings, ren_child_nbr, child_render_percent) DNA_STRUCT_RENAME_ELEM(RigidBodyWorld, steps_per_second, substeps_per_frame) DNA_STRUCT_RENAME_ELEM(RenderData, bake_filter, bake_margin) DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type) diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index d4c38060e1b..3ebcae5f947 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -179,7 +179,7 @@ typedef enum PropertySubType { /* Make sure enums are updated with these */ /* HIGHEST FLAG IN USE: 1 << 31 - * FREE FLAGS: 2, 9, 11, 13, 14, 15, 30 */ + * FREE FLAGS: 2, 9, 11, 13, 14, 15. */ typedef enum PropertyFlag { /** * Editable means the property is editable in the user diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 4e3a4aae727..9980545c19d 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -236,6 +236,10 @@ if(WITH_IMAGE_HDR) add_definitions(-DWITH_HDR) endif() +if(WITH_IMAGE_WEBP) + add_definitions(-DWITH_WEBP) +endif() + if(WITH_AUDASPACE) add_definitions(-DWITH_AUDASPACE) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index a9725da7841..82d90a5c54b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -137,7 +137,11 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { ICON_OUTLINER_COLLECTION, "Collections", "Show Collection data-blocks"}, - {FILTER_ID_CV, "filter_hair", ICON_CURVES_DATA, "Hairs", "Show/hide Hair data-blocks"}, + {FILTER_ID_CV, + "filter_curves", + ICON_CURVES_DATA, + "Hair Curves", + "Show/hide Curves data-blocks"}, {FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"}, {FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"}, {FILTER_ID_LP, diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 943b2fccbb7..b4617321f44 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -160,7 +160,7 @@ void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr) bool RNA_pointer_is_null(const PointerRNA *ptr) { - return !((ptr->data != NULL) && (ptr->owner_id != NULL) && (ptr->type != NULL)); + return (ptr->data == NULL) || (ptr->owner_id == NULL) || (ptr->type == NULL); } static void rna_pointer_inherit_id(StructRNA *type, PointerRNA *parent, PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 6052a742ad5..c86a852b0ea 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -33,7 +33,7 @@ static const EnumPropertyItem prop_direction_items[] = { #ifdef RNA_RUNTIME static const EnumPropertyItem prop_smooth_direction_items[] = { - {0, "SMOOTH", ICON_ADD, "Smooth", "Smooth the surfae"}, + {0, "SMOOTH", ICON_ADD, "Smooth", "Smooth the surface"}, {BRUSH_DIR_IN, "ENHANCE_DETAILS", ICON_REMOVE, @@ -247,6 +247,7 @@ const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { {CURVES_SCULPT_TOOL_COMB, "COMB", ICON_NONE, "Comb", ""}, {CURVES_SCULPT_TOOL_DELETE, "DELETE", ICON_NONE, "Delete", ""}, {CURVES_SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_NONE, "Snake Hook", ""}, + {CURVES_SCULPT_TOOL_ADD, "ADD", ICON_NONE, "Add", ""}, {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""}, {CURVES_SCULPT_TOOL_TEST2, "TEST2", ICON_NONE, "Test 2", ""}, {0, NULL, 0, NULL, NULL}, @@ -1913,6 +1914,20 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); } +static void rna_def_curves_sculpt_options(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "BrushCurvesSculptSettings", NULL); + RNA_def_struct_sdna(srna, "BrushCurvesSculptSettings"); + RNA_def_struct_ui_text(srna, "Curves Sculpt Brush Settings", ""); + + prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, INT32_MAX); + RNA_def_property_ui_text(prop, "Add Amount", "Number of curves added by the Add brush"); +} + static void rna_def_brush(BlenderRNA *brna) { StructRNA *srna; @@ -3491,6 +3506,11 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "gpencil_settings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Gpencil Settings", ""); + + prop = RNA_def_property(srna, "curves_sculpt_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Curves Sculpt Settings", ""); } /** @@ -3577,6 +3597,7 @@ void RNA_def_brush(BlenderRNA *brna) rna_def_vertex_paint_capabilities(brna); rna_def_weight_paint_capabilities(brna); rna_def_gpencil_options(brna); + rna_def_curves_sculpt_options(brna); rna_def_brush_texture_slot(brna); rna_def_operator_stroke_element(brna); } diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 31b67d4eb19..840674c7bc6 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -173,11 +173,7 @@ static char *rna_ColorRamp_path(PointerRNA *ptr) char *node_path; for (node = ntree->nodes.first; node; node = node->next) { - if (ELEM(node->type, - SH_NODE_VALTORGB, - CMP_NODE_VALTORGB, - TEX_NODE_VALTORGB, - GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { + if (ELEM(node->type, SH_NODE_VALTORGB, CMP_NODE_VALTORGB, TEX_NODE_VALTORGB)) { if (node->storage == ptr->data) { /* all node color ramp properties called 'color_ramp' * prepend path from ID to the node @@ -304,11 +300,7 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * bNode *node; for (node = ntree->nodes.first; node; node = node->next) { - if (ELEM(node->type, - SH_NODE_VALTORGB, - CMP_NODE_VALTORGB, - TEX_NODE_VALTORGB, - GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { + if (ELEM(node->type, SH_NODE_VALTORGB, CMP_NODE_VALTORGB, TEX_NODE_VALTORGB)) { BKE_ntree_update_tag_node_property(ntree, node); ED_node_tree_propagate_change(NULL, bmain, ntree); } @@ -613,6 +605,11 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, { ID *id = ptr->owner_id; + if (!id) { + /* Happens for color space settings on operators. */ + return; + } + if (GS(id->name) == ID_IM) { Image *ima = (Image *)id; diff --git a/source/blender/makesrna/intern/rna_curve_api.c b/source/blender/makesrna/intern/rna_curve_api.c index f31e72ce652..b7be5293578 100644 --- a/source/blender/makesrna/intern/rna_curve_api.c +++ b/source/blender/makesrna/intern/rna_curve_api.c @@ -43,22 +43,19 @@ static void rna_Nurb_valid_message(Nurb *nu, int direction, int *result_len, con int pnts; short order, flag; - const char *dir; if (direction == 0) { pnts = nu->pntsu; order = nu->orderu; flag = nu->flagu; - dir = "U"; } else { pnts = nu->pntsv; order = nu->orderv; flag = nu->flagv; - dir = "V"; } char buf[64]; - if (BKE_nurb_valid_message(pnts, order, flag, type, is_surf, dir, buf, sizeof(buf))) { + if (BKE_nurb_valid_message(pnts, order, flag, type, is_surf, direction, buf, sizeof(buf))) { const int buf_len = strlen(buf); *r_result = BLI_strdupn(buf, buf_len); *result_len = buf_len; diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index ce0598d355d..dab3cd68d4c 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -2446,8 +2446,10 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "cfl_condition", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "cfl_condition"); RNA_def_property_range(prop, 0.0, 10.0); - RNA_def_property_ui_text( - prop, "CFL", "Maximal velocity per cell (higher value results in greater timesteps)"); + RNA_def_property_ui_text(prop, + "CFL", + "Maximal velocity per cell (greater CFL numbers will minimize the " + "number of simulation steps and the computation time.)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); prop = RNA_def_property(srna, "use_adaptive_timesteps", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 33c0b29f6d0..edeb7443957 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -79,6 +79,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_DASH, "Dot Dash", "Generate dot-dash styled strokes"}, + {eGpencilModifierType_Envelope, + "GP_ENVELOPE", + ICON_MOD_SKIN, + "Envelope", + "Create an envelope shape"}, {eGpencilModifierType_Length, "GP_LENGTH", ICON_MOD_LENGTH, @@ -208,6 +213,25 @@ static const EnumPropertyItem gpencil_length_mode_items[] = { {GP_LENGTH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Length in geometry space"}, {0, NULL, 0, NULL, NULL}, }; + +static const EnumPropertyItem gpencil_envelope_mode_items[] = { + {GP_ENVELOPE_DEFORM, + "DEFORM", + 0, + "Deform", + "Deform the stroke to best match the envelope shape"}, + {GP_ENVELOPE_SEGMENTS, + "SEGMENTS", + 0, + "Segments", + "Add segments to create the envelope. Keep the original stroke"}, + {GP_ENVELOPE_FILLS, + "FILLS", + 0, + "Fills", + "Add fill segments to create the envelope. Don't keep the original stroke"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #ifdef RNA_RUNTIME @@ -279,6 +303,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) return &RNA_LineartGpencilModifier; case eGpencilModifierType_Dash: return &RNA_DashGpencilModifierData; + case eGpencilModifierType_Envelope: + return &RNA_EnvelopeGpencilModifier; /* Default */ case eGpencilModifierType_None: case NUM_GREASEPENCIL_MODIFIER_TYPES: @@ -355,6 +381,7 @@ RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, target_vgname); RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Lineart, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Shrinkwrap, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(Envelope, vgname); # undef RNA_GP_MOD_VGROUP_NAME_SET @@ -775,6 +802,16 @@ static void rna_ShrinkwrapGpencilModifier_face_cull_set(struct PointerRNA *ptr, swm->shrink_opts = (swm->shrink_opts & ~MOD_SHRINKWRAP_CULL_TARGET_MASK) | value; } +static void rna_EnvelopeGpencilModifier_material_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *reports) +{ + EnvelopeGpencilModifierData *emd = (EnvelopeGpencilModifierData *)ptr->data; + Material **ma_target = &emd->material; + + rna_GpencilModifier_material_set(ptr, value, ma_target, reports); +} + #else static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) @@ -3676,7 +3713,7 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "gap", PROP_INT, PROP_NONE); - RNA_def_property_range(prop, 1, INT16_MAX); + RNA_def_property_range(prop, 0, INT16_MAX); RNA_def_property_ui_text(prop, "Gap", "The number of points skipped after this segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -3968,6 +4005,112 @@ static void rna_def_modifier_gpencilshrinkwrap(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_modifier_gpencilenvelope(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "EnvelopeGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Envelope Modifier", "Envelope stroke effect modifier"); + RNA_def_struct_sdna(srna, "EnvelopeGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_SKIN); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "layername"); + RNA_def_property_ui_text(prop, "Layer", "Layer name"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, + NULL, + "rna_EnvelopeGpencilModifier_material_set", + NULL, + "rna_GpencilModifier_material_poll"); + RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "vgname"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_EnvelopeGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "pass_index"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "spread", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "spread"); + RNA_def_property_range(prop, 1, INT_MAX); + RNA_def_property_ui_text( + prop, "Spread Length", "The number of points to skip to create straight segments"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, gpencil_envelope_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Algorithm to use for generating the envelope"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "mat_nr", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "mat_nr"); + RNA_def_property_range(prop, -1, INT16_MAX); + RNA_def_property_ui_text(prop, "Material Index", "The material to use for the new strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "thickness", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "thickness"); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Thickness", "Multiplier for the thickness of the new strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "strength"); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Strength", "Multiplier for the strength of the new strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_LAYER); + RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_MATERIAL); + RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_vertex", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "layer_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Layer pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_LAYERPASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + RNA_define_lib_overridable(false); +} + void RNA_def_greasepencil_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -4058,6 +4201,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) rna_def_modifier_gpencillength(brna); rna_def_modifier_gpencildash(brna); rna_def_modifier_gpencilshrinkwrap(brna); + rna_def_modifier_gpencilenvelope(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 56e23278176..3a93a44e0d1 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -13,6 +13,7 @@ #include "BLI_utildefines.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_node_tree_update.h" #include "DEG_depsgraph.h" @@ -238,8 +239,8 @@ static int rna_Image_file_format_get(PointerRNA *ptr) { Image *image = (Image *)ptr->data; ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); - int imtype = BKE_image_ftype_to_imtype(ibuf ? ibuf->ftype : IMB_FTYPE_NONE, - ibuf ? &ibuf->foptions : NULL); + int imtype = BKE_ftype_to_imtype(ibuf ? ibuf->ftype : IMB_FTYPE_NONE, + ibuf ? &ibuf->foptions : NULL); BKE_image_release_ibuf(image, ibuf, NULL); @@ -251,7 +252,7 @@ static void rna_Image_file_format_set(PointerRNA *ptr, int value) Image *image = (Image *)ptr->data; if (BKE_imtype_is_movie(value) == 0) { /* should be able to throw an error here */ ImbFormatOptions options; - int ftype = BKE_image_imtype_to_ftype(value, &options); + int ftype = BKE_imtype_to_ftype(value, &options); BKE_image_file_format_set(image, ftype, &options); } } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 92e3254765c..0b188b2b3db 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -25,7 +25,9 @@ #ifdef RNA_RUNTIME # include "BKE_image.h" +# include "BKE_image_format.h" # include "BKE_main.h" +# include "BKE_scene.h" # include <errno.h> # include "IMB_colormanagement.h" @@ -68,19 +70,23 @@ static void rna_Image_save_render( else { ImBuf *write_ibuf; - write_ibuf = IMB_colormanagement_imbuf_for_write( - ibuf, true, true, &scene->view_settings, &scene->display_settings, &scene->r.im_format); + ImageFormatData image_format; + BKE_image_format_init_for_write(&image_format, scene, NULL); - write_ibuf->planes = scene->r.im_format.planes; + write_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &image_format); + + write_ibuf->planes = image_format.planes; write_ibuf->dither = scene->r.dither_intensity; - if (!BKE_imbuf_write(write_ibuf, path, &scene->r.im_format)) { + if (!BKE_imbuf_write(write_ibuf, path, &image_format)) { BKE_reportf(reports, RPT_ERROR, "Could not write image: %s, '%s'", strerror(errno), path); } if (write_ibuf != ibuf) { IMB_freeImBuf(write_ibuf); } + + BKE_image_format_free(&image_format); } BKE_image_release_ibuf(image, ibuf, lock); @@ -96,14 +102,14 @@ static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *r ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); if (ibuf) { - char filename[FILE_MAX]; - BLI_strncpy(filename, image->filepath, sizeof(filename)); - BLI_path_abs(filename, ID_BLEND_PATH(bmain, &image->id)); + char filepath[FILE_MAX]; + BLI_strncpy(filepath, image->filepath, sizeof(filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &image->id)); /* NOTE: we purposefully ignore packed files here, * developers need to explicitly write them via 'packed_files' */ - if (IMB_saveiff(ibuf, filename, ibuf->flags)) { + if (IMB_saveiff(ibuf, filepath, ibuf->flags)) { image->type = IMA_TYPE_IMAGE; if (image->source == IMA_SRC_GENERATED) { diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index c59fd2a0535..84f083cb37a 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -393,7 +393,15 @@ char *rna_TextureSlot_path(struct PointerRNA *ptr); char *rna_Node_ImageUser_path(struct PointerRNA *ptr); /* Set U.is_dirty and redraw. */ + +/** + * Use single function so we can more easily break-point it. + */ void rna_userdef_is_dirty_update_impl(void); +/** + * Use as a fallback update handler to ensure #U.runtime.is_dirty is set. + * So the preferences are saved when modified. + */ void rna_userdef_is_dirty_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr); /* API functions */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index b12b33c67af..2ef2e776842 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1074,7 +1074,7 @@ static void rna_MultiresModifier_filepath_get(PointerRNA *ptr, char *value) Object *ob = (Object *)ptr->owner_id; CustomDataExternal *external = ((Mesh *)ob->data)->ldata.external; - BLI_strncpy(value, (external) ? external->filename : "", sizeof(external->filename)); + BLI_strncpy(value, (external) ? external->filepath : "", sizeof(external->filepath)); } static void rna_MultiresModifier_filepath_set(PointerRNA *ptr, const char *value) @@ -1082,8 +1082,8 @@ static void rna_MultiresModifier_filepath_set(PointerRNA *ptr, const char *value Object *ob = (Object *)ptr->owner_id; CustomDataExternal *external = ((Mesh *)ob->data)->ldata.external; - if (external && !STREQ(external->filename, value)) { - BLI_strncpy(external->filename, value, sizeof(external->filename)); + if (external && !STREQ(external->filepath, value)) { + BLI_strncpy(external->filepath, value, sizeof(external->filepath)); multires_force_external_reload(ob); } } @@ -1093,7 +1093,7 @@ static int rna_MultiresModifier_filepath_length(PointerRNA *ptr) Object *ob = (Object *)ptr->owner_id; CustomDataExternal *external = ((Mesh *)ob->data)->ldata.external; - return strlen((external) ? external->filename : ""); + return strlen((external) ? external->filepath : ""); } static int rna_ShrinkwrapModifier_face_cull_get(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index f212b4a1d23..70b335446fc 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -461,30 +461,6 @@ const EnumPropertyItem rna_enum_node_filter_items[] = { {0, NULL, 0, NULL, NULL}, }; -static const EnumPropertyItem rna_node_geometry_attribute_randomize_operation_items[] = { - {GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE, - "REPLACE_CREATE", - ICON_NONE, - "Replace/Create", - "Replace the value and data type of an existing attribute, or create a new one"}, - {GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD, - "ADD", - ICON_NONE, - "Add", - "Add the random values to the existing attribute values"}, - {GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT, - "SUBTRACT", - ICON_NONE, - "Subtract", - "Subtract random values from the existing attribute values"}, - {GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY, - "MULTIPLY", - ICON_NONE, - "Multiply", - "Multiply the existing attribute values with the random values"}, - {0, NULL, 0, NULL, NULL}, -}; - static const EnumPropertyItem rna_node_geometry_curve_handle_type_items[] = { {GEO_NODE_CURVE_HANDLE_FREE, "FREE", @@ -547,73 +523,8 @@ static EnumPropertyItem rna_node_geometry_mesh_circle_fill_type_items[] = { }; #endif -#define ITEM_ATTRIBUTE \ - { \ - GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \ - } -#define ITEM_FLOAT \ - { \ - GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \ - } -#define ITEM_VECTOR \ - { \ - GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \ - } -#define ITEM_COLOR \ - { \ - GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \ - } -#define ITEM_INTEGER \ - { \ - GEO_NODE_ATTRIBUTE_INPUT_INTEGER, "INTEGER", 0, "Integer", "" \ - } -#define ITEM_BOOLEAN \ - { \ - GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \ - } - -/* Used in both runtime and static code. */ -static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = { - ITEM_ATTRIBUTE, - ITEM_FLOAT, - ITEM_VECTOR, - ITEM_COLOR, - ITEM_INTEGER, - ITEM_BOOLEAN, - {0, NULL, 0, NULL, NULL}, -}; - #ifndef RNA_RUNTIME -static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vector[] = { - ITEM_ATTRIBUTE, - ITEM_VECTOR, - {0, NULL, 0, NULL, NULL}, -}; -static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float_vector[] = { - ITEM_ATTRIBUTE, - ITEM_FLOAT, - ITEM_VECTOR, - {0, NULL, 0, NULL, NULL}, -}; -static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = { - ITEM_ATTRIBUTE, - ITEM_FLOAT, - {0, NULL, 0, NULL, NULL}, -}; -static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_int[] = { - ITEM_ATTRIBUTE, - ITEM_INTEGER, - {0, NULL, 0, NULL, NULL}, -}; -static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = { - ITEM_ATTRIBUTE, - ITEM_FLOAT, - ITEM_VECTOR, - ITEM_COLOR, - {0, NULL, 0, NULL, NULL}, -}; - #endif #undef ITEM_ATTRIBUTE @@ -2170,31 +2081,6 @@ static const EnumPropertyItem *rna_FunctionNodeCompare_operation_itemf(bContext } } -static bool attribute_clamp_type_supported(const EnumPropertyItem *item) -{ - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR); -} - -static const EnumPropertyItem *rna_GeometryNodeAttributeClamp_type_itemf(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_attribute_type_items, attribute_clamp_type_supported); -} - -static bool attribute_random_type_supported(const EnumPropertyItem *item) -{ - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32); -} -static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf( - bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_attribute_type_items, attribute_random_type_supported); -} - static bool random_value_type_supported(const EnumPropertyItem *item) { return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32); @@ -2222,49 +2108,6 @@ static const EnumPropertyItem *rna_GeoNodeAccumulateField_type_itemf(bContext *U return itemf_function_check(rna_enum_attribute_type_items, accumulate_field_type_supported); } -static const EnumPropertyItem *rna_GeometryNodeAttributeRandomize_operation_itemf( - bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) -{ - bNode *node = ptr->data; - const NodeAttributeRandomize *node_storage = (NodeAttributeRandomize *)node->storage; - const CustomDataType data_type = (CustomDataType)node_storage->data_type; - - EnumPropertyItem *item_array = NULL; - int items_len = 0; - for (const EnumPropertyItem *item = rna_node_geometry_attribute_randomize_operation_items; - item->identifier != NULL; - item++) { - if (data_type == CD_PROP_BOOL) { - if (item->value == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) { - RNA_enum_item_add(&item_array, &items_len, item); - } - } - else { - RNA_enum_item_add(&item_array, &items_len, item); - } - } - RNA_enum_item_end(&item_array, &items_len); - - *r_free = true; - return item_array; -} - -static void rna_GeometryNodeAttributeRandomize_data_type_update(Main *bmain, - Scene *scene, - PointerRNA *ptr) -{ - bNode *node = ptr->data; - NodeAttributeRandomize *node_storage = (NodeAttributeRandomize *)node->storage; - - /* The boolean data type has no extra operations besides, - * replace, so make sure the enum value is set properly. */ - if (node_storage->data_type == CD_PROP_BOOL) { - node_storage->operation = GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE; - } - - rna_Node_socket_update(bmain, scene, ptr); -} - static void rna_GeometryNodeCompare_data_type_update(Main *bmain, Scene *scene, PointerRNA *ptr) { bNode *node = ptr->data; @@ -2289,37 +2132,18 @@ static void rna_GeometryNodeCompare_data_type_update(Main *bmain, Scene *scene, rna_Node_socket_update(bmain, scene, ptr); } -static bool attribute_convert_type_supported(const EnumPropertyItem *item) -{ - return ELEM(item->value, - CD_AUTO_FROM_NAME, - CD_PROP_FLOAT, - CD_PROP_FLOAT2, - CD_PROP_FLOAT3, - CD_PROP_COLOR, - CD_PROP_BOOL, - CD_PROP_INT32); -} -static const EnumPropertyItem *rna_GeometryNodeAttributeConvert_type_itemf( - bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_attribute_type_with_auto_items, - attribute_convert_type_supported); -} - -static bool attribute_fill_type_supported(const EnumPropertyItem *item) +static bool generic_attribute_type_supported(const EnumPropertyItem *item) { return ELEM( item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32); } -static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext *UNUSED(C), +static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { *r_free = true; - return itemf_function_check(rna_enum_attribute_type_items, attribute_fill_type_supported); + return itemf_function_check(rna_enum_attribute_type_items, generic_attribute_type_supported); } static bool transfer_attribute_type_supported(const EnumPropertyItem *item) @@ -2327,7 +2151,6 @@ static bool transfer_attribute_type_supported(const EnumPropertyItem *item) return ELEM( item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32); } - static const EnumPropertyItem *rna_NodeGeometryTransferAttribute_type_itemf( bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { @@ -2346,122 +2169,6 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeStatistic_type_itemf( return itemf_function_check(rna_enum_attribute_type_items, attribute_statistic_type_supported); } -/** - * This bit of ugly code makes sure the float / attribute option shows up instead of - * vector / attribute if the node uses an operation that uses a float for input B or C. - */ -static const EnumPropertyItem *rna_GeometryNodeAttributeVectorMath_input_type_b_itemf( - bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) -{ - bNode *node = ptr->data; - NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage; - - EnumPropertyItem *item_array = NULL; - int items_len = 0; - for (const EnumPropertyItem *item = rna_node_geometry_attribute_input_type_items_any; - item->identifier != NULL; - item++) { - if (item->value == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) { - RNA_enum_item_add(&item_array, &items_len, item); - } - else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) { - if (node_storage->operation == NODE_VECTOR_MATH_SCALE) { - RNA_enum_item_add(&item_array, &items_len, item); - } - } - else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) { - if (node_storage->operation != NODE_VECTOR_MATH_SCALE) { - RNA_enum_item_add(&item_array, &items_len, item); - } - } - } - RNA_enum_item_end(&item_array, &items_len); - - *r_free = true; - return item_array; -} - -static const EnumPropertyItem *rna_GeometryNodeAttributeVectorMath_input_type_c_itemf( - bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) -{ - bNode *node = ptr->data; - NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage; - - EnumPropertyItem *item_array = NULL; - int items_len = 0; - for (const EnumPropertyItem *item = rna_node_geometry_attribute_input_type_items_any; - item->identifier != NULL; - item++) { - if (item->value == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) { - RNA_enum_item_add(&item_array, &items_len, item); - } - else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) { - if (node_storage->operation == NODE_VECTOR_MATH_REFRACT) { - RNA_enum_item_add(&item_array, &items_len, item); - } - } - else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) { - if (node_storage->operation != NODE_VECTOR_MATH_REFRACT) { - RNA_enum_item_add(&item_array, &items_len, item); - } - } - } - RNA_enum_item_end(&item_array, &items_len); - - *r_free = true; - return item_array; -} - -static void rna_GeometryNodeAttributeVectorMath_operation_update(Main *bmain, - Scene *scene, - PointerRNA *ptr) -{ - bNode *node = ptr->data; - NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage; - - const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation; - - /* The scale operation can't use a vector input, so reset - * the input type enum in case it's set to vector. */ - if (operation == NODE_VECTOR_MATH_SCALE) { - if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) { - node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - } - } - - /* Scale is also the only operation that uses the float input type, so a - * a check is also necessary for the other direction. */ - if (operation != NODE_VECTOR_MATH_SCALE) { - if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) { - node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - } - } - - rna_Node_socket_update(bmain, scene, ptr); -} - -static bool attribute_map_range_type_supported(const EnumPropertyItem *item) -{ - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3); -} -static const EnumPropertyItem *rna_GeometryNodeAttributeMapRange_type_itemf( - bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_attribute_type_items, attribute_map_range_type_supported); -} - -static bool attribute_curve_map_type_supported(const EnumPropertyItem *item) -{ - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR); -} -static const EnumPropertyItem *rna_GeometryNodeAttributeCurveMap_type_itemf( - bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_attribute_type_items, attribute_curve_map_type_supported); -} - static StructRNA *rna_ShaderNode_register(Main *bmain, ReportList *reports, void *data, @@ -9636,71 +9343,6 @@ static void def_fn_random_value(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_attribute_randomize(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeRandomize", "storage"); - - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "data_type"); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeRandom_type_itemf"); - RNA_def_property_enum_default(prop, CD_PROP_FLOAT); - RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeRandomize_data_type_update"); - - prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "operation"); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_randomize_operation_items); - RNA_def_property_enum_funcs( - prop, NULL, NULL, "rna_GeometryNodeAttributeRandomize_operation_itemf"); - RNA_def_property_enum_default(prop, GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE); - RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - -static void def_geo_attribute_fill(StructRNA *srna) -{ - PropertyRNA *prop; - - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "custom1"); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); - RNA_def_property_enum_default(prop, CD_PROP_FLOAT); - RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); - - prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "custom2"); - RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items); - RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO); - RNA_def_property_ui_text(prop, "Domain", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -} - -static void def_geo_attribute_convert(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeConvert", "storage"); - - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_with_auto_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeConvert_type_itemf"); - RNA_def_property_enum_default(prop, CD_AUTO_FROM_NAME); - RNA_def_property_ui_text(prop, "Data Type", "The data type to save the result attribute with"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); - - prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items); - RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO); - RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -} - static void def_geo_attribute_statistic(StructRNA *srna) { PropertyRNA *prop; @@ -9724,229 +9366,6 @@ static void def_geo_attribute_statistic(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_attribute_math(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeMath", "storage"); - - prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "operation"); - RNA_def_property_enum_items(prop, rna_enum_node_math_items); - RNA_def_property_enum_default(prop, NODE_MATH_ADD); - RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_a"); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type A", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_b"); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type B", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_c", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_c"); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type C", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - -static void def_geo_attribute_vector_math(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeVectorMath", "storage"); - - prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "operation"); - RNA_def_property_enum_items(prop, rna_enum_node_vec_math_items); - RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeVectorMath_operation_update"); - - prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_a"); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type A", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_b"); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); - RNA_def_property_enum_funcs( - prop, NULL, NULL, "rna_GeometryNodeAttributeVectorMath_input_type_b_itemf"); - RNA_def_property_ui_text(prop, "Input Type B", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_c", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_c"); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); - RNA_def_property_enum_funcs( - prop, NULL, NULL, "rna_GeometryNodeAttributeVectorMath_input_type_c_itemf"); - RNA_def_property_ui_text(prop, "Input Type C", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - -static void def_geo_attribute_map_range(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeMapRange", "storage"); - - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_type"); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMapRange_type_itemf"); - RNA_def_property_ui_text(prop, "Data Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "interpolation_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "interpolation_type"); - RNA_def_property_enum_items(prop, rna_enum_node_map_range_items); - RNA_def_property_ui_text(prop, "Interpolation Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); -} - -static void def_geo_point_instance(StructRNA *srna) -{ - static const EnumPropertyItem instance_type_items[] = { - {GEO_NODE_POINT_INSTANCE_TYPE_OBJECT, - "OBJECT", - ICON_NONE, - "Object", - "Instance an individual object on all points"}, - {GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION, - "COLLECTION", - ICON_NONE, - "Collection", - "Instance an entire collection on all points"}, - {GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY, - "GEOMETRY", - ICON_NONE, - "Geometry", - "Copy geometry to all points"}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - RNA_def_struct_sdna_from(srna, "NodeGeometryPointInstance", "storage"); - - prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "instance_type"); - RNA_def_property_enum_items(prop, instance_type_items); - RNA_def_property_enum_default(prop, GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); - RNA_def_property_ui_text(prop, "Instance Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "use_whole_collection", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION); - RNA_def_property_ui_text(prop, "Whole Collection", "Instance entire collection on each point"); - RNA_def_property_update(prop, 0, "rna_Node_socket_update"); -} - -static void def_geo_attribute_mix(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeMix", "storage"); - - prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_ramp_blend_items); - RNA_def_property_enum_default(prop, MA_RAMP_BLEND); - RNA_def_property_ui_text(prop, "Blending Mode", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type Factor", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); - RNA_def_property_ui_text(prop, "Input Type A", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); - RNA_def_property_ui_text(prop, "Input Type B", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - -static void def_geo_attribute_clamp(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeClamp", "storage"); - - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_type"); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeClamp_type_itemf"); - RNA_def_property_ui_text(prop, "Data Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_node_clamp_items); - RNA_def_property_enum_default(prop, NODE_CLAMP_MINMAX); - RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -} - -static void def_geo_attribute_attribute_compare(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeCompare", "storage"); - - prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_node_float_compare_items); - RNA_def_property_enum_default(prop, NODE_COMPARE_GREATER_THAN); - RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); - RNA_def_property_ui_text(prop, "Input Type A", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); - RNA_def_property_ui_text(prop, "Input Type B", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - -static void def_geo_point_distribute(StructRNA *srna) -{ - PropertyRNA *prop; - - static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = { - {GEO_NODE_POINT_DISTRIBUTE_RANDOM, - "RANDOM", - 0, - "Random", - "Distribute points randomly on the surface"}, - {GEO_NODE_POINT_DISTRIBUTE_POISSON, - "POISSON", - 0, - "Poisson Disk", - "Distribute the points randomly on the surface while taking a minimum distance between " - "points into account"}, - {0, NULL, 0, NULL, NULL}, - }; - - prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "custom1"); - RNA_def_property_enum_items(prop, rna_node_geometry_point_distribute_method_items); - RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM); - RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_extrude_mesh(StructRNA *srna) { PropertyRNA *prop; @@ -9995,97 +9414,6 @@ static void def_geo_distribute_points_on_faces(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_attribute_color_ramp(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeColorRamp", "storage"); - - prop = RNA_def_property(srna, "color_ramp", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "ColorRamp"); - RNA_def_property_ui_text(prop, "Color Ramp", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -} - -static void def_geo_attribute_curve_map(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeCurveMap", "storage"); - - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "data_type"); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeCurveMap_type_itemf"); - RNA_def_property_ui_text(prop, "Data Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "curve_vec", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "CurveMapping"); - RNA_def_property_ui_text(prop, "Mapping", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "curve_rgb", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "CurveMapping"); - RNA_def_property_ui_text(prop, "Mapping", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -} - -static void def_geo_attribute_vector_rotate(StructRNA *srna) -{ - static const EnumPropertyItem rotate_mode_items[] = { - {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS, - "AXIS_ANGLE", - 0, - "Axis Angle", - "Rotate a point using axis angle"}, - {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X, "X_AXIS", 0, "X Axis", "Rotate a point using X axis"}, - {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y, "Y_AXIS", 0, "Y Axis", "Rotate a point using Y axis"}, - {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z, "Z_AXIS", 0, "Z Axis", "Rotate a point using Z axis"}, - {GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ, - "EULER_XYZ", - 0, - "Euler", - "Rotate a point using XYZ order"}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeVectorRotate", "storage"); - - prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "mode"); - RNA_def_property_enum_items(prop, rotate_mode_items); - RNA_def_property_ui_text(prop, "Mode", "Type of rotation"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); - - prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Vector", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_center", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Center", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Axis", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type Angle", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Rotation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_curve_spline_type(StructRNA *srna) { static const EnumPropertyItem type_items[] = { @@ -10121,24 +9449,6 @@ static void def_geo_curve_set_handle_type(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_legacy_curve_set_handles(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage"); - - prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "handle_type"); - RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); - RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); - RNA_def_property_flag(prop, PROP_ENUM_FLAG); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_curve_set_handle_positions(StructRNA *srna) { PropertyRNA *prop; @@ -10151,24 +9461,6 @@ static void def_geo_curve_set_handle_positions(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_curve_select_handles(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSelectHandles", "storage"); - - prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "handle_type"); - RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); - RNA_def_property_ui_text(prop, "Mode", "Whether to check the type of left and right handles"); - RNA_def_property_flag(prop, PROP_ENUM_FLAG); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_curve_handle_type_selection(StructRNA *srna) { PropertyRNA *prop; @@ -10266,66 +9558,6 @@ static void def_geo_curve_primitive_line(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_point_rotate(StructRNA *srna) -{ - static const EnumPropertyItem type_items[] = { - {GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE, - "AXIS_ANGLE", - ICON_NONE, - "Axis Angle", - "Rotate around an axis by an angle"}, - {GEO_NODE_POINT_ROTATE_TYPE_EULER, - "EULER", - ICON_NONE, - "Euler", - "Rotate around the X, Y, and Z axes"}, - {0, NULL, 0, NULL, NULL}, - }; - - static const EnumPropertyItem space_items[] = { - {GEO_NODE_POINT_ROTATE_SPACE_OBJECT, - "OBJECT", - ICON_NONE, - "Object", - "Rotate points in the local space of the object"}, - {GEO_NODE_POINT_ROTATE_SPACE_POINT, - "POINT", - ICON_NONE, - "Point", - "Rotate every point in its local space (as defined by the 'rotation' attribute)"}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryRotatePoints", "storage"); - - prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, type_items); - RNA_def_property_ui_text(prop, "Type", "Method used to describe the rotation"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, space_items); - RNA_def_property_ui_text(prop, "Space", "Base orientation of the points"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Axis", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type Angle", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Rotation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_fn_rotate_euler(StructRNA *srna) { static const EnumPropertyItem type_items[] = { @@ -10371,76 +9603,6 @@ static void def_fn_rotate_euler(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_align_rotation_to_vector(StructRNA *srna) -{ - static const EnumPropertyItem axis_items[] = { - {GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X, - "X", - ICON_NONE, - "X", - "Align the X axis with the vector"}, - {GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Y, - "Y", - ICON_NONE, - "Y", - "Align the Y axis with the vector"}, - {GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Z, - "Z", - ICON_NONE, - "Z", - "Align the Z axis with the vector"}, - {0, NULL, 0, NULL, NULL}, - }; - - static const EnumPropertyItem pivot_axis_items[] = { - {GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO, - "AUTO", - ICON_NONE, - "Auto", - "Automatically detect the best rotation axis to rotate towards the vector"}, - {GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_X, - "X", - ICON_NONE, - "X", - "Rotate around the local X axis"}, - {GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Y, - "Y", - ICON_NONE, - "Y", - "Rotate around the local Y axis"}, - {GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Z, - "Z", - ICON_NONE, - "Z", - "Rotate around the local Z axis"}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryAlignRotationToVector", "storage"); - - prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, axis_items); - RNA_def_property_ui_text(prop, "Axis", "Axis to align to the vector"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "pivot_axis", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, pivot_axis_items); - RNA_def_property_ui_text(prop, "Pivot Axis", "Axis to rotate around"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type Factor", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Vector", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_fn_align_euler_to_vector(StructRNA *srna) { static const EnumPropertyItem axis_items[] = { @@ -10501,30 +9663,6 @@ static void def_fn_align_euler_to_vector(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_point_scale(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryPointScale", "storage"); - - prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float_vector); - RNA_def_property_ui_text(prop, "Input Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - -static void def_geo_point_translate(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryPointTranslate", "storage"); - - prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_object_info(StructRNA *srna) { PropertyRNA *prop; @@ -10555,37 +9693,6 @@ static void def_geo_object_info(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations"); } -static void def_geo_legacy_points_to_volume(StructRNA *srna) -{ - PropertyRNA *prop; - - static EnumPropertyItem resolution_mode_items[] = { - {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT, - "VOXEL_AMOUNT", - 0, - "Amount", - "Specify the approximate number of voxels along the diagonal"}, - {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE, - "VOXEL_SIZE", - 0, - "Size", - "Specify the voxel side length"}, - {0, NULL, 0, NULL, NULL}, - }; - - RNA_def_struct_sdna_from(srna, "NodeGeometryPointsToVolume", "storage"); - - prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, resolution_mode_items); - RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_radius", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Radius Input Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_points_to_volume(StructRNA *srna) { PropertyRNA *prop; @@ -10639,39 +9746,6 @@ static void def_geo_collection_info(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations"); } -static void def_geo_legacy_attribute_proximity(StructRNA *srna) -{ - static const EnumPropertyItem target_geometry_element[] = { - {GEO_NODE_PROXIMITY_TARGET_POINTS, - "POINTS", - ICON_NONE, - "Points", - "Calculate proximity to the target's points (usually faster than the other two modes)"}, - {GEO_NODE_PROXIMITY_TARGET_EDGES, - "EDGES", - ICON_NONE, - "Edges", - "Calculate proximity to the target's edges"}, - {GEO_NODE_PROXIMITY_TARGET_FACES, - "FACES", - ICON_NONE, - "Faces", - "Calculate proximity to the target's faces"}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeProximity", "storage"); - - prop = RNA_def_property(srna, "target_geometry_element", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, target_geometry_element); - RNA_def_property_enum_default(prop, GEO_NODE_PROXIMITY_TARGET_FACES); - RNA_def_property_ui_text( - prop, "Target Geometry", "Element of the target geometry to calculate the distance from"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_proximity(StructRNA *srna) { static const EnumPropertyItem target_element_items[] = { @@ -10736,40 +9810,6 @@ static void def_geo_volume_to_mesh(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_attribute_combine_xyz(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeCombineXYZ", "storage"); - - prop = RNA_def_property(srna, "input_type_x", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type X", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_y", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type Y", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_z", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type Z", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - -static void def_geo_attribute_separate_xyz(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeAttributeSeparateXYZ", "storage"); - - prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_mesh_circle(StructRNA *srna) { PropertyRNA *prop; @@ -10936,18 +9976,6 @@ static void def_geo_curve_resample(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_legacy_curve_subdivide(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSubdivide", "storage"); - - prop = RNA_def_property(srna, "cuts_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_int); - RNA_def_property_ui_text(prop, "Cuts Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_curve_fillet(StructRNA *srna) { PropertyRNA *prop; @@ -10974,38 +10002,6 @@ static void def_geo_curve_fillet(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_legacy_curve_to_points(StructRNA *srna) -{ - PropertyRNA *prop; - - static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_RESAMPLE_EVALUATED, - "EVALUATED", - 0, - "Evaluated", - "Create points from the curve's evaluated points, based on the resolution attribute for " - "NURBS and Bezier splines"}, - {GEO_NODE_CURVE_RESAMPLE_COUNT, - "COUNT", - 0, - "Count", - "Sample each spline by evenly distributing the specified number of points"}, - {GEO_NODE_CURVE_RESAMPLE_LENGTH, - "LENGTH", - 0, - "Length", - "Sample each spline by splitting it into segments with the specified length"}, - {0, NULL, 0, NULL, NULL}, - }; - - RNA_def_struct_sdna_from(srna, "NodeGeometryCurveToPoints", "storage"); - - prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, mode_items); - RNA_def_property_ui_text(prop, "Mode", "How to generate points from the input curve"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_curve_to_points(StructRNA *srna) { PropertyRNA *prop; @@ -11100,40 +10096,6 @@ static void def_geo_curve_trim(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_attribute_transfer(StructRNA *srna) -{ - static EnumPropertyItem mapping_items[] = { - {GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, - "NEAREST_FACE_INTERPOLATED", - 0, - "Nearest Face Interpolated", - "Transfer the attribute from the nearest face on a surface (loose points and edges are " - "ignored)"}, - {GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST, - "NEAREST", - 0, - "Nearest", - "Transfer the element from the nearest element (using face and edge centers for the " - "distance computation)"}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeTransfer", "storage"); - - prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items); - RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO); - RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, mapping_items); - RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -} - static void def_geo_transfer_attribute(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { @@ -11194,42 +10156,6 @@ static void def_geo_input_material(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_legacy_raycast(StructRNA *srna) -{ - static EnumPropertyItem mapping_items[] = { - {GEO_NODE_RAYCAST_INTERPOLATED, - "INTERPOLATED", - 0, - "Interpolated", - "Interpolate the attribute from the corners of the hit face"}, - {GEO_NODE_RAYCAST_NEAREST, - "NEAREST", - 0, - "Nearest", - "Use the attribute value of the closest mesh element"}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeGeometryRaycast", "storage"); - - prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, mapping_items); - RNA_def_property_ui_text(prop, "Mapping", "Mapping from the target geometry to hit points"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "input_type_ray_direction", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); - RNA_def_property_ui_text(prop, "Input Type Ray Direction", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); - - prop = RNA_def_property(srna, "input_type_ray_length", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); - RNA_def_property_ui_text(prop, "Input Type Ray Length", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); -} - static void def_geo_raycast(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { @@ -11257,7 +10183,7 @@ static void def_geo_raycast(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -11289,7 +10215,7 @@ static void def_geo_store_named_attribute(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -11309,7 +10235,7 @@ static void def_geo_input_named_attribute(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", "The data type used to read the attribute values"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -11323,7 +10249,7 @@ static void def_geo_attribute_capture(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -11550,7 +10476,7 @@ static void def_geo_viewer(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -11580,7 +10506,7 @@ static void def_geo_field_at_index(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom2"); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); RNA_def_property_ui_text(prop, "Data Type", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); } @@ -13099,12 +12025,12 @@ static void rna_def_node_link(BlenderRNA *brna) prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_LINK_VALID); - RNA_def_struct_ui_text(srna, "Valid", "Link is valid"); + RNA_def_property_ui_text(prop, "Valid", "Link is valid"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, NULL); prop = RNA_def_property(srna, "is_muted", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_LINK_MUTED); - RNA_def_struct_ui_text(srna, "Muted", "Link is muted and can be ignored"); + RNA_def_property_ui_text(prop, "Muted", "Link is muted and can be ignored"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, NULL); prop = RNA_def_property(srna, "from_node", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 2770255802d..03f800d14d1 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -3139,15 +3139,19 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Particle_redo"); /* children */ + + /* NOTE(@campbellbarton): name is not following conventions: `nbr`. + * Could be changed next major version. */ prop = RNA_def_property(srna, "child_nbr", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "child_nbr"); /* Optional if prop names are the same. */ + RNA_def_property_int_sdna( + prop, NULL, "child_percent"); /* Optional if prop names are the same. */ RNA_def_property_range(prop, 0, 100000); RNA_def_property_ui_range(prop, 0, 1000, 1, -1); RNA_def_property_ui_text(prop, "Children Per Parent", "Number of children per parent"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); prop = RNA_def_property(srna, "rendered_child_count", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "ren_child_nbr"); + RNA_def_property_int_sdna(prop, NULL, "child_render_percent"); RNA_def_property_range(prop, 0, 100000); RNA_def_property_ui_range(prop, 0, 10000, 1, -1); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index d398dadbec7..6be2c361ba9 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -20,6 +20,7 @@ #include "DNA_view3d_types.h" #include "DNA_world_types.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "BLI_listbase.h" @@ -29,6 +30,7 @@ #include "BKE_armature.h" #include "BKE_editmesh.h" +#include "BKE_idtype.h" #include "BKE_paint.h" #include "BKE_volume.h" @@ -328,6 +330,13 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { # define R_IMF_ENUM_TIFF #endif +#ifdef WITH_WEBP +# define R_IMF_ENUM_WEBP \ + {R_IMF_IMTYPE_WEBP, "WEBP", ICON_FILE_IMAGE, "WebP", "Output image in WebP format"}, +#else +# define R_IMF_ENUM_WEBP +#endif + #define IMAGE_TYPE_ITEMS_IMAGE_ONLY \ R_IMF_ENUM_BMP \ /* DDS save not supported yet R_IMF_ENUM_DDS */ \ @@ -338,7 +347,7 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { R_IMF_ENUM_TAGA \ R_IMF_ENUM_TAGA_RAW{0, "", 0, " ", NULL}, \ R_IMF_ENUM_CINEON R_IMF_ENUM_DPX R_IMF_ENUM_EXR_MULTILAYER R_IMF_ENUM_EXR R_IMF_ENUM_HDR \ - R_IMF_ENUM_TIFF + R_IMF_ENUM_TIFF R_IMF_ENUM_WEBP #ifdef RNA_RUNTIME static const EnumPropertyItem image_only_type_items[] = { @@ -633,6 +642,7 @@ const EnumPropertyItem rna_enum_transform_orientation_items[] = { # include "BKE_gpencil.h" # include "BKE_idprop.h" # include "BKE_image.h" +# include "BKE_image_format.h" # include "BKE_layer.h" # include "BKE_main.h" # include "BKE_mesh.h" @@ -1439,6 +1449,35 @@ static const EnumPropertyItem *rna_ImageFormatSettings_exr_codec_itemf(bContext } # endif + +static bool rna_ImageFormatSettings_has_linear_colorspace_get(PointerRNA *ptr) +{ + ImageFormatData *imf = (ImageFormatData *)ptr->data; + return BKE_imtype_requires_linear_float(imf->imtype); +} + +static void rna_ImageFormatSettings_color_management_set(PointerRNA *ptr, int value) +{ + ImageFormatData *imf = (ImageFormatData *)ptr->data; + + if (imf->color_management != value) { + imf->color_management = value; + + /* Copy from scene when enabling override. */ + if (imf->color_management == R_IMF_COLOR_MANAGEMENT_OVERRIDE) { + ID *owner_id = ptr->owner_id; + if (owner_id && GS(owner_id->name) == ID_NT) { + /* For compositing nodes, find the corresponding scene. */ + const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(owner_id); + owner_id = type_info->owner_get(G_MAIN, owner_id); + } + if (owner_id && GS(owner_id->name) == ID_SCE) { + BKE_image_format_color_management_copy_from_scene(imf, (Scene *)owner_id); + } + } + } +} + static int rna_SceneRender_file_ext_length(PointerRNA *ptr) { RenderData *rd = (RenderData *)ptr->data; @@ -5457,6 +5496,12 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) }; # endif + static const EnumPropertyItem color_management_items[] = { + {R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE, "FOLLOW_SCENE", 0, "Follow Scene", ""}, + {R_IMF_COLOR_MANAGEMENT_OVERRIDE, "OVERRIDE", 0, "Override", ""}, + {0, NULL, 0, NULL, NULL}, + }; + StructRNA *srna; PropertyRNA *prop; @@ -5615,17 +5660,32 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3D"); /* color management */ + prop = RNA_def_property(srna, "color_management", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, color_management_items); + RNA_def_property_ui_text( + prop, "Color Management", "Which color management settings to use for file saving"); + RNA_def_property_enum_funcs(prop, NULL, "rna_ImageFormatSettings_color_management_set", NULL); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "view_settings", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "view_settings"); RNA_def_property_struct_type(prop, "ColorManagedViewSettings"); RNA_def_property_ui_text( prop, "View Settings", "Color management settings applied on image before saving"); prop = RNA_def_property(srna, "display_settings", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "display_settings"); RNA_def_property_struct_type(prop, "ColorManagedDisplaySettings"); RNA_def_property_ui_text( prop, "Display Settings", "Settings of device saved image would be displayed on"); + + prop = RNA_def_property(srna, "linear_colorspace_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ColorManagedInputColorspaceSettings"); + RNA_def_property_ui_text(prop, "Color Space Settings", "Output color space settings"); + + prop = RNA_def_property(srna, "has_linear_colorspace", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_ImageFormatSettings_has_linear_colorspace_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Has Linear Color Space", "File format expects linear color space"); } static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) @@ -6160,7 +6220,8 @@ static void rna_def_scene_render_data(BlenderRNA *brna) prop = RNA_def_property(srna, "hair_subdiv", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 0, 3); - RNA_def_property_ui_text(prop, "Additional Subdiv", "Additional subdivision along the hair"); + RNA_def_property_ui_text( + prop, "Additional Subdivision", "Additional subdivision along the hair"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update"); /* Performance */ diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 40f3d79b363..97e1f325816 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1518,6 +1518,23 @@ static void rna_def_curves_sculpt(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Distance", "Radius around curves roots in which no new curves can be added"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); + RNA_def_property_ui_text( + prop, "Interpolate Length", "Use length of the curves in close proximity"); + + prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); + RNA_def_property_ui_text( + prop, "Interpolate Shape", "Use shape of the curves in close proximity"); + + prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text( + prop, + "Curve Length", + "Length of newly added curves when it is not interpolated from other curves"); } void RNA_def_sculpt_paint(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c index ae7772fcb62..cefa445740d 100644 --- a/source/blender/makesrna/intern/rna_shader_fx.c +++ b/source/blender/makesrna/intern/rna_shader_fx.c @@ -524,7 +524,6 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "glow_color[3]"); RNA_def_property_range(prop, 0.0, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Effect Opacity"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "select_color", PROP_FLOAT, PROP_COLOR); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 2344aa42838..3839b3ba6a4 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3773,13 +3773,6 @@ static void rna_def_space_outliner(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Filter by Type", "Data-block type to show"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); - prop = RNA_def_property(srna, "use_filter_lib_override", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "filter", SO_FILTER_NO_LIB_OVERRIDE); - RNA_def_property_ui_text(prop, - "Show Library Overrides", - "For libraries with overrides created, show the overridden values"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL); - prop = RNA_def_property(srna, "use_filter_lib_override_system", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "filter", SO_FILTER_SHOW_SYSTEM_OVERRIDES); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_text.c b/source/blender/makesrna/intern/rna_text.c index 3d2df5b7411..e9c67d71ceb 100644 --- a/source/blender/makesrna/intern/rna_text.c +++ b/source/blender/makesrna/intern/rna_text.c @@ -25,7 +25,7 @@ #ifdef RNA_RUNTIME -static void rna_Text_filename_get(PointerRNA *ptr, char *value) +static void rna_Text_filepath_get(PointerRNA *ptr, char *value) { Text *text = (Text *)ptr->data; @@ -37,13 +37,13 @@ static void rna_Text_filename_get(PointerRNA *ptr, char *value) } } -static int rna_Text_filename_length(PointerRNA *ptr) +static int rna_Text_filepath_length(PointerRNA *ptr) { Text *text = (Text *)ptr->data; return (text->filepath) ? strlen(text->filepath) : 0; } -static void rna_Text_filename_set(PointerRNA *ptr, const char *value) +static void rna_Text_filepath_set(PointerRNA *ptr, const char *value) { Text *text = (Text *)ptr->data; @@ -204,7 +204,7 @@ static void rna_def_text(BlenderRNA *brna) prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs( - prop, "rna_Text_filename_get", "rna_Text_filename_length", "rna_Text_filename_set"); + prop, "rna_Text_filepath_get", "rna_Text_filepath_length", "rna_Text_filepath_set"); RNA_def_property_ui_text(prop, "File Path", "Filename of the text file"); prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 3c8f1ee28c9..56d1b48811a 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -21,6 +21,7 @@ #include "BLI_utildefines.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_paint.h" #include "RNA_define.h" @@ -191,8 +192,23 @@ static void rna_Texture_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *pt static void rna_Texture_mapping_update(Main *bmain, Scene *scene, PointerRNA *ptr) { + ID *id = ptr->owner_id; TexMapping *texmap = ptr->data; BKE_texture_mapping_init(texmap); + + if (GS(id->name) == ID_NT) { + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + /* Try to find and tag the node that this #TexMapping belongs to. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + /* This assumes that the #TexMapping is stored at the beginning of the node storage. This is + * generally true, see #NodeTexBase. If the assumption happens to be false, there might be a + * missing update. */ + if (node->storage == texmap) { + BKE_ntree_update_tag_node_property(ntree, node); + } + } + } + rna_Texture_update(bmain, scene, ptr); } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 78e6bfec03f..164564241ed 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -208,7 +208,6 @@ static void rna_userdef_version_get(PointerRNA *ptr, int *value) /** Mark the preferences as being changed so they are saved on exit. */ # define USERDEF_TAG_DIRTY rna_userdef_is_dirty_update_impl() -/** Use single function so we can more easily break-point it. */ void rna_userdef_is_dirty_update_impl(void) { /* We can't use 'ptr->data' because this update function @@ -219,14 +218,11 @@ void rna_userdef_is_dirty_update_impl(void) } } -/** - * Use as a fallback update handler, - * never use 'ptr' unless its type is checked. - */ void rna_userdef_is_dirty_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) { + /* WARNING: never use 'ptr' unless its type is checked. */ rna_userdef_is_dirty_update_impl(); } @@ -2198,7 +2194,7 @@ static void rna_def_userdef_theme_space_view3d(BlenderRNA *brna) srna = RNA_def_struct(brna, "ThemeView3D", NULL); RNA_def_struct_sdna(srna, "ThemeSpace"); RNA_def_struct_clear_flag(srna, STRUCT_UNDO); - RNA_def_struct_ui_text(srna, "Theme 3D View", "Theme settings for the 3D View"); + RNA_def_struct_ui_text(srna, "Theme 3D Viewport", "Theme settings for the 3D viewport"); rna_def_userdef_theme_spaces_gradient(srna); @@ -3959,7 +3955,7 @@ static void rna_def_userdef_themes(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_pointer_sdna(prop, NULL, "space_view3d"); RNA_def_property_struct_type(prop, "ThemeView3D"); - RNA_def_property_ui_text(prop, "3D View", ""); + RNA_def_property_ui_text(prop, "3D Viewport", ""); prop = RNA_def_property(srna, "graph_editor", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); @@ -4571,7 +4567,7 @@ static void rna_def_userdef_view(BlenderRNA *brna) prop = RNA_def_property(srna, "show_tooltips", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_TOOLTIPS); RNA_def_property_ui_text( - prop, "Tooltips", "Display tooltips (when off hold Alt to force display)"); + prop, "Tooltips", "Display tooltips (when disabled, hold Alt to force display)"); prop = RNA_def_property(srna, "show_tooltips_python", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_TOOLTIPS_PYTHON); @@ -4587,14 +4583,17 @@ static void rna_def_userdef_view(BlenderRNA *brna) prop = RNA_def_property(srna, "show_object_info", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_DRAWVIEWINFO); - RNA_def_property_ui_text( - prop, "Display Object Info", "Display objects name and frame number in 3D view"); + RNA_def_property_ui_text(prop, + "Display Object Info", + "Include the name of the active object and the current frame number in " + "the text info overlay"); RNA_def_property_update(prop, 0, "rna_userdef_update"); prop = RNA_def_property(srna, "show_view_name", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_SHOW_VIEWPORTNAME); - RNA_def_property_ui_text( - prop, "Show View Name", "Show the name of the view's direction in each 3D View"); + RNA_def_property_ui_text(prop, + "Display View Name", + "Include the name of the view orientation in the text info overlay"); RNA_def_property_update(prop, 0, "rna_userdef_update"); prop = RNA_def_property(srna, "show_splash", PROP_BOOLEAN, PROP_NONE); @@ -4603,10 +4602,10 @@ static void rna_def_userdef_view(BlenderRNA *brna) prop = RNA_def_property(srna, "show_playback_fps", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_SHOW_FPS); - RNA_def_property_ui_text( - prop, - "Show Playback FPS", - "Show the frames per second screen refresh rate, while animation is played back"); + RNA_def_property_ui_text(prop, + "Display Playback Frame Rate (FPS)", + "Include the number of frames displayed per second in the text info " + "overlay while animation is played back"); RNA_def_property_update(prop, 0, "rna_userdef_update"); USERDEF_TAG_DIRTY_PROPERTY_UPDATE_DISABLE; @@ -4786,9 +4785,10 @@ static void rna_def_userdef_view(BlenderRNA *brna) prop = RNA_def_property(srna, "mini_axis_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, mini_axis_type_items); - RNA_def_property_ui_text(prop, - "Mini Axes Type", - "Show a small rotating 3D axes in the top right corner of the 3D View"); + RNA_def_property_ui_text( + prop, + "Mini Axes Type", + "Show a small rotating 3D axes in the top right corner of the 3D viewport"); RNA_def_property_update(prop, 0, "rna_userdef_gizmo_update"); prop = RNA_def_property(srna, "mini_axis_size", PROP_INT, PROP_PIXEL); @@ -4987,7 +4987,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna) "VIEW", 0, "View", - "Align newly added objects to the active 3D View direction"}, + "Align newly added objects to the active 3D view orientation"}, {USER_ADD_CURSORALIGNED, "CURSOR", 0, @@ -5016,15 +5016,12 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, object_align_items); RNA_def_property_ui_text( - prop, - "Align Object To", - "When adding objects from a 3D View menu, either align them with that view or " - "with the world"); + prop, "Align Object To", "The default alignment for objects added from a 3D viewport menu"); prop = RNA_def_property(srna, "use_enter_edit_mode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_ADD_EDITMODE); RNA_def_property_ui_text( - prop, "Enter Edit Mode", "Enter Edit Mode automatically after adding a new object"); + prop, "Enter Edit Mode", "Enter edit mode automatically after adding a new object"); prop = RNA_def_property(srna, "collection_instance_empty_size", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.001f, FLT_MAX); @@ -5037,7 +5034,9 @@ static void rna_def_userdef_edit(BlenderRNA *brna) prop = RNA_def_property(srna, "use_text_edit_auto_close", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "text_flag", USER_TEXT_EDIT_AUTO_CLOSE); RNA_def_property_ui_text( - prop, "Auto Close", "Auto close relevant characters inside the text editor"); + prop, + "Auto Close Character Pairs", + "Automatically close relevant character pairs when typing in the text editor"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TEXT, NULL); /* Undo */ @@ -5279,10 +5278,10 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Duplicate GPencil", "Causes grease pencil data to be duplicated with the object"); - prop = RNA_def_property(srna, "use_duplicate_hair", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_duplicate_curves", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_CURVES); RNA_def_property_ui_text( - prop, "Duplicate Hair", "Causes hair data to be duplicated with the object"); + prop, "Duplicate Curves", "Causes curves data to be duplicated with the object"); prop = RNA_def_property(srna, "use_duplicate_pointcloud", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_POINTCLOUD); @@ -5539,9 +5538,10 @@ static void rna_def_userdef_system(BlenderRNA *brna) prop = RNA_def_property(srna, "use_edit_mode_smooth_wire", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna( prop, NULL, "gpu_flag", USER_GPU_FLAG_NO_EDIT_MODE_SMOOTH_WIRE); - RNA_def_property_ui_text(prop, - "Edit-Mode Smooth Wires", - "Enable Edit-Mode edge smoothing, reducing aliasing, requires restart"); + RNA_def_property_ui_text( + prop, + "Edit Mode Smooth Wires", + "Enable edit mode edge smoothing, reducing aliasing (requires restart)"); RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); prop = RNA_def_property(srna, "use_region_overlap", PROP_BOOLEAN, PROP_NONE); @@ -5596,11 +5596,7 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "anisotropic_filter"); RNA_def_property_enum_items(prop, anisotropic_items); RNA_def_property_enum_default(prop, 1); - RNA_def_property_ui_text( - prop, - "Anisotropic Filter", - "Quality of the anisotropic filtering (values greater than 1.0 enable anisotropic " - "filtering)"); + RNA_def_property_ui_text(prop, "Anisotropic Filtering", "Quality of anisotropic filtering"); RNA_def_property_update(prop, 0, "rna_userdef_anisotropic_update"); prop = RNA_def_property(srna, "gl_texture_limit", PROP_ENUM, PROP_NONE); @@ -5649,9 +5645,9 @@ static void rna_def_userdef_system(BlenderRNA *brna) prop = RNA_def_property(srna, "use_select_pick_depth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "gpu_flag", USER_GPU_FLAG_NO_DEPT_PICK); RNA_def_property_ui_text(prop, - "OpenGL Depth Picking", - "Use the depth buffer for picking 3D View selection " - "(without this the front most object may not be selected first)"); + "GPU Depth Picking", + "When making a selection in 3D View, use the GPU depth buffer to " + "ensure the frontmost object is selected first"); /* GPU subdivision evaluation. */ @@ -6437,16 +6433,21 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Override Templates", "Enable library override template in the python API"); - prop = RNA_def_property(srna, "use_geometry_nodes_legacy", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_legacy", 1); - RNA_def_property_ui_text( - prop, "Geometry Nodes Legacy", "Enable legacy geometry nodes in the menu"); - prop = RNA_def_property(srna, "use_named_attribute_nodes", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_named_attribute_nodes", 1); RNA_def_property_ui_text(prop, "Named Attribute Nodes", "Enable named attribute nodes in the geometry nodes add menu"); + + prop = RNA_def_property(srna, "use_select_nearest_on_first_click", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_select_nearest_on_first_click", 1); + RNA_def_property_ui_text(prop, + "Object Select Nearest on First Click", + "When enabled, always select the front-most object on the first click"); + + prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1); + RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 88984a652d7..a5e5bf36dcd 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -61,7 +61,7 @@ set(SRC intern/MOD_meshcache_pc2.c intern/MOD_meshcache_util.c intern/MOD_meshdeform.c - intern/MOD_meshsequencecache.c + intern/MOD_meshsequencecache.cc intern/MOD_mirror.c intern/MOD_multires.c intern/MOD_nodes.cc diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 8298f7c92b2..b237f952287 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -397,8 +397,10 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, Object *start_cap_ob = amd->start_cap; if (start_cap_ob && start_cap_ob != ctx->object) { - vgroup_start_cap_remap = BKE_object_defgroup_index_map_create( - start_cap_ob, ctx->object, &vgroup_start_cap_remap_len); + if (start_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) { + vgroup_start_cap_remap = BKE_object_defgroup_index_map_create( + start_cap_ob, ctx->object, &vgroup_start_cap_remap_len); + } start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob, false); if (start_cap_mesh) { @@ -410,8 +412,10 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, } Object *end_cap_ob = amd->end_cap; if (end_cap_ob && end_cap_ob != ctx->object) { - vgroup_end_cap_remap = BKE_object_defgroup_index_map_create( - end_cap_ob, ctx->object, &vgroup_end_cap_remap_len); + if (end_cap_ob->type == OB_MESH && ctx->object->type == OB_MESH) { + vgroup_end_cap_remap = BKE_object_defgroup_index_map_create( + end_cap_ob, ctx->object, &vgroup_end_cap_remap_len); + } end_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(end_cap_ob, false); if (end_cap_mesh) { diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index cbad4ccd662..b6cceade4e2 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -111,6 +111,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, .add_key_index = false, .use_shapekey = false, .active_shapekey = 0, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 52773e6d3ef..915428f99da 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -244,6 +244,7 @@ static BMesh *BMD_mesh_bm_create( BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; + bmesh_from_mesh_params.calc_vert_normal = true; BM_mesh_bm_from_me(bm, mesh_operand_ob, &bmesh_from_mesh_params); if (UNLIKELY(*r_is_flip)) { diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 8434446a074..67515478be5 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -91,6 +91,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * DecimateModifierData *dmd = (DecimateModifierData *)md; Mesh *mesh = meshData, *result = NULL; BMesh *bm; + bool calc_vert_normal; bool calc_face_normal; float *vweights = NULL; @@ -107,18 +108,21 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return mesh; } calc_face_normal = true; + calc_vert_normal = true; break; case MOD_DECIM_MODE_UNSUBDIV: if (dmd->iter == 0) { return mesh; } calc_face_normal = false; + calc_vert_normal = false; break; case MOD_DECIM_MODE_DISSOLVE: if (dmd->angle == 0.0f) { return mesh; } calc_face_normal = true; + calc_vert_normal = false; break; default: return mesh; @@ -160,6 +164,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = calc_face_normal, + .calc_vert_normal = calc_vert_normal, .cd_mask_extra = {.vmask = CD_MASK_ORIGINDEX, .emask = CD_MASK_ORIGINDEX, .pmask = CD_MASK_ORIGINDEX}, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 3570bdda5a9..14431e5ff2e 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -57,6 +57,7 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = calc_face_normals, + .calc_vert_normal = false, .add_key_index = false, .use_shapekey = false, .active_shapekey = 0, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 14af22645e3..cfc5b17f37e 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -4,7 +4,7 @@ * \ingroup modifiers */ -#include <string.h> +#include <cstring> #include "BLI_math_vector.h" #include "BLI_string.h" @@ -60,11 +60,11 @@ static void initData(ModifierData *md) { - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mcmd, modifier)); - mcmd->cache_file = NULL; + mcmd->cache_file = nullptr; mcmd->object_path[0] = '\0'; mcmd->read_flag = MOD_MESHSEQ_READ_ALL; @@ -80,13 +80,13 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla BKE_modifier_copydata_generic(md, target, flag); - tmcmd->reader = NULL; + tmcmd->reader = nullptr; tmcmd->reader_object_path[0] = '\0'; } static void freeData(ModifierData *md) { - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); if (mcmd->reader) { mcmd->reader_object_path[0] = '\0'; @@ -98,10 +98,10 @@ static bool isDisabled(const struct Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams)) { - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); /* leave it up to the modifier to check the file is valid on calculation */ - return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0'); + return (mcmd->cache_file == nullptr) || (mcmd->object_path[0] == '\0'); } static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh) @@ -145,17 +145,17 @@ static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh) static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { #if defined(WITH_USD) || defined(WITH_ALEMBIC) - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); /* Only used to check whether we are operating on org data or not... */ - Mesh *me = (ctx->object->type == OB_MESH) ? ctx->object->data : NULL; + Mesh *me = (ctx->object->type == OB_MESH) ? static_cast<Mesh *>(ctx->object->data) : nullptr; Mesh *org_mesh = mesh; Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); CacheFile *cache_file = mcmd->cache_file; const float frame = DEG_get_ctime(ctx->depsgraph); const float time = BKE_cachefile_time_offset(cache_file, frame, FPS); - const char *err_str = NULL; + const char *err_str = nullptr; if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) { STRNCPY(mcmd->reader_object_path, mcmd->object_path); @@ -196,7 +196,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } - if (me != NULL) { + if (me != nullptr) { MVert *mvert = mesh->mvert; MEdge *medge = mesh->medge; MPoly *mpoly = mesh->mpoly; @@ -205,15 +205,16 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * flags) and duplicate those too. */ if ((me->mvert == mvert) || (me->medge == medge) || (me->mpoly == mpoly)) { /* We need to duplicate data here, otherwise we'll modify org mesh, see T51701. */ - mesh = (Mesh *)BKE_id_copy_ex(NULL, - &mesh->id, - NULL, - LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT | - LIB_ID_CREATE_NO_DEG_TAG | LIB_ID_COPY_NO_PREVIEW); + mesh = reinterpret_cast<Mesh *>( + BKE_id_copy_ex(nullptr, + &mesh->id, + nullptr, + LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT | + LIB_ID_CREATE_NO_DEG_TAG | LIB_ID_COPY_NO_PREVIEW)); } } - Mesh *result = NULL; + Mesh *result = nullptr; switch (cache_file->type) { case CACHEFILE_TYPE_ALEMBIC: { @@ -250,8 +251,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BKE_modifier_set_error(ctx->object, md, "%s", err_str); } - if (!ELEM(result, NULL, mesh) && (mesh != org_mesh)) { - BKE_id_free(NULL, mesh); + if (!ELEM(result, nullptr, mesh) && (mesh != org_mesh)) { + BKE_id_free(nullptr, mesh); mesh = org_mesh; } @@ -265,9 +266,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * static bool dependsOnTime(Scene *scene, ModifierData *md, const int dag_eval_mode) { #if defined(WITH_USD) || defined(WITH_ALEMBIC) - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); /* Do not evaluate animations if using the render engine procedural. */ - return (mcmd->cache_file != NULL) && + return (mcmd->cache_file != nullptr) && !BKE_cache_file_uses_render_procedural(mcmd->cache_file, scene, dag_eval_mode); #else UNUSED_VARS(scene, md, dag_eval_mode); @@ -277,16 +278,16 @@ static bool dependsOnTime(Scene *scene, ModifierData *md, const int dag_eval_mod static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); - walk(userData, ob, (ID **)&mcmd->cache_file, IDWALK_CB_USER); + walk(userData, ob, reinterpret_cast<ID **>(&mcmd->cache_file), IDWALK_CB_USER); } static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); - if (mcmd->cache_file != NULL) { + if (mcmd->cache_file != nullptr) { DEG_add_object_cache_relation( ctx->node, mcmd->cache_file, DEG_OB_COMP_CACHE, "Mesh Cache File"); } @@ -307,12 +308,13 @@ static void panel_draw(const bContext *C, Panel *panel) uiTemplateCacheFile(layout, C, ptr, "cache_file"); if (has_cache_file) { - uiItemPointerR(layout, ptr, "object_path", &cache_file_ptr, "object_paths", NULL, ICON_NONE); + uiItemPointerR( + layout, ptr, "object_path", &cache_file_ptr, "object_paths", nullptr, ICON_NONE); } if (RNA_enum_get(&ob_ptr, "type") == OB_MESH) { - uiItemR(layout, ptr, "read_data", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_vertex_interpolation", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "read_data", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_vertex_interpolation", 0, nullptr, ICON_NONE); } modifier_panel_end(layout, ptr); @@ -332,7 +334,7 @@ static void velocity_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); uiTemplateCacheFileVelocity(layout, &fileptr); - uiItemR(layout, ptr, "velocity_scale", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "velocity_scale", 0, nullptr, ICON_NONE); } static void time_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -387,27 +389,27 @@ static void panelRegister(ARegionType *region_type) { PanelType *panel_type = modifier_panel_register( region_type, eModifierType_MeshSequenceCache, panel_draw); - modifier_subpanel_register(region_type, "time", "Time", NULL, time_panel_draw, panel_type); + modifier_subpanel_register(region_type, "time", "Time", nullptr, time_panel_draw, panel_type); modifier_subpanel_register(region_type, "render_procedural", "Render Procedural", - NULL, + nullptr, render_procedural_panel_draw, panel_type); modifier_subpanel_register( - region_type, "velocity", "Velocity", NULL, velocity_panel_draw, panel_type); + region_type, "velocity", "Velocity", nullptr, velocity_panel_draw, panel_type); modifier_subpanel_register(region_type, "override_layers", "Override Layers", - NULL, + nullptr, override_layers_panel_draw, panel_type); } static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) { - MeshSeqCacheModifierData *msmcd = (MeshSeqCacheModifierData *)md; - msmcd->reader = NULL; + MeshSeqCacheModifierData *msmcd = reinterpret_cast<MeshSeqCacheModifierData *>(md); + msmcd->reader = nullptr; msmcd->reader_object_path[0] = '\0'; } @@ -417,29 +419,30 @@ ModifierTypeInfo modifierType_MeshSequenceCache = { /* structSize */ sizeof(MeshSeqCacheModifierData), /* srna */ &RNA_MeshSequenceCacheModifier, /* type */ eModifierTypeType_Constructive, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs, + /* flags */ + static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs), /* icon */ ICON_MOD_MESHDEFORM, /* TODO: Use correct icon. */ /* copyData */ copyData, - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, + /* deformVerts */ nullptr, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, + /* modifyGeometrySet */ nullptr, /* initData */ initData, - /* requiredDataMask */ NULL, + /* requiredDataMask */ nullptr, /* freeData */ freeData, /* isDisabled */ isDisabled, /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ dependsOnTime, - /* dependsOnNormals */ NULL, + /* dependsOnNormals */ nullptr, /* foreachIDLink */ foreachIDLink, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, /* panelRegister */ panelRegister, - /* blendWrite */ NULL, + /* blendWrite */ nullptr, /* blendRead */ blendRead, }; diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 7cf425c6e27..e94f8e50fec 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -89,9 +89,14 @@ using blender::Array; using blender::ColorGeometry4f; +using blender::CPPType; using blender::destruct_ptr; using blender::float3; using blender::FunctionRef; +using blender::GMutablePointer; +using blender::GMutableSpan; +using blender::GPointer; +using blender::GVArray; using blender::IndexRange; using blender::Map; using blender::MultiValueMap; @@ -104,9 +109,6 @@ using blender::Vector; using blender::bke::OutputAttribute; using blender::fn::Field; using blender::fn::GField; -using blender::fn::GMutablePointer; -using blender::fn::GPointer; -using blender::fn::GVArray; using blender::fn::ValueOrField; using blender::fn::ValueOrFieldCPPType; using blender::nodes::FieldInferencingInterface; @@ -1607,29 +1609,17 @@ static void panel_draw(const bContext *C, Panel *panel) } /* Draw node warnings. */ - bool has_legacy_node = false; if (nmd->runtime_eval_log != nullptr) { const geo_log::ModifierLog &log = *static_cast<geo_log::ModifierLog *>(nmd->runtime_eval_log); log.foreach_node_log([&](const geo_log::NodeLog &node_log) { for (const geo_log::NodeWarning &warning : node_log.warnings()) { - if (warning.type == geo_log::NodeWarningType::Legacy) { - has_legacy_node = true; - } - else if (warning.type != geo_log::NodeWarningType::Info) { + if (warning.type != geo_log::NodeWarningType::Info) { uiItemL(layout, warning.message.c_str(), ICON_ERROR); } } }); } - if (has_legacy_node) { - uiLayout *row = uiLayoutRow(layout, false); - uiItemL(row, TIP_("Node tree has legacy node"), ICON_ERROR); - uiLayout *sub = uiLayoutRow(row, false); - uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); - uiItemO(sub, "", ICON_VIEWZOOM, "NODE_OT_geometry_node_view_legacy"); - } - modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index c4aa023faa6..8e5f9dc429f 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -11,12 +11,12 @@ #include "FN_field.hh" #include "FN_field_cpp_type.hh" -#include "FN_generic_value_map.hh" #include "FN_multi_function.hh" #include "BLT_translation.h" #include "BLI_enumerable_thread_specific.hh" +#include "BLI_generic_value_map.hh" #include "BLI_stack.hh" #include "BLI_task.h" #include "BLI_task.hh" @@ -26,11 +26,8 @@ namespace blender::modifiers::geometry_nodes { -using fn::CPPType; using fn::Field; using fn::GField; -using fn::GValueMap; -using fn::GVArray; using fn::ValueOrField; using fn::ValueOrFieldCPPType; using nodes::GeoNodeExecParams; @@ -980,15 +977,11 @@ class GeometryNodesEvaluator { void execute_geometry_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state) { + using Clock = std::chrono::steady_clock; const bNode &bnode = *node->bnode(); NodeParamsProvider params_provider{*this, node, node_state, run_state}; GeoNodeExecParams params{params_provider}; - if (node->idname().find("Legacy") != StringRef::not_found) { - params.error_message_add(geo_log::NodeWarningType::Legacy, - TIP_("Legacy node will be removed before Blender 4.0")); - } - using Clock = std::chrono::steady_clock; Clock::time_point begin = Clock::now(); bnode.typeinfo->geometry_node_execute(params); Clock::time_point end = Clock::now(); @@ -1004,14 +997,6 @@ class GeometryNodesEvaluator { NodeState &node_state, NodeTaskRunState *run_state) { - if (node->idname().find("Legacy") != StringRef::not_found) { - /* Create geometry nodes params just for creating an error message. */ - NodeParamsProvider params_provider{*this, node, node_state, run_state}; - GeoNodeExecParams params{params_provider}; - params.error_message_add(geo_log::NodeWarningType::Legacy, - TIP_("Legacy node will be removed before Blender 4.0")); - } - LinearAllocator<> &allocator = local_allocators_.local(); bool any_input_is_field = false; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh index e981157da41..cbcbcab5679 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -2,14 +2,13 @@ #pragma once +#include "BLI_generic_pointer.hh" #include "BLI_map.hh" #include "NOD_derived_node_tree.hh" #include "NOD_geometry_nodes_eval_log.hh" #include "NOD_multi_function.hh" -#include "FN_generic_pointer.hh" - #include "DNA_modifier_types.h" #include "FN_multi_function.hh" @@ -19,8 +18,6 @@ namespace geo_log = blender::nodes::geometry_nodes_eval_log; namespace blender::modifiers::geometry_nodes { using namespace nodes::derived_node_tree_types; -using fn::GMutablePointer; -using fn::GPointer; struct GeometryNodesEvaluationParams { blender::LinearAllocator<> allocator; diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index e42223e2ad5..6ab6dc5d4c8 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -450,18 +450,18 @@ static Frame **collect_hull_frames( { SkinNode *f; Frame **hull_frames; - int nbr, i; + int hull_frames_num, i; (*tothullframe) = emap[v].count; hull_frames = MEM_calloc_arrayN( (*tothullframe), sizeof(Frame *), "hull_from_frames.hull_frames"); - i = 0; - for (nbr = 0; nbr < emap[v].count; nbr++) { - const MEdge *e = &medge[emap[v].indices[nbr]]; + hull_frames_num = 0; + for (i = 0; i < emap[v].count; i++) { + const MEdge *e = &medge[emap[v].indices[i]]; f = &frames[BKE_mesh_edge_other_vert(e, v)]; /* Can't have adjacent branch nodes yet */ if (f->totframe) { - hull_frames[i++] = &f->frames[0]; + hull_frames[hull_frames_num++] = &f->frames[0]; } else { (*tothullframe)--; diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index ff25c1afd49..d896cab4688 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -1982,6 +1982,18 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, result->dvert = dvert; } + /* Get vertex crease layer and ensure edge creases are active if vertex creases are found, since + * they will introduce edge creases in the used custom interpolation method. */ + const float *vertex_crease = CustomData_get_layer(&mesh->vdata, CD_CREASE); + if (vertex_crease) { + result->cd_flag |= ME_CDFLAG_EDGE_CREASE; + /* delete all vertex creases in the result if a rim is used. */ + if (do_rim) { + CustomData_free_layers(&result->vdata, CD_CREASE, result->totvert); + result->cd_flag &= (char)(~ME_CDFLAG_VERT_CREASE); + } + } + /* Make_new_verts. */ { gs_ptr = orig_vert_groups_arr; @@ -2105,6 +2117,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, EdgeGroup *g2 = gs; EdgeGroup *last_g = NULL; EdgeGroup *first_g = NULL; + char mv_crease = vertex_crease ? (char)(vertex_crease[i] * 255.0f) : 0; /* Data calculation cache. */ char max_crease; char last_max_crease = 0; @@ -2174,7 +2187,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, medge[edge_index].v2 = g->new_vert; medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER | ((last_flag | flag) & (ME_SEAM | ME_SHARP)); - medge[edge_index].crease = min_cc(last_max_crease, max_crease); + medge[edge_index].crease = max_cc(mv_crease, min_cc(last_max_crease, max_crease)); medge[edge_index++].bweight = max_cc(mv->bweight, min_cc(last_max_bweight, max_bweight)); } @@ -2202,7 +2215,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, medge[edge_index].v2 = first_g->new_vert; medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER | ((last_flag | first_flag) & (ME_SEAM | ME_SHARP)); - medge[edge_index].crease = min_cc(last_max_crease, first_max_crease); + medge[edge_index].crease = max_cc(mv_crease, + min_cc(last_max_crease, first_max_crease)); medge[edge_index++].bweight = max_cc(mv->bweight, min_cc(last_max_bweight, first_max_bweight)); diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index 79dcdf48402..e560a859735 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -61,6 +61,7 @@ static Mesh *triangulate_mesh(Mesh *mesh, &((struct BMeshCreateParams){0}), &((struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = false, .cd_mask_extra = cd_mask_extra, })); diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 6d95a169319..dae7d19844e 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -69,6 +69,7 @@ static Mesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, Mesh * &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = true, + .calc_vert_normal = true, .add_key_index = false, .use_shapekey = false, .active_shapekey = 0, diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index d5d97f7be2e..3d3450d9252 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -143,8 +143,6 @@ void ntreeCompositExecTree(struct Scene *scene, struct RenderData *rd, int rendering, int do_previews, - const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings, const char *view_name); /** diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index a33cdc1b64c..cde4b67e120 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -6,8 +6,6 @@ extern "C" { #endif -void register_node_type_fn_legacy_random_float(void); - void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); void register_node_type_fn_compare(void); diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index be21dd4b88f..064112b7efd 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -15,45 +15,11 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); -void register_node_type_geo_legacy_attribute_proximity(void); -void register_node_type_geo_legacy_attribute_randomize(void); -void register_node_type_geo_legacy_attribute_transfer(void); -void register_node_type_geo_legacy_curve_endpoints(void); -void register_node_type_geo_legacy_curve_reverse(void); -void register_node_type_geo_legacy_curve_set_handles(void); -void register_node_type_geo_legacy_curve_spline_type(void); -void register_node_type_geo_legacy_curve_subdivide(void); -void register_node_type_geo_legacy_curve_to_points(void); -void register_node_type_geo_legacy_delete_geometry(void); -void register_node_type_geo_legacy_edge_split(void); -void register_node_type_geo_legacy_material_assign(void); -void register_node_type_geo_legacy_mesh_to_curve(void); -void register_node_type_geo_legacy_points_to_volume(void); -void register_node_type_geo_legacy_raycast(void); -void register_node_type_geo_legacy_select_by_handle_type(void); -void register_node_type_geo_legacy_select_by_material(void); -void register_node_type_geo_legacy_subdivision_surface(void); -void register_node_type_geo_legacy_volume_to_mesh(void); - void register_node_type_geo_accumulate_field(void); -void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); -void register_node_type_geo_attribute_clamp(void); -void register_node_type_geo_attribute_color_ramp(void); -void register_node_type_geo_attribute_combine_xyz(void); -void register_node_type_geo_attribute_compare(void); -void register_node_type_geo_attribute_convert(void); -void register_node_type_geo_attribute_curve_map(void); void register_node_type_geo_attribute_domain_size(void); -void register_node_type_geo_attribute_fill(void); -void register_node_type_geo_attribute_map_range(void); -void register_node_type_geo_attribute_math(void); -void register_node_type_geo_attribute_mix(void); -void register_node_type_geo_legacy_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_statistic(void); -void register_node_type_geo_attribute_vector_math(void); -void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); @@ -134,12 +100,6 @@ void register_node_type_geo_mesh_subdivide(void); void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_mesh_to_points(void); void register_node_type_geo_object_info(void); -void register_node_type_geo_point_distribute(void); -void register_node_type_geo_point_instance(void); -void register_node_type_geo_point_rotate(void); -void register_node_type_geo_point_scale(void); -void register_node_type_geo_point_separate(void); -void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_vertices(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_proximity(void); @@ -147,7 +107,6 @@ void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); void register_node_type_geo_remove_attribute(void); void register_node_type_geo_rotate_instances(void); -void register_node_type_geo_sample_texture(void); void register_node_type_geo_scale_elements(void); void register_node_type_geo_scale_instances(void); void register_node_type_geo_select_by_handle_type(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 540afc41dba..dc0965f5d71 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -32,21 +32,12 @@ using bke::ReadAttributeLookup; using bke::StrongAnonymousAttributeID; using bke::WeakAnonymousAttributeID; using bke::WriteAttributeLookup; -using fn::CPPType; using fn::Field; using fn::FieldContext; using fn::FieldEvaluator; using fn::FieldInput; using fn::FieldOperation; using fn::GField; -using fn::GMutablePointer; -using fn::GMutableSpan; -using fn::GPointer; -using fn::GSpan; -using fn::GVArray; -using fn::GVArray_GSpan; -using fn::GVMutableArray; -using fn::GVMutableArray_GSpan; using fn::ValueOrField; using geometry_nodes_eval_log::NodeWarningType; diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 7ef149d5dc5..319fcdeebb7 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -18,13 +18,12 @@ #include "BLI_enumerable_thread_specific.hh" #include "BLI_function_ref.hh" +#include "BLI_generic_pointer.hh" #include "BLI_linear_allocator.hh" #include "BLI_map.hh" #include "BKE_geometry_set.hh" -#include "FN_generic_pointer.hh" - #include "NOD_derived_node_tree.hh" #include <chrono> @@ -34,9 +33,6 @@ struct SpaceSpreadsheet; namespace blender::nodes::geometry_nodes_eval_log { -using fn::GMutablePointer; -using fn::GPointer; - /** Contains information about a value that has been computed during geometry nodes evaluation. */ class ValueLog { public: @@ -67,7 +63,7 @@ class GenericValueLog : public ValueLog { class GFieldValueLog : public ValueLog { private: fn::GField field_; - const fn::CPPType &type_; + const CPPType &type_; Vector<std::string> input_tooltips_; public: @@ -83,7 +79,7 @@ class GFieldValueLog : public ValueLog { return input_tooltips_; } - const fn::CPPType &type() const + const CPPType &type() const { return type_; } @@ -144,7 +140,6 @@ enum class NodeWarningType { Error, Warning, Info, - Legacy, }; struct NodeWarning { diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 97a6b8a6e63..0e1f181eff1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -254,8 +254,6 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" ) DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" ) -DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "") - DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "") @@ -273,48 +271,6 @@ DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Sli DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "LEGACY_ATTRIBUTE_COMPARE", LegacyAttributeCompare, "Attribute Compare", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "LEGACY_ATTRIBUTE_CONVERT", LegacyAttributeConvert, "Attribute Convert", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "LEGACY_ATTRIBUTE_CURVE_MAP", LegacyAttributeCurveMap, "Attribute Curve Map", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_FILL, def_geo_attribute_fill, "LEGACY_ATTRIBUTE_FILL", LegacyAttributeFill, "Attribute Fill", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "LEGACY_ATTRIBUTE_MAP_RANGE", LegacyAttributeMapRange, "Attribute Map Range", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "LEGACY_ATTRIBUTE_MATH", LegacyAttributeMath, "Attribute Math", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_legacy_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_REMOVE, 0, "LEGACY_ATTRIBUTE_REMOVE", LegacyAttributeRemove, "Attribute Remove", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_ENDPOINTS, 0, "LEGACY_CURVE_ENDPOINTS", LegacyCurveEndpoints, "Curve Endpoints", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_legacy_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_legacy_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_legacy_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_EDGE_SPLIT, 0, "LEGACY_EDGE_SPLIT", LegacyEdgeSplit, "Edge Split", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_INSTANCE, def_geo_point_instance, "LEGACY_POINT_INSTANCE", LegacyPointInstance, "Point Instance", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGACY_POINT_ROTATE", LegacyRotatePoints, "Point Rotate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") - DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index b8bdd9859e0..57f76f20ac6 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -93,9 +93,9 @@ set(SRC nodes/node_composite_scene_time.cc nodes/node_composite_sepcomb_hsva.cc nodes/node_composite_sepcomb_rgba.cc + nodes/node_composite_sepcomb_xyz.cc nodes/node_composite_sepcomb_ycca.cc nodes/node_composite_sepcomb_yuva.cc - nodes/node_composite_sepcomb_xyz.cc nodes/node_composite_setalpha.cc nodes/node_composite_split_viewer.cc nodes/node_composite_stabilize2d.cc diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 7522ad1efe6..0b75dd9cef3 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -206,14 +206,12 @@ void ntreeCompositExecTree(Scene *scene, RenderData *rd, int rendering, int do_preview, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, const char *view_name) { #ifdef WITH_COMPOSITOR - COM_execute(rd, scene, ntree, rendering, view_settings, display_settings, view_name); + COM_execute(rd, scene, ntree, rendering, view_name); #else - UNUSED_VARS(scene, ntree, rd, rendering, view_settings, display_settings, view_name); + UNUSED_VARS(scene, ntree, rd, rendering, view_name); #endif UNUSED_VARS(do_preview); diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc index 2e1276dda24..1fd6e62b4c5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc +++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc @@ -12,6 +12,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_image_format.h" #include "RNA_access.h" #include "RNA_prototypes.h" @@ -21,9 +22,9 @@ #include "WM_api.h" -#include "node_composite_util.hh" +#include "IMB_openexr.h" -#include "intern/openexr/openexr_multi.h" +#include "node_composite_util.hh" /* **************** OUTPUT FILE ******************** */ @@ -135,7 +136,7 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, } } else { - BKE_imformat_defaults(&sockdata->format); + BKE_image_format_init(&sockdata->format, false); } /* use node data format by default */ sockdata->use_node_format = true; @@ -205,7 +206,7 @@ static void init_output_file(const bContext *C, PointerRNA *ptr) format = &nimf->format; } else { - BKE_imformat_defaults(&nimf->format); + BKE_image_format_init(&nimf->format, false); } /* add one socket by default */ @@ -216,9 +217,13 @@ static void free_output_file(bNode *node) { /* free storage data in sockets */ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; + BKE_image_format_free(&sockdata->format); MEM_freeN(sock->storage); } + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + BKE_image_format_free(&nimf->format); MEM_freeN(node->storage); } @@ -229,6 +234,9 @@ static void copy_output_file(bNodeTree *UNUSED(dest_ntree), bNodeSocket *src_sock, *dest_sock; dest_node->storage = MEM_dupallocN(src_node->storage); + NodeImageMultiFile *dest_nimf = (NodeImageMultiFile *)dest_node->storage; + NodeImageMultiFile *src_nimf = (NodeImageMultiFile *)src_node->storage; + BKE_image_format_copy(&dest_nimf->format, &src_nimf->format); /* duplicate storage data in sockets */ for (src_sock = (bNodeSocket *)src_node->inputs.first, @@ -236,6 +244,9 @@ static void copy_output_file(bNodeTree *UNUSED(dest_ntree), src_sock && dest_sock; src_sock = src_sock->next, dest_sock = (bNodeSocket *)dest_sock->next) { dest_sock->storage = MEM_dupallocN(src_sock->storage); + NodeImageMultiFileSocket *dest_sockdata = (NodeImageMultiFileSocket *)dest_sock->storage; + NodeImageMultiFileSocket *src_sockdata = (NodeImageMultiFileSocket *)src_sock->storage; + BKE_image_format_copy(&dest_sockdata->format, &src_sockdata->format); } } @@ -292,7 +303,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; node_composit_buts_file_output(layout, C, ptr); - uiTemplateImageSettings(layout, &imfptr, false); + uiTemplateImageSettings(layout, &imfptr, true); /* disable stereo output for multilayer, too much work for something that no one will use */ /* if someone asks for that we can implement it */ @@ -411,12 +422,16 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi ICON_NONE); } - col = uiLayoutColumn(layout, false); - uiLayoutSetActive(col, use_node_format == false); - uiTemplateImageSettings(col, &imfptr, false); + if (!use_node_format) { + const bool use_color_management = RNA_boolean_get(&active_input_ptr, "save_as_render"); + + col = uiLayoutColumn(layout, false); + uiTemplateImageSettings(col, &imfptr, use_color_management); - if (is_multiview) { - uiTemplateImageFormatViews(layout, &imfptr, nullptr); + if (is_multiview) { + col = uiLayoutColumn(layout, false); + uiTemplateImageFormatViews(col, &imfptr, nullptr); + } } } } diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt index 7ffc5c71b66..6ccc4c7bf5c 100644 --- a/source/blender/nodes/function/CMakeLists.txt +++ b/source/blender/nodes/function/CMakeLists.txt @@ -18,8 +18,6 @@ set(INC set(SRC - nodes/legacy/node_fn_random_float.cc - nodes/node_fn_align_euler_to_vector.cc nodes/node_fn_boolean_math.cc nodes/node_fn_compare.cc diff --git a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc deleted file mode 100644 index a34cfe578d0..00000000000 --- a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "node_function_util.hh" - -#include "BLI_hash.h" - -namespace blender::nodes::node_fn_random_float_cc { - -static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b) -{ - b.is_function_node(); - b.add_input<decl::Float>(N_("Min")).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); - b.add_output<decl::Float>(N_("Value")); -} - -class RandomFloatFunction : public blender::fn::MultiFunction { - public: - RandomFloatFunction() - { - static blender::fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static blender::fn::MFSignature create_signature() - { - blender::fn::MFSignatureBuilder signature{"Random float"}; - signature.single_input<float>("Min"); - signature.single_input<float>("Max"); - signature.single_input<int>("Seed"); - signature.single_output<float>("Value"); - return signature.build(); - } - - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override - { - const blender::VArray<float> &min_values = params.readonly_single_input<float>(0, "Min"); - const blender::VArray<float> &max_values = params.readonly_single_input<float>(1, "Max"); - const blender::VArray<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(static_cast<uint32_t>(seed)); - values[i] = value * (max_value - min_value) + min_value; - } - } -}; - -static void fn_node_legacy_random_float_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) -{ - static RandomFloatFunction fn; - builder.set_matching_fn(fn); -} - -} // namespace blender::nodes::node_fn_random_float_cc - -void register_node_type_fn_legacy_random_float() -{ - namespace file_ns = blender::nodes::node_fn_random_float_cc; - - static bNodeType ntype; - - fn_node_type_base(&ntype, FN_NODE_LEGACY_RANDOM_FLOAT, "Random Float", 0); - ntype.declare = file_ns::fn_node_legacy_random_float_declare; - ntype.build_multi_function = file_ns::fn_node_legacy_random_float_build_multi_function; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc index 3718ce6f359..a4fc1a6bfd1 100644 --- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -15,11 +15,20 @@ namespace blender::nodes::node_fn_rotate_euler_cc { static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b) { + auto enable_axis_angle = [](bNode &node) { + node.custom1 = FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE; + }; + b.is_function_node(); b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).hide_value(); - b.add_input<decl::Vector>(N_("Rotate By")).subtype(PROP_EULER); - b.add_input<decl::Vector>(N_("Axis")).default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); - b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE); + b.add_input<decl::Vector>(N_("Rotate By")).subtype(PROP_EULER).make_available([](bNode &node) { + node.custom1 = FN_NODE_ROTATE_EULER_TYPE_EULER; + }); + b.add_input<decl::Vector>(N_("Axis")) + .default_value({0.0, 0.0, 1.0}) + .subtype(PROP_XYZ) + .make_available(enable_axis_angle); + b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE).make_available(enable_axis_angle); b.add_output<decl::Vector>(N_("Rotation")); } diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 0e99d8ad646..84280c0889a 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -25,48 +25,6 @@ set(INC set(SRC - nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc - nodes/legacy/node_geo_legacy_attribute_clamp.cc - nodes/legacy/node_geo_legacy_attribute_color_ramp.cc - nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc - nodes/legacy/node_geo_legacy_attribute_compare.cc - nodes/legacy/node_geo_legacy_attribute_convert.cc - nodes/legacy/node_geo_legacy_attribute_curve_map.cc - nodes/legacy/node_geo_legacy_attribute_fill.cc - nodes/legacy/node_geo_legacy_attribute_map_range.cc - nodes/legacy/node_geo_legacy_attribute_math.cc - nodes/legacy/node_geo_legacy_attribute_mix.cc - nodes/legacy/node_geo_legacy_attribute_proximity.cc - nodes/legacy/node_geo_legacy_attribute_randomize.cc - nodes/legacy/node_geo_legacy_attribute_remove.cc - nodes/legacy/node_geo_legacy_attribute_sample_texture.cc - nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc - nodes/legacy/node_geo_legacy_attribute_transfer.cc - nodes/legacy/node_geo_legacy_attribute_vector_math.cc - nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc - nodes/legacy/node_geo_legacy_curve_endpoints.cc - nodes/legacy/node_geo_legacy_curve_reverse.cc - nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc - nodes/legacy/node_geo_legacy_curve_set_handles.cc - nodes/legacy/node_geo_legacy_curve_spline_type.cc - nodes/legacy/node_geo_legacy_curve_subdivide.cc - nodes/legacy/node_geo_legacy_curve_to_points.cc - nodes/legacy/node_geo_legacy_delete_geometry.cc - nodes/legacy/node_geo_legacy_edge_split.cc - nodes/legacy/node_geo_legacy_material_assign.cc - nodes/legacy/node_geo_legacy_mesh_to_curve.cc - nodes/legacy/node_geo_legacy_point_distribute.cc - nodes/legacy/node_geo_legacy_point_instance.cc - nodes/legacy/node_geo_legacy_point_rotate.cc - nodes/legacy/node_geo_legacy_point_scale.cc - nodes/legacy/node_geo_legacy_point_separate.cc - nodes/legacy/node_geo_legacy_point_translate.cc - nodes/legacy/node_geo_legacy_points_to_volume.cc - nodes/legacy/node_geo_legacy_raycast.cc - nodes/legacy/node_geo_legacy_select_by_material.cc - nodes/legacy/node_geo_legacy_subdivision_surface.cc - nodes/legacy/node_geo_legacy_volume_to_mesh.cc - nodes/node_geo_accumulate_field.cc nodes/node_geo_attribute_capture.cc nodes/node_geo_attribute_domain_size.cc @@ -100,9 +58,9 @@ set(SRC nodes/node_geo_curve_to_points.cc nodes/node_geo_curve_trim.cc nodes/node_geo_delete_geometry.cc - nodes/node_geo_duplicate_elements.cc nodes/node_geo_distribute_points_on_faces.cc nodes/node_geo_dual_mesh.cc + nodes/node_geo_duplicate_elements.cc nodes/node_geo_edge_split.cc nodes/node_geo_extrude_mesh.cc nodes/node_geo_field_at_index.cc @@ -197,6 +155,7 @@ set(LIB bf_bmesh bf_functions bf_geometry + bf_nodes ) if(WITH_BULLET) diff --git a/source/blender/nodes/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc index 474bfaa214a..58ded7aadd2 100644 --- a/source/blender/nodes/geometry/node_geometry_exec.cc +++ b/source/blender/nodes/geometry/node_geometry_exec.cc @@ -1,10 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "FN_cpp_type_make.hh" +#include "BLI_cpp_type_make.hh" #include "NOD_geometry_exec.hh" -MAKE_CPP_TYPE(GeometrySet, GeometrySet, CPPTypeFlags::Printable); - -namespace blender::nodes { - -} +BLI_CPP_TYPE_MAKE(GeometrySet, GeometrySet, CPPTypeFlags::Printable); diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index fc1c73d2851..7f9ec329efd 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -14,29 +14,6 @@ namespace blender::nodes { -using bke::GeometryInstanceGroup; - -void update_attribute_input_socket_availabilities(bNodeTree &ntree, - bNode &node, - const StringRef name, - const GeometryNodeAttributeInputMode mode, - const bool name_is_available) -{ - const GeometryNodeAttributeInputMode mode_ = (GeometryNodeAttributeInputMode)mode; - LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { - if (name == socket->name) { - const bool socket_is_available = - name_is_available && - ((socket->type == SOCK_STRING && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) || - (socket->type == SOCK_FLOAT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) || - (socket->type == SOCK_INT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_INTEGER) || - (socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) || - (socket->type == SOCK_RGBA && mode_ == GEO_NODE_ATTRIBUTE_INPUT_COLOR)); - nodeSetSocketAvailability(&ntree, socket, socket_is_available); - } - } -} - std::optional<CustomDataType> node_data_type_to_custom_data_type(const eNodeSocketDatatype type) { switch (type) { diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 101613acc21..5b7211e44b4 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -28,21 +28,6 @@ bool geo_node_poll_default(struct bNodeType *ntype, const char **r_disabled_hint); namespace blender::nodes { -/** - * Update the availability of a group of input sockets with the same name, - * used for switching between attribute inputs or single values. - * - * \param mode: Controls which socket of the group to make available. - * \param name_is_available: If false, make all sockets with this name unavailable. - */ -void update_attribute_input_socket_availabilities(bNodeTree &ntree, - bNode &node, - const StringRef name, - GeometryNodeAttributeInputMode mode, - bool name_is_available = true); - -Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, - AttributeDomain domain); void transform_mesh(Mesh &mesh, const float3 translation, @@ -93,28 +78,6 @@ void separate_geometry(GeometrySet &geometry_set, bool invert, bool &r_is_error); -struct CurveToPointsResults { - int result_size; - MutableSpan<float3> positions; - MutableSpan<float> radii; - MutableSpan<float> tilts; - - Map<AttributeIDRef, GMutableSpan> point_attributes; - - MutableSpan<float3> tangents; - MutableSpan<float3> normals; - MutableSpan<float3> rotations; -}; -/** - * Create references for all result point cloud attributes to simplify accessing them later on. - */ -CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, - const CurveEval &curve); - -void curve_create_default_rotation_attribute(Span<float3> tangents, - Span<float3> normals, - MutableSpan<float3> rotations); - std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc deleted file mode 100644 index eefcfcb2880..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc +++ /dev/null @@ -1,228 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_math_rotation.h" -#include "BLI_task.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_align_rotation_to_vector_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Factor")); - b.add_input<decl::Float>(N_("Factor"), "Factor_001") - .default_value(1.0f) - .min(0.0f) - .max(1.0f) - .subtype(PROP_FACTOR); - b.add_input<decl::String>(N_("Vector")); - b.add_input<decl::Vector>(N_("Vector"), "Vector_001") - .default_value({0.0, 0.0, 1.0}) - .subtype(PROP_ANGLE); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE); - uiLayout *col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "input_type_factor", 0, IFACE_("Factor"), ICON_NONE); - uiItemR(col, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryAlignRotationToVector *node_storage = MEM_cnew<NodeGeometryAlignRotationToVector>( - __func__); - - node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X; - node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - - node->storage = node_storage; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) - node->storage; - update_attribute_input_socket_availabilities( - *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); - update_attribute_input_socket_availabilities( - *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); -} - -static void align_rotations_auto_pivot(const VArray<float3> &vectors, - const VArray<float> &factors, - const float3 local_main_axis, - const MutableSpan<float3> rotations) -{ - threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { - for (const int i : range) { - const float3 vector = vectors[i]; - if (is_zero_v3(vector)) { - continue; - } - - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float3 old_axis; - mul_v3_m3v3(old_axis, old_rotation, local_main_axis); - - const float3 new_axis = math::normalize(vector); - float3 rotation_axis = math::cross_high_precision(old_axis, new_axis); - if (is_zero_v3(rotation_axis)) { - /* The vectors are linearly dependent, so we fall back to another axis. */ - rotation_axis = math::cross_high_precision(old_axis, float3(1, 0, 0)); - if (is_zero_v3(rotation_axis)) { - /* This is now guaranteed to not be zero. */ - rotation_axis = math::cross_high_precision(old_axis, float3(0, 1, 0)); - } - } - - const float full_angle = angle_normalized_v3v3(old_axis, new_axis); - const float angle = factors[i] * full_angle; - - float rotation[3][3]; - axis_angle_to_mat3(rotation, rotation_axis, angle); - - float new_rotation_matrix[3][3]; - mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); - - float3 new_rotation; - mat3_to_eul(new_rotation, new_rotation_matrix); - - rotations[i] = new_rotation; - } - }); -} - -static void align_rotations_fixed_pivot(const VArray<float3> &vectors, - const VArray<float> &factors, - const float3 local_main_axis, - const float3 local_pivot_axis, - const MutableSpan<float3> rotations) -{ - if (local_main_axis == local_pivot_axis) { - /* Can't compute any meaningful rotation angle in this case. */ - return; - } - - threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { - for (const int i : range) { - const float3 vector = vectors[i]; - if (is_zero_v3(vector)) { - continue; - } - - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float3 old_axis; - mul_v3_m3v3(old_axis, old_rotation, local_main_axis); - float3 pivot_axis; - mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); - - float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); - if (full_angle > M_PI) { - /* Make sure the point is rotated as little as possible. */ - full_angle -= 2.0f * M_PI; - } - const float angle = factors[i] * full_angle; - - float rotation[3][3]; - axis_angle_to_mat3(rotation, pivot_axis, angle); - - float new_rotation_matrix[3][3]; - mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); - - float3 new_rotation; - mat3_to_eul(new_rotation, new_rotation_matrix); - - rotations[i] = new_rotation; - } - }); -} - -static void align_rotations_on_component(GeometryComponent &component, - const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *) - node.storage; - - OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>( - "rotation", ATTR_DOMAIN_POINT, {0, 0, 0}); - if (!rotations) { - return; - } - - VArray<float> factors = params.get_input_attribute<float>( - "Factor", component, ATTR_DOMAIN_POINT, 1.0f); - VArray<float3> vectors = params.get_input_attribute<float3>( - "Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1}); - - float3 local_main_axis{0, 0, 0}; - local_main_axis[storage.axis] = 1; - if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) { - align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span()); - } - else { - float3 local_pivot_axis{0, 0, 0}; - local_pivot_axis[storage.pivot_axis - 1] = 1; - align_rotations_fixed_pivot( - vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span()); - } - - rotations.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), - params); - } - if (geometry_set.has<CurveComponent>()) { - align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_align_rotation_to_vector_cc - -void register_node_type_geo_align_rotation_to_vector() -{ - namespace file_ns = blender::nodes::node_geo_legacy_align_rotation_to_vector_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, - "Align Rotation to Vector", - NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage(&ntype, - "NodeGeometryAlignRotationToVector", - node_free_standard_storage, - node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc deleted file mode 100644 index 71cc2332c78..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc +++ /dev/null @@ -1,267 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_clamp_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")); - b.add_input<decl::String>(N_("Result")); - b.add_input<decl::Vector>(N_("Min")); - b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Min"), "Min_001"); - b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f); - b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000); - b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); - b.add_input<decl::Color>(N_("Min"), "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::Color>(N_("Max"), "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeClamp *data = MEM_cnew<NodeAttributeClamp>(__func__); - data->data_type = CD_PROP_FLOAT; - data->operation = NODE_CLAMP_MINMAX; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 3); - bNodeSocket *sock_max_vector = sock_min_vector->next; - bNodeSocket *sock_min_float = sock_max_vector->next; - bNodeSocket *sock_max_float = sock_min_float->next; - bNodeSocket *sock_min_int = sock_max_float->next; - bNodeSocket *sock_max_int = sock_min_int->next; - bNodeSocket *sock_min_color = sock_max_int->next; - bNodeSocket *sock_max_color = sock_min_color->next; - - const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)node->storage; - const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); - nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(ntree, sock_min_color, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(ntree, sock_max_color, data_type == CD_PROP_COLOR); -} - -template<typename T> T clamp_value(const T val, const T min, const T max); - -template<> inline float clamp_value(const float val, const float min, const float max) -{ - return std::min(std::max(val, min), max); -} - -template<> inline int clamp_value(const int val, const int min, const int max) -{ - return std::min(std::max(val, min), max); -} - -template<> inline float3 clamp_value(const float3 val, const float3 min, const float3 max) -{ - float3 tmp; - tmp.x = std::min(std::max(val.x, min.x), max.x); - tmp.y = std::min(std::max(val.y, min.y), max.y); - tmp.z = std::min(std::max(val.z, min.z), max.z); - return tmp; -} - -template<> -inline ColorGeometry4f clamp_value(const ColorGeometry4f val, - const ColorGeometry4f min, - const ColorGeometry4f max) -{ - ColorGeometry4f tmp; - tmp.r = std::min(std::max(val.r, min.r), max.r); - tmp.g = std::min(std::max(val.g, min.g), max.g); - tmp.b = std::min(std::max(val.b, min.b), max.b); - tmp.a = std::min(std::max(val.a, min.a), max.a); - return tmp; -} - -template<typename T> -static void clamp_attribute(const VArray<T> &inputs, - const MutableSpan<T> outputs, - const T min, - const T max) -{ - for (const int i : IndexRange(outputs.size())) { - outputs[i] = clamp_value<T>(inputs[i], min, max); - } -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef source_name, - StringRef result_name) -{ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); - if (source_info) { - return source_info->domain; - } - return ATTR_DOMAIN_POINT; -} - -static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const std::string attribute_name = params.get_input<std::string>("Attribute"); - const std::string result_name = params.get_input<std::string>("Result"); - - if (attribute_name.empty() || result_name.empty()) { - return; - } - - if (!component.attribute_exists(attribute_name)) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + attribute_name + "\""); - return; - } - - const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)params.node().storage; - const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); - const AttributeDomain domain = get_result_domain(component, attribute_name, result_name); - const int operation = static_cast<int>(storage.operation); - - GVArray attribute_input = component.attribute_try_get_for_read( - attribute_name, domain, data_type); - - OutputAttribute attribute_result = component.attribute_try_get_for_output_only( - result_name, domain, data_type); - - if (!attribute_result) { - params.error_message_add(NodeWarningType::Error, - TIP_("Could not find or create attribute with name \"") + - result_name + "\""); - return; - } - - switch (data_type) { - case CD_PROP_FLOAT3: { - float3 min = params.get_input<float3>("Min"); - float3 max = params.get_input<float3>("Max"); - if (operation == NODE_CLAMP_RANGE) { - if (min.x > max.x) { - std::swap(min.x, max.x); - } - if (min.y > max.y) { - std::swap(min.y, max.y); - } - if (min.z > max.z) { - std::swap(min.z, max.z); - } - } - MutableSpan<float3> results = attribute_result.as_span<float3>(); - clamp_attribute<float3>(attribute_input.typed<float3>(), results, min, max); - break; - } - case CD_PROP_FLOAT: { - const float min = params.get_input<float>("Min_001"); - const float max = params.get_input<float>("Max_001"); - MutableSpan<float> results = attribute_result.as_span<float>(); - if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<float>(attribute_input.typed<float>(), results, max, min); - } - else { - clamp_attribute<float>(attribute_input.typed<float>(), results, min, max); - } - break; - } - case CD_PROP_INT32: { - const int min = params.get_input<int>("Min_002"); - const int max = params.get_input<int>("Max_002"); - MutableSpan<int> results = attribute_result.as_span<int>(); - if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<int>(attribute_input.typed<int>(), results, max, min); - } - else { - clamp_attribute<int>(attribute_input.typed<int>(), results, min, max); - } - break; - } - case CD_PROP_COLOR: { - ColorGeometry4f min = params.get_input<ColorGeometry4f>("Min_003"); - ColorGeometry4f max = params.get_input<ColorGeometry4f>("Max_003"); - if (operation == NODE_CLAMP_RANGE) { - if (min.r > max.r) { - std::swap(min.r, max.r); - } - if (min.g > max.g) { - std::swap(min.g, max.g); - } - if (min.b > max.b) { - std::swap(min.b, max.b); - } - if (min.a > max.a) { - std::swap(min.a, max.a); - } - } - MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); - clamp_attribute<ColorGeometry4f>( - attribute_input.typed<ColorGeometry4f>(), results, min, max); - break; - } - default: { - BLI_assert(false); - break; - } - } - - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_clamp_cc - -void register_node_type_geo_attribute_clamp() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_clamp_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - node_type_storage( - &ntype, "NodeAttributeClamp", node_free_standard_storage, node_copy_standard_storage); - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc deleted file mode 100644 index 5a5cadaea12..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" - -#include "BKE_colorband.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_color_ramp_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiTemplateColorRamp(layout, ptr, "color_ramp", false); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeColorRamp *node_storage = MEM_cnew<NodeAttributeColorRamp>(__func__); - BKE_colorband_init(&node_storage->color_ramp, true); - node->storage = node_storage; -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef input_name, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the input attribute's domain if it exists. */ - std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name); - if (source_info) { - return source_info->domain; - } - - return ATTR_DOMAIN_POINT; -} - -static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) -{ - const bNode &bnode = params.node(); - NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)bnode.storage; - const std::string result_name = params.get_input<std::string>("Result"); - const std::string input_name = params.get_input<std::string>("Attribute"); - - /* Always output a color attribute for now. We might want to allow users to customize. - * Using the type of an existing attribute could work, but does not have a real benefit - * currently. */ - const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - - OutputAttribute_Typed<ColorGeometry4f> attribute_result = - component.attribute_try_get_for_output_only<ColorGeometry4f>(result_name, result_domain); - if (!attribute_result) { - return; - } - - VArray<float> attribute_in = component.attribute_get_for_read<float>( - input_name, result_domain, 0.0f); - - MutableSpan<ColorGeometry4f> results = attribute_result.as_span(); - - ColorBand *color_ramp = &node_storage->color_ramp; - threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { - for (const int i : range) { - BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]); - } - }); - - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); - } - if (geometry_set.has<PointCloudComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); - } - if (geometry_set.has<CurveComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_color_ramp_cc - -void register_node_type_geo_attribute_color_ramp() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_color_ramp_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE); - node_type_storage( - &ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc deleted file mode 100644 index ec6c65aa8bc..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_combine_xyz_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("X")); - b.add_input<decl::Float>(N_("X"), "X_001"); - b.add_input<decl::String>(N_("Y")); - b.add_input<decl::Float>(N_("Y"), "Y_001"); - b.add_input<decl::String>(N_("Z")); - b.add_input<decl::Float>(N_("Z"), "Z_001"); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiLayout *col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "input_type_x", 0, IFACE_("X"), ICON_NONE); - uiItemR(col, ptr, "input_type_y", 0, IFACE_("Y"), ICON_NONE); - uiItemR(col, ptr, "input_type_z", 0, IFACE_("Z"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeCombineXYZ *data = MEM_cnew<NodeAttributeCombineXYZ>(__func__); - - data->input_type_x = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - data->input_type_y = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - data->input_type_z = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeAttributeCombineXYZ *node_storage = (NodeAttributeCombineXYZ *)node->storage; - update_attribute_input_socket_availabilities( - *ntree, *node, "X", (GeometryNodeAttributeInputMode)node_storage->input_type_x); - update_attribute_input_socket_availabilities( - *ntree, *node, "Y", (GeometryNodeAttributeInputMode)node_storage->input_type_y); - update_attribute_input_socket_availabilities( - *ntree, *node, "Z", (GeometryNodeAttributeInputMode)node_storage->input_type_z); -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the highest priority domain from existing input attributes, or the default. */ - return params.get_highest_priority_input_domain({"X", "Y", "Z"}, component, ATTR_DOMAIN_POINT); -} - -static void combine_attributes(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const std::string result_name = params.get_input<std::string>("Result"); - if (result_name.empty()) { - return; - } - const AttributeDomain result_domain = get_result_domain(component, params, result_name); - - OutputAttribute_Typed<float3> attribute_result = - component.attribute_try_get_for_output_only<float3>(result_name, result_domain); - if (!attribute_result) { - return; - } - VArray<float> attribute_x = params.get_input_attribute<float>( - "X", component, result_domain, 0.0f); - VArray<float> attribute_y = params.get_input_attribute<float>( - "Y", component, result_domain, 0.0f); - VArray<float> attribute_z = params.get_input_attribute<float>( - "Z", component, result_domain, 0.0f); - - for (const int i : IndexRange(attribute_result->size())) { - const float x = attribute_x[i]; - const float y = attribute_y[i]; - const float z = attribute_z[i]; - attribute_result->set(i, {x, y, z}); - } - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - combine_attributes(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_combine_xyz_cc - -void register_node_type_geo_attribute_combine_xyz() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_combine_xyz_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, - "Attribute Combine XYZ", - NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage( - &ntype, "NodeAttributeCombineXYZ", node_free_standard_storage, node_copy_standard_storage); - - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc deleted file mode 100644 index 2136a07e58e..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc +++ /dev/null @@ -1,345 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "NOD_math_functions.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_compare_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("A")); - b.add_input<decl::Float>(N_("A"), "A_001"); - b.add_input<decl::Vector>(N_("A"), "A_002"); - b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input<decl::String>(N_("B")); - b.add_input<decl::Float>(N_("B"), "B_001"); - b.add_input<decl::Vector>(N_("B"), "B_002"); - b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input<decl::Float>(N_("Threshold")).default_value(0.01f).min(0.0f); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "input_type_a", 0, IFACE_("A"), ICON_NONE); - uiItemR(layout, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeCompare *data = MEM_cnew<NodeAttributeCompare>(__func__); - data->operation = NODE_COMPARE_GREATER_THAN; - data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - node->storage = data; -} - -static bool operation_tests_equality(const NodeAttributeCompare &node_storage) -{ - return ELEM(node_storage.operation, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL); -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node->storage; - update_attribute_input_socket_availabilities( - *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); - update_attribute_input_socket_availabilities( - *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); - - bNodeSocket *socket_threshold = (bNodeSocket *)BLI_findlink(&node->inputs, 9); - nodeSetSocketAvailability(ntree, socket_threshold, operation_tests_equality(*node_storage)); -} - -static void do_math_operation(const VArray<float> &input_a, - const VArray<float> &input_b, - const NodeCompareOperation operation, - MutableSpan<bool> span_result) -{ - const int size = input_a.size(); - - if (try_dispatch_float_math_fl_fl_to_bool( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float a = input_a[i]; - const float b = input_b[i]; - const bool out = math_function(a, b); - span_result[i] = out; - } - })) { - return; - } - - /* The operation is not supported by this node currently. */ - BLI_assert(false); -} - -static void do_equal_operation_float(const VArray<float> &input_a, - const VArray<float> &input_b, - const float threshold, - MutableSpan<bool> span_result) -{ - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const float a = input_a[i]; - const float b = input_b[i]; - span_result[i] = compare_ff(a, b, threshold); - } -} - -static void do_equal_operation_float3(const VArray<float3> &input_a, - const VArray<float3> &input_b, - const float threshold, - MutableSpan<bool> span_result) -{ - const float threshold_squared = pow2f(threshold); - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const float3 a = input_a[i]; - const float3 b = input_b[i]; - span_result[i] = len_squared_v3v3(a, b) < threshold_squared; - } -} - -static void do_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a, - const VArray<ColorGeometry4f> &input_b, - const float threshold, - MutableSpan<bool> span_result) -{ - const float threshold_squared = pow2f(threshold); - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const ColorGeometry4f a = input_a[i]; - const ColorGeometry4f b = input_b[i]; - span_result[i] = len_squared_v4v4(a, b) < threshold_squared; - } -} - -static void do_equal_operation_bool(const VArray<bool> &input_a, - const VArray<bool> &input_b, - const float UNUSED(threshold), - MutableSpan<bool> span_result) -{ - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const bool a = input_a[i]; - const bool b = input_b[i]; - span_result[i] = a == b; - } -} - -static void do_not_equal_operation_float(const VArray<float> &input_a, - const VArray<float> &input_b, - const float threshold, - MutableSpan<bool> span_result) -{ - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const float a = input_a[i]; - const float b = input_b[i]; - span_result[i] = !compare_ff(a, b, threshold); - } -} - -static void do_not_equal_operation_float3(const VArray<float3> &input_a, - const VArray<float3> &input_b, - const float threshold, - MutableSpan<bool> span_result) -{ - const float threshold_squared = pow2f(threshold); - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const float3 a = input_a[i]; - const float3 b = input_b[i]; - span_result[i] = len_squared_v3v3(a, b) >= threshold_squared; - } -} - -static void do_not_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a, - const VArray<ColorGeometry4f> &input_b, - const float threshold, - MutableSpan<bool> span_result) -{ - const float threshold_squared = pow2f(threshold); - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const ColorGeometry4f a = input_a[i]; - const ColorGeometry4f b = input_b[i]; - span_result[i] = len_squared_v4v4(a, b) >= threshold_squared; - } -} - -static void do_not_equal_operation_bool(const VArray<bool> &input_a, - const VArray<bool> &input_b, - const float UNUSED(threshold), - MutableSpan<bool> span_result) -{ - const int size = input_a.size(); - for (const int i : IndexRange(size)) { - const bool a = input_a[i]; - const bool b = input_b[i]; - span_result[i] = a != b; - } -} - -static CustomDataType get_data_type(GeometryComponent &component, - const GeoNodeExecParams ¶ms, - const NodeAttributeCompare &node_storage) -{ - if (operation_tests_equality(node_storage)) { - /* Convert the input attributes to the same data type for the equality tests. Use the higher - * complexity attribute type, otherwise information necessary to the comparison may be lost. */ - return bke::attribute_data_type_highest_complexity({ - params.get_input_attribute_data_type("A", component, CD_PROP_FLOAT), - params.get_input_attribute_data_type("B", component, CD_PROP_FLOAT), - }); - } - - /* Use float compare for every operation besides equality. */ - return CD_PROP_FLOAT; -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the highest priority domain from existing input attributes, or the default. */ - return params.get_highest_priority_input_domain({"A", "B"}, component, ATTR_DOMAIN_POINT); -} - -static void attribute_compare_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node.storage; - const NodeCompareOperation operation = static_cast<NodeCompareOperation>( - node_storage->operation); - const std::string result_name = params.get_input<std::string>("Result"); - - const AttributeDomain result_domain = get_result_domain(component, params, result_name); - - OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>( - result_name, result_domain); - if (!attribute_result) { - return; - } - - const CustomDataType input_data_type = get_data_type(component, params, *node_storage); - - GVArray attribute_a = params.get_input_attribute( - "A", component, result_domain, input_data_type, nullptr); - GVArray attribute_b = params.get_input_attribute( - "B", component, result_domain, input_data_type, nullptr); - - if (!attribute_a || !attribute_b) { - /* Attribute wasn't found. */ - return; - } - - MutableSpan<bool> result_span = attribute_result.as_span(); - - /* Use specific types for correct equality operations, but for other operations we use implicit - * conversions and float comparison. In other words, the comparison is not element-wise. */ - if (operation_tests_equality(*node_storage)) { - const float threshold = params.get_input<float>("Threshold"); - if (operation == NODE_COMPARE_EQUAL) { - if (input_data_type == CD_PROP_FLOAT) { - do_equal_operation_float( - attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span); - } - else if (input_data_type == CD_PROP_FLOAT3) { - do_equal_operation_float3( - attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span); - } - else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(), - attribute_b.typed<ColorGeometry4f>(), - threshold, - result_span); - } - else if (input_data_type == CD_PROP_BOOL) { - do_equal_operation_bool( - attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span); - } - } - else if (operation == NODE_COMPARE_NOT_EQUAL) { - if (input_data_type == CD_PROP_FLOAT) { - do_not_equal_operation_float( - attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span); - } - else if (input_data_type == CD_PROP_FLOAT3) { - do_not_equal_operation_float3( - attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span); - } - else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(), - attribute_b.typed<ColorGeometry4f>(), - threshold, - result_span); - } - else if (input_data_type == CD_PROP_BOOL) { - do_not_equal_operation_bool( - attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span); - } - } - } - else { - do_math_operation( - attribute_a.typed<float>(), attribute_b.typed<float>(), operation, result_span); - } - - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - attribute_compare_calc(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_compare_cc - -void register_node_type_geo_attribute_compare() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_compare_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - node_type_update(&ntype, file_ns::node_update); - node_type_storage( - &ntype, "NodeAttributeCompare", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc deleted file mode 100644 index 5ded4efd4f7..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_convert_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE); - uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeConvert *data = MEM_cnew<NodeAttributeConvert>(__func__); - - data->data_type = CD_AUTO_FROM_NAME; - data->domain = ATTR_DOMAIN_AUTO; - node->storage = data; -} - -static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component, - const StringRef source_name, - const StringRef result_name) -{ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return *result_info; - } - std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); - if (source_info) { - return *source_info; - } - /* The node won't do anything in this case, but we still have to return a value. */ - return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL}; -} - -static bool conversion_can_be_skipped(const GeometryComponent &component, - const StringRef source_name, - const StringRef result_name, - const AttributeDomain result_domain, - const CustomDataType result_type) -{ - if (source_name != result_name) { - return false; - } - std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name); - if (!info) { - return false; - } - if (info->domain != result_domain) { - return false; - } - if (info->data_type != result_type) { - return false; - } - return true; -} - -static void attribute_convert_calc(GeometryComponent &component, - const GeoNodeExecParams ¶ms, - const StringRef source_name, - const StringRef result_name, - const CustomDataType data_type, - const AttributeDomain domain) -{ - const AttributeMetaData auto_info = get_result_domain_and_type( - component, source_name, result_name); - const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain; - const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type : - data_type; - - if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) { - return; - } - - GVArray source_attribute = component.attribute_try_get_for_read( - source_name, result_domain, result_type); - if (!source_attribute) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + source_name + "\""); - return; - } - - OutputAttribute result_attribute = component.attribute_try_get_for_output_only( - result_name, result_domain, result_type); - if (!result_attribute) { - return; - } - - GVArray_GSpan source_span{source_attribute}; - GMutableSpan result_span = result_attribute.as_span(); - - BLI_assert(source_span.size() == result_span.size()); - - const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type); - BLI_assert(cpp_type != nullptr); - - cpp_type->copy_assign_n(source_span.data(), result_span.data(), result_span.size()); - result_attribute.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - const std::string result_name = params.extract_input<std::string>("Result"); - const std::string source_name = params.extract_input<std::string>("Attribute"); - const NodeAttributeConvert &node_storage = *(const NodeAttributeConvert *)params.node().storage; - const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type); - const AttributeDomain domain = static_cast<AttributeDomain>(node_storage.domain); - - if (result_name.empty()) { - params.set_default_remaining_outputs(); - return; - } - - if (geometry_set.has<MeshComponent>()) { - attribute_convert_calc(geometry_set.get_component_for_write<MeshComponent>(), - params, - source_name, - result_name, - data_type, - domain); - } - if (geometry_set.has<PointCloudComponent>()) { - attribute_convert_calc(geometry_set.get_component_for_write<PointCloudComponent>(), - params, - source_name, - result_name, - data_type, - domain); - } - if (geometry_set.has<CurveComponent>()) { - attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(), - params, - source_name, - result_name, - data_type, - domain); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_convert_cc - -void register_node_type_geo_attribute_convert() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_convert_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - node_type_init(&ntype, file_ns::node_init); - node_type_storage( - &ntype, "NodeAttributeConvert", node_free_standard_storage, node_copy_standard_storage); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc deleted file mode 100644 index 800587e2157..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc +++ /dev/null @@ -1,208 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_blenlib.h" -#include "BLI_task.hh" - -#include "BKE_colortools.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_curve_map_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); - bNode *node = (bNode *)ptr->data; - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; - switch (data->data_type) { - case CD_PROP_FLOAT: - uiTemplateCurveMapping(layout, ptr, "curve_vec", 0, false, false, false, false); - break; - case CD_PROP_FLOAT3: - uiTemplateCurveMapping(layout, ptr, "curve_vec", 'v', false, false, false, false); - break; - case CD_PROP_COLOR: - uiTemplateCurveMapping(layout, ptr, "curve_rgb", 'c', false, false, false, false); - break; - } -} - -static void node_free_storage(bNode *node) -{ - if (node->storage) { - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; - BKE_curvemapping_free(data->curve_vec); - BKE_curvemapping_free(data->curve_rgb); - MEM_freeN(node->storage); - } -} - -static void node_copy_storage(bNodeTree *UNUSED(dest_ntree), - bNode *dest_node, - const bNode *src_node) -{ - dest_node->storage = MEM_dupallocN(src_node->storage); - NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage; - NodeAttributeCurveMap *dest_data = (NodeAttributeCurveMap *)dest_node->storage; - dest_data->curve_vec = BKE_curvemapping_copy(src_data->curve_vec); - dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeCurveMap *data = MEM_cnew<NodeAttributeCurveMap>(__func__); - - data->data_type = CD_PROP_FLOAT; - data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f); - data->curve_vec->cur = 3; - data->curve_rgb = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); - node->storage = data; -} - -static void node_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - /* Set the active curve when data type is changed. */ - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; - if (data->data_type == CD_PROP_FLOAT) { - data->curve_vec->cur = 3; - } - else if (data->data_type == CD_PROP_FLOAT3) { - data->curve_vec->cur = 0; - } -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef input_name, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - /* Otherwise use the input attribute's domain if it exists. */ - std::optional<AttributeMetaData> input_info = component.attribute_get_meta_data(input_name); - if (input_info) { - return input_info->domain; - } - - return ATTR_DOMAIN_POINT; -} - -static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) -{ - const bNode &bnode = params.node(); - NodeAttributeCurveMap &node_storage = *(NodeAttributeCurveMap *)bnode.storage; - const std::string result_name = params.get_input<std::string>("Result"); - const std::string input_name = params.get_input<std::string>("Attribute"); - - const CustomDataType result_type = (CustomDataType)node_storage.data_type; - const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - - OutputAttribute attribute_result = component.attribute_try_get_for_output_only( - result_name, result_domain, result_type); - if (!attribute_result) { - return; - } - - switch (result_type) { - case CD_PROP_FLOAT: { - const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; - VArray<float> attribute_in = component.attribute_get_for_read<float>( - input_name, result_domain, float(0.0f)); - MutableSpan<float> results = attribute_result.as_span<float>(); - threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]); - } - }); - break; - } - case CD_PROP_FLOAT3: { - const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; - VArray<float3> attribute_in = component.attribute_get_for_read<float3>( - input_name, result_domain, float3(0.0f)); - MutableSpan<float3> results = attribute_result.as_span<float3>(); - threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]); - } - }); - break; - } - case CD_PROP_COLOR: { - const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; - VArray<ColorGeometry4f> attribute_in = component.attribute_get_for_read<ColorGeometry4f>( - input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); - MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); - threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); - } - }); - break; - } - default: { - BLI_assert_unreachable(); - break; - } - } - - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - const bNode &bnode = params.node(); - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage; - BKE_curvemapping_init(data->curve_vec); - BKE_curvemapping_init(data->curve_rgb); - - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); - } - if (geometry_set.has<PointCloudComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); - } - if (geometry_set.has<CurveComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_curve_map_cc - -void register_node_type_geo_attribute_curve_map() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_curve_map_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE); - node_type_update(&ntype, file_ns::node_update); - node_type_init(&ntype, file_ns::node_init); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_storage( - &ntype, "NodeAttributeCurveMap", file_ns::node_free_storage, file_ns::node_copy_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc deleted file mode 100644 index 21fe8e12e65..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_fill_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")).is_attribute_name(); - b.add_input<decl::Vector>(N_("Value"), "Value"); - b.add_input<decl::Float>(N_("Value"), "Value_001"); - b.add_input<decl::Color>(N_("Value"), "Value_002"); - b.add_input<decl::Bool>(N_("Value"), "Value_003"); - b.add_input<decl::Int>(N_("Value"), "Value_004"); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - node->custom1 = CD_PROP_FLOAT; - node->custom2 = ATTR_DOMAIN_AUTO; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); - bNodeSocket *socket_value_float = socket_value_vector->next; - bNodeSocket *socket_value_color4f = socket_value_float->next; - bNodeSocket *socket_value_boolean = socket_value_color4f->next; - bNodeSocket *socket_value_int32 = socket_value_boolean->next; - - const CustomDataType data_type = static_cast<CustomDataType>(node->custom1); - - nodeSetSocketAvailability(ntree, socket_value_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, socket_value_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, socket_value_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(ntree, socket_value_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(ntree, socket_value_int32, data_type == CD_PROP_INT32); -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name); - if (result_info) { - return result_info->domain; - } - return ATTR_DOMAIN_POINT; -} - -static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const std::string attribute_name = params.get_input<std::string>("Attribute"); - if (attribute_name.empty()) { - return; - } - - const bNode &node = params.node(); - const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); - const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2); - const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? - get_result_domain(component, attribute_name) : - domain; - - OutputAttribute attribute = component.attribute_try_get_for_output_only( - attribute_name, result_domain, data_type); - if (!attribute) { - return; - } - - switch (data_type) { - case CD_PROP_FLOAT: { - const float value = params.get_input<float>("Value_001"); - attribute->fill(&value); - break; - } - case CD_PROP_FLOAT3: { - const float3 value = params.get_input<float3>("Value"); - attribute->fill(&value); - break; - } - case CD_PROP_COLOR: { - const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002"); - attribute->fill(&value); - break; - } - case CD_PROP_BOOL: { - const bool value = params.get_input<bool>("Value_003"); - attribute->fill(&value); - break; - } - case CD_PROP_INT32: { - const int value = params.get_input<int>("Value_004"); - attribute->fill(&value); - break; - } - default: - break; - } - - attribute.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_fill_cc - -void register_node_type_geo_attribute_fill() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_fill_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - ntype.declare = file_ns::node_declare; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc deleted file mode 100644 index 05440b09442..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc +++ /dev/null @@ -1,422 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_math_base_safe.h" -#include "BLI_task.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_map_range_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")); - b.add_input<decl::String>(N_("Result")); - b.add_input<decl::Float>(N_("From Min")); - b.add_input<decl::Float>(N_("From Max")).default_value(1.0f); - b.add_input<decl::Float>(N_("To Min")); - b.add_input<decl::Float>(N_("To Max")).default_value(1.0f); - b.add_input<decl::Float>(N_("Steps")).default_value(4.0f); - b.add_input<decl::Vector>(N_("From Min"), "From Min_001"); - b.add_input<decl::Vector>(N_("From Max"), "From Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Vector>(N_("To Min"), "To Min_001"); - b.add_input<decl::Vector>(N_("To Max"), "To Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Vector>(N_("Steps"), "Steps_001").default_value({4.0f, 4.0f, 4.0f}); - b.add_input<decl::Bool>(N_("Clamp")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeMapRange *data = MEM_cnew<NodeAttributeMapRange>(__func__); - data->data_type = CD_PROP_FLOAT; - data->interpolation_type = NODE_MAP_RANGE_LINEAR; - - node->storage = data; -} -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node->storage; - - bNodeSocket *sock_from_min_float = (bNodeSocket *)BLI_findlink(&node->inputs, 3); - bNodeSocket *sock_from_max_float = sock_from_min_float->next; - bNodeSocket *sock_to_min_float = sock_from_max_float->next; - bNodeSocket *sock_to_max_float = sock_to_min_float->next; - bNodeSocket *sock_steps_float = sock_to_max_float->next; - - bNodeSocket *sock_from_min_vector = sock_steps_float->next; - bNodeSocket *sock_from_max_vector = sock_from_min_vector->next; - bNodeSocket *sock_to_min_vector = sock_from_max_vector->next; - bNodeSocket *sock_to_max_vector = sock_to_min_vector->next; - bNodeSocket *sock_steps_vector = sock_to_max_vector->next; - - bNodeSocket *sock_clamp = sock_steps_vector->next; - - const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type); - - nodeSetSocketAvailability(ntree, - sock_clamp, - node_storage.interpolation_type == NODE_MAP_RANGE_LINEAR || - node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED); - - nodeSetSocketAvailability(ntree, sock_from_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, sock_from_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, sock_to_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, sock_to_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, - sock_steps_float, - data_type == CD_PROP_FLOAT && - node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED); - - nodeSetSocketAvailability(ntree, sock_from_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, sock_from_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, sock_to_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, sock_to_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, - sock_steps_vector, - data_type == CD_PROP_FLOAT3 && - node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED); -} - -static float map_linear(const float value, - const float min_from, - const float max_from, - const float min_to, - const float max_to) -{ - /* First we calculate a fraction that measures how far along - * the [min_from, max_from] interval the value lies. - * - * value - * min_from [------>|------------------------] max_from - * factor (e.g. 0.25) - * - * Then to find where the value is mapped, we add the same fraction - * of the [min_to, max_to] interval to min_to. - * - * min_to [--->|-----------] max_to - * v - * min_to + (max_to - min_to) * factor - */ - const float factor = safe_divide(value - min_from, max_from - min_from); - return min_to + factor * (max_to - min_to); -} - -static float map_stepped(const float value, - const float min_from, - const float max_from, - const float min_to, - const float max_to, - const float steps) -{ - /* First the factor is calculated here in the same way as for the linear mapping. - * - * Then the factor is mapped to multiples of 1.0 / steps. - * This is best understood with a few examples. Assume steps == 3. - * ____________________________________ - * | factor | * 4.0 | floor() | / 3.0 | - * |--------|-------|---------|-------| - * | 0.0 | 0.0 | 0.0 | 0.0 | - * | 0.1 | 0.4 | 0.0 | 0.0 | - * | 0.25 | 1.0 | 1.0 | 0.333 | - * | 0.45 | 1.8 | 1.0 | 0.333 | - * | 0.5 | 2.0 | 2.0 | 0.666 | - * | 0.55 | 2.2 | 2.0 | 0.666 | - * | 0.999 | 3.999 | 3.0 | 1.0 | - * | 1.0 | 4.0 | 4.0 | 1.333 | - * ------------------------------------ - * Note that the factor is not always mapped the closest multiple of 1.0 /steps. - */ - const float factor = safe_divide(value - min_from, max_from - min_from); - const float factor_mapped = safe_divide(floorf(factor * (steps + 1.0f)), steps); - return min_to + factor_mapped * (max_to - min_to); -} - -static float smoothstep_polynomial(float x) -{ - /* This polynomial is only meant to be used for the [0, 1] range. */ - return (3.0f - 2.0f * x) * (x * x); -} - -static float map_smoothstep(const float value, - const float min_from, - const float max_from, - const float min_to, - const float max_to) -{ - const float factor = safe_divide(value - min_from, max_from - min_from); - const float factor_clamped = std::clamp(factor, 0.0f, 1.0f); - const float factor_mapped = smoothstep_polynomial(factor_clamped); - return min_to + factor_mapped * (max_to - min_to); -} - -static float smootherstep_polynomial(float x) -{ - /* This polynomial is only meant to be used for the [0, 1] range. */ - return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); -} - -static float map_smootherstep(const float value, - const float min_from, - const float max_from, - const float min_to, - const float max_to) -{ - const float factor = safe_divide(value - min_from, max_from - min_from); - const float factor_clamped = std::clamp(factor, 0.0f, 1.0f); - const float factor_mapped = smootherstep_polynomial(factor_clamped); - return min_to + factor_mapped * (max_to - min_to); -} - -static void map_range_float(const VArray<float> &attribute_input, - MutableSpan<float> results, - const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage; - const int interpolation_type = node_storage.interpolation_type; - const float min_from = params.get_input<float>("From Min"); - const float max_from = params.get_input<float>("From Max"); - const float min_to = params.get_input<float>("To Min"); - const float max_to = params.get_input<float>("To Max"); - - VArray_Span<float> span{attribute_input}; - - switch (interpolation_type) { - case NODE_MAP_RANGE_LINEAR: { - threading::parallel_for(span.index_range(), 2048, [&](IndexRange range) { - for (const int i : range) { - results[i] = map_linear(span[i], min_from, max_from, min_to, max_to); - } - }); - break; - } - case NODE_MAP_RANGE_STEPPED: { - const float steps = params.get_input<float>("Steps"); - threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps); - } - }); - break; - } - case NODE_MAP_RANGE_SMOOTHSTEP: { - threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to); - } - }); - break; - } - case NODE_MAP_RANGE_SMOOTHERSTEP: { - threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to); - } - }); - break; - } - } - - if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) && - params.get_input<bool>("Clamp")) { - /* Users can specify min_to > max_to, but clamping expects min < max. */ - const float clamp_min = min_to < max_to ? min_to : max_to; - const float clamp_max = min_to < max_to ? max_to : min_to; - - threading::parallel_for(results.index_range(), 2048, [&](IndexRange range) { - for (const int i : range) { - results[i] = std::clamp(results[i], clamp_min, clamp_max); - } - }); - } -} - -static void map_range_float3(const VArray<float3> &attribute_input, - const MutableSpan<float3> results, - const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage; - const int interpolation_type = node_storage.interpolation_type; - const float3 min_from = params.get_input<float3>("From Min_001"); - const float3 max_from = params.get_input<float3>("From Max_001"); - const float3 min_to = params.get_input<float3>("To Min_001"); - const float3 max_to = params.get_input<float3>("To Max_001"); - - VArray_Span<float3> span{attribute_input}; - - switch (interpolation_type) { - case NODE_MAP_RANGE_LINEAR: { - threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } - }); - break; - } - case NODE_MAP_RANGE_STEPPED: { - const float3 steps = params.get_input<float3>("Steps_001"); - threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i].x = map_stepped( - span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x); - results[i].y = map_stepped( - span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y); - results[i].z = map_stepped( - span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z); - } - }); - break; - } - case NODE_MAP_RANGE_SMOOTHSTEP: { - threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } - }); - break; - } - case NODE_MAP_RANGE_SMOOTHERSTEP: { - threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } - }); - break; - } - } - - if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) && - params.get_input<bool>("Clamp")) { - /* Users can specify min_to > max_to, but clamping expects min < max. */ - float3 clamp_min; - float3 clamp_max; - clamp_min.x = min_to.x < max_to.x ? min_to.x : max_to.x; - clamp_max.x = min_to.x < max_to.x ? max_to.x : min_to.x; - clamp_min.y = min_to.y < max_to.y ? min_to.y : max_to.y; - clamp_max.y = min_to.y < max_to.y ? max_to.y : min_to.y; - clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z; - clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z; - - for (int i : results.index_range()) { - clamp_v3_v3v3(results[i], clamp_min, clamp_max); - } - } -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef source_name, - StringRef result_name) -{ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); - if (source_info) { - return source_info->domain; - } - return ATTR_DOMAIN_POINT; -} - -static void map_range_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const std::string input_name = params.get_input<std::string>("Attribute"); - const std::string result_name = params.get_input<std::string>("Result"); - - if (input_name.empty() || result_name.empty()) { - return; - } - - const bNode &node = params.node(); - NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage; - const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type); - - const AttributeDomain domain = get_result_domain(component, input_name, result_name); - - GVArray attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type); - - if (!attribute_input) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + input_name + "\""); - return; - } - - OutputAttribute attribute_result = component.attribute_try_get_for_output_only( - result_name, domain, data_type); - if (!attribute_result) { - params.error_message_add(NodeWarningType::Error, - TIP_("Could not find or create attribute with name \"") + - result_name + "\""); - return; - } - - switch (data_type) { - case CD_PROP_FLOAT: { - map_range_float(attribute_input.typed<float>(), attribute_result.as_span<float>(), params); - break; - } - case CD_PROP_FLOAT3: { - map_range_float3( - attribute_input.typed<float3>(), attribute_result.as_span<float3>(), params); - break; - } - default: - BLI_assert_unreachable(); - } - - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - if (geometry_set.has<MeshComponent>()) { - map_range_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_map_range_cc - -void register_node_type_geo_attribute_map_range() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_map_range_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE); - ntype.geometry_node_execute = file_ns::node_geo_exec; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage( - &ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.draw_buttons = file_ns::fn_attribute_map_range_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc deleted file mode 100644 index 9e909ec749b..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc +++ /dev/null @@ -1,308 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" - -#include "RNA_enum_types.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "NOD_math_functions.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_math_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("A")); - b.add_input<decl::Float>(N_("A"), "A_001"); - b.add_input<decl::String>(N_("B")); - b.add_input<decl::Float>(N_("B"), "B_001"); - b.add_input<decl::String>(N_("C")); - b.add_input<decl::Float>(N_("C"), "C_001"); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static bool operation_use_input_c(const NodeMathOperation operation) -{ - return ELEM(operation, - NODE_MATH_MULTIPLY_ADD, - NODE_MATH_SMOOTH_MIN, - NODE_MATH_SMOOTH_MAX, - NODE_MATH_WRAP, - NODE_MATH_COMPARE); -} - -static bool operation_use_input_b(const NodeMathOperation operation) -{ - switch (operation) { - case NODE_MATH_ADD: - case NODE_MATH_SUBTRACT: - case NODE_MATH_MULTIPLY: - case NODE_MATH_DIVIDE: - case NODE_MATH_POWER: - case NODE_MATH_LOGARITHM: - case NODE_MATH_MINIMUM: - case NODE_MATH_MAXIMUM: - case NODE_MATH_LESS_THAN: - case NODE_MATH_GREATER_THAN: - case NODE_MATH_MODULO: - case NODE_MATH_ARCTAN2: - case NODE_MATH_SNAP: - case NODE_MATH_WRAP: - case NODE_MATH_COMPARE: - case NODE_MATH_MULTIPLY_ADD: - case NODE_MATH_PINGPONG: - case NODE_MATH_SMOOTH_MIN: - case NODE_MATH_SMOOTH_MAX: - return true; - case NODE_MATH_SINE: - case NODE_MATH_COSINE: - case NODE_MATH_TANGENT: - case NODE_MATH_ARCSINE: - case NODE_MATH_ARCCOSINE: - case NODE_MATH_ARCTANGENT: - case NODE_MATH_ROUND: - case NODE_MATH_ABSOLUTE: - case NODE_MATH_FLOOR: - case NODE_MATH_CEIL: - case NODE_MATH_FRACTION: - case NODE_MATH_SQRT: - case NODE_MATH_INV_SQRT: - case NODE_MATH_SIGN: - case NODE_MATH_EXPONENT: - case NODE_MATH_RADIANS: - case NODE_MATH_DEGREES: - case NODE_MATH_SINH: - case NODE_MATH_COSH: - case NODE_MATH_TANH: - case NODE_MATH_TRUNC: - return false; - } - BLI_assert(false); - return false; -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = (bNode *)ptr->data; - NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage; - NodeMathOperation operation = (NodeMathOperation)node_storage->operation; - - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); - - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "input_type_a", 0, IFACE_("A"), ICON_NONE); - if (operation_use_input_b(operation)) { - uiItemR(layout, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE); - } - if (operation_use_input_c(operation)) { - uiItemR(layout, ptr, "input_type_c", 0, IFACE_("C"), ICON_NONE); - } -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeMath *data = MEM_cnew<NodeAttributeMath>(__func__); - - data->operation = NODE_MATH_ADD; - data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - node->storage = data; -} - -static void geo_node_math_label(const bNodeTree *UNUSED(ntree), - const bNode *node, - char *label, - int maxlen) -{ - NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; - const char *name; - bool enum_label = RNA_enum_name(rna_enum_node_math_items, node_storage.operation, &name); - if (!enum_label) { - name = "Unknown"; - } - BLI_strncpy(label, IFACE_(name), maxlen); -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; - NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage.operation); - - update_attribute_input_socket_availabilities( - *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "B", - (GeometryNodeAttributeInputMode)node_storage.input_type_b, - operation_use_input_b(operation)); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "C", - (GeometryNodeAttributeInputMode)node_storage.input_type_c, - operation_use_input_c(operation)); -} - -static void do_math_operation(const VArray<float> &span_a, - const VArray<float> &span_b, - const VArray<float> &span_c, - MutableSpan<float> span_result, - const NodeMathOperation operation) -{ - bool success = try_dispatch_float_math_fl_fl_fl_to_fl( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) { - for (const int i : range) { - span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); - } - }); - }); - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation(const VArray<float> &span_a, - const VArray<float> &span_b, - MutableSpan<float> span_result, - const NodeMathOperation operation) -{ - bool success = try_dispatch_float_math_fl_fl_to_fl( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { - for (const int i : range) { - span_result[i] = math_function(span_a[i], span_b[i]); - } - }); - }); - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation(const VArray<float> &span_input, - MutableSpan<float> span_result, - const NodeMathOperation operation) -{ - bool success = try_dispatch_float_math_fl_to_fl( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { - for (const int i : range) { - span_result[i] = math_function(span_input[i]); - } - }); - }); - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - const NodeMathOperation operation, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the highest priority domain from existing input attributes, or the default. */ - const AttributeDomain default_domain = ATTR_DOMAIN_POINT; - if (operation_use_input_b(operation)) { - if (operation_use_input_c(operation)) { - return params.get_highest_priority_input_domain({"A", "B", "C"}, component, default_domain); - } - return params.get_highest_priority_input_domain({"A", "B"}, component, default_domain); - } - return params.get_highest_priority_input_domain({"A"}, component, default_domain); -} - -static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - const NodeAttributeMath *node_storage = (const NodeAttributeMath *)node.storage; - const NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage->operation); - const std::string result_name = params.get_input<std::string>("Result"); - - /* The result type of this node is always float. */ - const AttributeDomain result_domain = get_result_domain( - component, params, operation, result_name); - - OutputAttribute_Typed<float> attribute_result = - component.attribute_try_get_for_output_only<float>(result_name, result_domain); - if (!attribute_result) { - return; - } - - VArray<float> attribute_a = params.get_input_attribute<float>( - "A", component, result_domain, 0.0f); - - MutableSpan<float> result_span = attribute_result.as_span(); - - /* Note that passing the data with `get_internal_span<float>()` works - * because the attributes were accessed with #CD_PROP_FLOAT. */ - if (operation_use_input_b(operation)) { - VArray<float> attribute_b = params.get_input_attribute<float>( - "B", component, result_domain, 0.0f); - if (operation_use_input_c(operation)) { - VArray<float> attribute_c = params.get_input_attribute<float>( - "C", component, result_domain, 0.0f); - do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation); - } - else { - do_math_operation(attribute_a, attribute_b, result_span, operation); - } - } - else { - do_math_operation(attribute_a, result_span, operation); - } - - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_math_cc - -void register_node_type_geo_attribute_math() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_math_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - ntype.labelfunc = file_ns::geo_node_math_label; - node_type_update(&ntype, file_ns::node_update); - node_type_init(&ntype, file_ns::node_init); - node_type_storage( - &ntype, "NodeAttributeMath", node_free_standard_storage, node_copy_standard_storage); - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc deleted file mode 100644 index aab33e1ef01..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc +++ /dev/null @@ -1,245 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" - -#include "BKE_material.h" - -#include "DNA_material_types.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_mix_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Factor")); - b.add_input<decl::Float>(N_("Factor"), "Factor_001") - .default_value(0.5f) - .min(0.0f) - .max(1.0f) - .subtype(PROP_FACTOR); - b.add_input<decl::String>(N_("A")); - b.add_input<decl::Float>(N_("A"), "A_001"); - b.add_input<decl::Vector>(N_("A"), "A_002"); - b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::String>(N_("B")); - b.add_input<decl::Float>(N_("B"), "B_001"); - b.add_input<decl::Vector>(N_("B"), "B_002"); - b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE); - uiLayout *col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "input_type_factor", 0, IFACE_("Factor"), ICON_NONE); - uiItemR(col, ptr, "input_type_a", 0, IFACE_("A"), ICON_NONE); - uiItemR(col, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeMix *data = MEM_cnew<NodeAttributeMix>("attribute mix node"); - data->blend_type = MA_RAMP_BLEND; - data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage; - update_attribute_input_socket_availabilities( - *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); - update_attribute_input_socket_availabilities( - *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); - update_attribute_input_socket_availabilities( - *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); -} - -static void do_mix_operation_float(const int blend_mode, - const VArray<float> &factors, - const VArray<float> &inputs_a, - const VArray<float> &inputs_b, - VMutableArray<float> &results) -{ - const int size = results.size(); - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float factor = factors[i]; - float3 a{inputs_a[i]}; - const float3 b{inputs_b[i]}; - ramp_blend(blend_mode, a, factor, b); - const float result = a.x; - results.set(i, result); - } - }); -} - -static void do_mix_operation_float3(const int blend_mode, - const VArray<float> &factors, - const VArray<float3> &inputs_a, - const VArray<float3> &inputs_b, - VMutableArray<float3> &results) -{ - const int size = results.size(); - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float factor = factors[i]; - float3 a = inputs_a[i]; - const float3 b = inputs_b[i]; - ramp_blend(blend_mode, a, factor, b); - results.set(i, a); - } - }); -} - -static void do_mix_operation_color4f(const int blend_mode, - const VArray<float> &factors, - const VArray<ColorGeometry4f> &inputs_a, - const VArray<ColorGeometry4f> &inputs_b, - VMutableArray<ColorGeometry4f> &results) -{ - const int size = results.size(); - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float factor = factors[i]; - ColorGeometry4f a = inputs_a[i]; - const ColorGeometry4f b = inputs_b[i]; - ramp_blend(blend_mode, a, factor, b); - results.set(i, a); - } - }); -} - -static void do_mix_operation(const CustomDataType result_type, - int blend_mode, - const VArray<float> &attribute_factor, - const GVArray &attribute_a, - const GVArray &attribute_b, - GVMutableArray &attribute_result) -{ - if (result_type == CD_PROP_FLOAT) { - VMutableArray<float> result = attribute_result.typed<float>(); - do_mix_operation_float(blend_mode, - attribute_factor, - attribute_a.typed<float>(), - attribute_b.typed<float>(), - result); - } - else if (result_type == CD_PROP_FLOAT3) { - VMutableArray<float3> result = attribute_result.typed<float3>(); - do_mix_operation_float3(blend_mode, - attribute_factor, - attribute_a.typed<float3>(), - attribute_b.typed<float3>(), - result); - } - else if (result_type == CD_PROP_COLOR) { - VMutableArray<ColorGeometry4f> result = attribute_result.typed<ColorGeometry4f>(); - do_mix_operation_color4f(blend_mode, - attribute_factor, - attribute_a.typed<ColorGeometry4f>(), - attribute_b.typed<ColorGeometry4f>(), - result); - } -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the highest priority domain from existing input attributes, or the default. */ - return params.get_highest_priority_input_domain({"A", "B"}, component, ATTR_DOMAIN_POINT); -} - -static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - const NodeAttributeMix *node_storage = (const NodeAttributeMix *)node.storage; - const std::string result_name = params.get_input<std::string>("Result"); - - /* Use the highest complexity data type among the inputs and outputs, that way the node will - * never "remove information". Use CD_PROP_BOOL as the lowest complexity data type, but in any - * real situation it won't be returned. */ - const CustomDataType result_type = bke::attribute_data_type_highest_complexity({ - params.get_input_attribute_data_type("A", component, CD_PROP_BOOL), - params.get_input_attribute_data_type("B", component, CD_PROP_BOOL), - params.get_input_attribute_data_type("Result", component, CD_PROP_BOOL), - }); - - const AttributeDomain result_domain = get_result_domain(component, params, result_name); - - OutputAttribute attribute_result = component.attribute_try_get_for_output_only( - result_name, result_domain, result_type); - if (!attribute_result) { - return; - } - - VArray<float> attribute_factor = params.get_input_attribute<float>( - "Factor", component, result_domain, 0.5f); - GVArray attribute_a = params.get_input_attribute( - "A", component, result_domain, result_type, nullptr); - GVArray attribute_b = params.get_input_attribute( - "B", component, result_domain, result_type, nullptr); - - do_mix_operation(result_type, - node_storage->blend_type, - attribute_factor, - attribute_a, - attribute_b, - attribute_result.varray()); - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_mix_cc - -void register_node_type_geo_attribute_mix() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_mix_cc; - - static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - ntype.declare = file_ns::node_declare; - ntype.draw_buttons = file_ns::node_layout; - node_type_storage( - &ntype, "NodeAttributeMix", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc deleted file mode 100644 index a3af6200ab8..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc +++ /dev/null @@ -1,237 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" -#include "BLI_timeit.hh" - -#include "DNA_mesh_types.h" - -#include "BKE_bvhutils.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_proximity_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Geometry>(N_("Target")); - b.add_input<decl::String>(N_("Distance")); - b.add_input<decl::String>(N_("Position")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "target_geometry_element", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryAttributeProximity *node_storage = MEM_cnew<NodeGeometryAttributeProximity>( - __func__); - - node_storage->target_geometry_element = GEO_NODE_PROXIMITY_TARGET_FACES; - node->storage = node_storage; -} - -static void calculate_mesh_proximity(const VArray<float3> &positions, - const Mesh &mesh, - const GeometryNodeAttributeProximityTargetType type, - MutableSpan<float> r_distances, - MutableSpan<float3> r_locations) -{ - BVHTreeFromMesh bvh_data; - switch (type) { - case GEO_NODE_PROXIMITY_TARGET_POINTS: - BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_VERTS, 2); - break; - case GEO_NODE_PROXIMITY_TARGET_EDGES: - BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_EDGES, 2); - break; - case GEO_NODE_PROXIMITY_TARGET_FACES: - BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); - break; - } - - if (bvh_data.tree == nullptr) { - return; - } - - threading::parallel_for(positions.index_range(), 512, [&](IndexRange range) { - BVHTreeNearest nearest; - copy_v3_fl(nearest.co, FLT_MAX); - nearest.index = -1; - - for (int i : range) { - /* Use the distance to the last found point as upper bound to speedup the bvh lookup. */ - nearest.dist_sq = math::distance_squared(float3(nearest.co), positions[i]); - - BLI_bvhtree_find_nearest( - bvh_data.tree, positions[i], &nearest, bvh_data.nearest_callback, &bvh_data); - - if (nearest.dist_sq < r_distances[i]) { - r_distances[i] = nearest.dist_sq; - if (!r_locations.is_empty()) { - r_locations[i] = nearest.co; - } - } - } - }); - - free_bvhtree_from_mesh(&bvh_data); -} - -static void calculate_pointcloud_proximity(const VArray<float3> &positions, - const PointCloud &pointcloud, - MutableSpan<float> r_distances, - MutableSpan<float3> r_locations) -{ - BVHTreeFromPointCloud bvh_data; - BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2); - if (bvh_data.tree == nullptr) { - return; - } - - threading::parallel_for(positions.index_range(), 512, [&](IndexRange range) { - BVHTreeNearest nearest; - copy_v3_fl(nearest.co, FLT_MAX); - nearest.index = -1; - - for (int i : range) { - /* Use the distance to the closest point in the mesh to speedup the pointcloud bvh lookup. - * This is ok because we only need to find the closest point in the pointcloud if it's - * closer than the mesh. */ - nearest.dist_sq = r_distances[i]; - - BLI_bvhtree_find_nearest( - bvh_data.tree, positions[i], &nearest, bvh_data.nearest_callback, &bvh_data); - - if (nearest.dist_sq < r_distances[i]) { - r_distances[i] = nearest.dist_sq; - if (!r_locations.is_empty()) { - r_locations[i] = nearest.co; - } - } - } - }); - - free_bvhtree_from_pointcloud(&bvh_data); -} - -static void attribute_calc_proximity(GeometryComponent &component, - GeometrySet &target, - GeoNodeExecParams ¶ms) -{ - const std::string distance_name = params.get_input<std::string>("Distance"); - OutputAttribute_Typed<float> distance_attribute = - component.attribute_try_get_for_output_only<float>(distance_name, ATTR_DOMAIN_POINT); - - const std::string location_name = params.get_input<std::string>("Position"); - OutputAttribute_Typed<float3> location_attribute = - component.attribute_try_get_for_output_only<float3>(location_name, ATTR_DOMAIN_POINT); - - ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position"); - if (!position_attribute || (!distance_attribute && !location_attribute)) { - return; - } - VArray<float3> positions = position_attribute.varray.typed<float3>(); - const NodeGeometryAttributeProximity &storage = - *(const NodeGeometryAttributeProximity *)params.node().storage; - - Array<float> distances_internal; - MutableSpan<float> distances; - if (distance_attribute) { - distances = distance_attribute.as_span(); - } - else { - /* Theoretically it would be possible to avoid using the distance array when it's not required - * and there is only one component. However, this only adds an allocation and a single float - * comparison per vertex, so it's likely not worth it. */ - distances_internal.reinitialize(positions.size()); - distances = distances_internal; - } - distances.fill(FLT_MAX); - MutableSpan<float3> locations = location_attribute ? location_attribute.as_span() : - MutableSpan<float3>(); - - if (target.has_mesh()) { - calculate_mesh_proximity( - positions, - *target.get_mesh_for_read(), - static_cast<GeometryNodeAttributeProximityTargetType>(storage.target_geometry_element), - distances, - locations); - } - - if (target.has_pointcloud() && - storage.target_geometry_element == GEO_NODE_PROXIMITY_TARGET_POINTS) { - calculate_pointcloud_proximity( - positions, *target.get_pointcloud_for_read(), distances, locations); - } - - if (distance_attribute) { - /* Squared distances are used above to speed up comparisons, - * so do the square roots now if necessary for the output attribute. */ - threading::parallel_for(distances.index_range(), 2048, [&](IndexRange range) { - for (const int i : range) { - distances[i] = std::sqrt(distances[i]); - } - }); - distance_attribute.save(); - } - if (location_attribute) { - location_attribute.save(); - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - /* This isn't required. This node should be rewritten to handle instances - * for the target geometry set. However, the generic BVH API complicates this. */ - geometry_set_target = geometry::realize_instances_legacy(geometry_set_target); - - if (geometry_set.has<MeshComponent>()) { - attribute_calc_proximity( - geometry_set.get_component_for_write<MeshComponent>(), geometry_set_target, params); - } - if (geometry_set.has<PointCloudComponent>()) { - attribute_calc_proximity( - geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params); - } - if (geometry_set.has<CurveComponent>()) { - attribute_calc_proximity( - geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_proximity_cc - -void register_node_type_geo_legacy_attribute_proximity() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_proximity_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_storage(&ntype, - "NodeGeometryAttributeProximity", - node_free_standard_storage, - node_copy_standard_storage); - - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc deleted file mode 100644 index 6a306229b8c..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc +++ /dev/null @@ -1,333 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_hash.h" -#include "BLI_rand.hh" -#include "BLI_task.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes { - -Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, - const AttributeDomain domain) -{ - const int domain_size = component.attribute_domain_size(domain); - - /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */ - GVArray hash_attribute = component.attribute_try_get_for_read("id", domain); - Array<uint32_t> hashes(domain_size); - if (hash_attribute) { - BLI_assert(hashes.size() == hash_attribute.size()); - const CPPType &cpp_type = hash_attribute.type(); - BLI_assert(cpp_type.is_hashable()); - GVArray_GSpan items{hash_attribute}; - threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - hashes[i] = cpp_type.hash(items[i]); - } - }); - } - else { - /* If there is no "id" attribute for per-point variation, just create it here. */ - RandomNumberGenerator rng(0); - for (const int i : hashes.index_range()) { - hashes[i] = rng.get_uint32(); - } - } - - return hashes; -} - -} // namespace blender::nodes - -namespace blender::nodes::node_geo_legacy_attribute_randomize_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")); - b.add_input<decl::Vector>(N_("Min")); - b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Min"), "Min_001"); - b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f); - b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000); - b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); - b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeRandomize *data = MEM_cnew<NodeAttributeRandomize>(__func__); - data->data_type = CD_PROP_FLOAT; - data->domain = ATTR_DOMAIN_POINT; - data->operation = GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); - bNodeSocket *sock_max_vector = sock_min_vector->next; - bNodeSocket *sock_min_float = sock_max_vector->next; - bNodeSocket *sock_max_float = sock_min_float->next; - bNodeSocket *sock_min_int = sock_max_float->next; - bNodeSocket *sock_max_int = sock_min_int->next; - - const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)node->storage; - const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); - nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32); -} - -template<typename T> -T random_value_in_range(const uint32_t id, const uint32_t seed, const T min, const T max); - -template<> -inline float random_value_in_range(const uint32_t id, - const uint32_t seed, - const float min, - const float max) -{ - return BLI_hash_int_2d_to_float(id, seed) * (max - min) + min; -} - -template<> -inline int random_value_in_range(const uint32_t id, - const uint32_t seed, - const int min, - const int max) -{ - return round_fl_to_int( - random_value_in_range<float>(id, seed, static_cast<float>(min), static_cast<float>(max))); -} - -template<> -inline float3 random_value_in_range(const uint32_t id, - const uint32_t seed, - const float3 min, - const float3 max) -{ - const float x = BLI_hash_int_3d_to_float(seed, id, 435109); - const float y = BLI_hash_int_3d_to_float(seed, id, 380867); - const float z = BLI_hash_int_3d_to_float(seed, id, 1059217); - - return float3(x, y, z) * (max - min) + min; -} - -template<typename T> -static void randomize_attribute(MutableSpan<T> span, - const T min, - const T max, - Span<uint32_t> ids, - const uint32_t seed, - const GeometryNodeAttributeRandomizeMode operation) -{ - /* The operations could be templated too, but it doesn't make the code much shorter. */ - switch (operation) { - case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE: - threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = random_value; - } - }); - break; - case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD: - threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] + random_value; - } - }); - break; - case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT: - threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] - random_value; - } - }); - break; - case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY: - threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] * random_value; - } - }); - break; - default: - BLI_assert(false); - break; - } -} - -static void randomize_attribute_bool(MutableSpan<bool> span, - Span<uint32_t> ids, - const uint32_t seed, - const GeometryNodeAttributeRandomizeMode operation) -{ - BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE); - UNUSED_VARS_NDEBUG(operation); - threading::parallel_for(span.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f; - span[i] = random_value; - } - }); -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - const StringRef name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the input domain chosen in the interface. */ - const bNode &node = params.node(); - return static_cast<AttributeDomain>(node.custom2); -} - -static void randomize_attribute_on_component(GeometryComponent &component, - const GeoNodeExecParams ¶ms, - StringRef attribute_name, - const CustomDataType data_type, - const GeometryNodeAttributeRandomizeMode operation, - const int seed) -{ - /* If the node is not in "replace / create" mode and the attribute - * doesn't already exist, don't do the operation. */ - if (operation != GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) { - if (!component.attribute_exists(attribute_name)) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + attribute_name + "\""); - return; - } - } - - const AttributeDomain domain = get_result_domain(component, params, attribute_name); - - OutputAttribute attribute = component.attribute_try_get_for_output( - attribute_name, domain, data_type); - if (!attribute) { - return; - } - - GMutableSpan span = attribute.as_span(); - - Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain); - - switch (data_type) { - case CD_PROP_FLOAT3: { - const float3 min = params.get_input<float3>("Min"); - const float3 max = params.get_input<float3>("Max"); - randomize_attribute<float3>(span.typed<float3>(), min, max, hashes, seed, operation); - break; - } - case CD_PROP_FLOAT: { - const float min = params.get_input<float>("Min_001"); - const float max = params.get_input<float>("Max_001"); - randomize_attribute<float>(span.typed<float>(), min, max, hashes, seed, operation); - break; - } - case CD_PROP_BOOL: { - randomize_attribute_bool(span.typed<bool>(), hashes, seed, operation); - break; - } - case CD_PROP_INT32: { - const int min = params.get_input<int>("Min_002"); - const int max = params.get_input<int>("Max_002"); - randomize_attribute<int>(span.typed<int>(), min, max, hashes, seed, operation); - break; - } - default: { - BLI_assert(false); - break; - } - } - - attribute.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - const std::string attribute_name = params.get_input<std::string>("Attribute"); - if (attribute_name.empty()) { - params.set_default_remaining_outputs(); - return; - } - const int seed = params.get_input<int>("Seed"); - const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)params.node().storage; - const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); - const GeometryNodeAttributeRandomizeMode operation = - static_cast<GeometryNodeAttributeRandomizeMode>(storage.operation); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - randomize_attribute_on_component(geometry_set.get_component_for_write<MeshComponent>(), - params, - attribute_name, - data_type, - operation, - seed); - } - if (geometry_set.has<PointCloudComponent>()) { - randomize_attribute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), - params, - attribute_name, - data_type, - operation, - seed); - } - if (geometry_set.has<CurveComponent>()) { - randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(), - params, - attribute_name, - data_type, - operation, - seed); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_randomize_cc - -void register_node_type_geo_legacy_attribute_randomize() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_randomize_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - node_type_storage( - &ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage); - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_remove.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_remove.cc deleted file mode 100644 index cc7118fd305..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_remove.cc +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_remove_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Attribute")).multi_input(); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void remove_attribute(GeometryComponent &component, - GeoNodeExecParams ¶ms, - Span<std::string> attribute_names) -{ - for (std::string attribute_name : attribute_names) { - if (attribute_name.empty()) { - continue; - } - - if (!component.attribute_try_delete(attribute_name)) { - params.error_message_add(NodeWarningType::Error, - TIP_("Cannot delete attribute with name \"") + attribute_name + - "\""); - } - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute"); - - for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, - GEO_COMPONENT_TYPE_POINT_CLOUD, - GEO_COMPONENT_TYPE_CURVE, - GEO_COMPONENT_TYPE_INSTANCES}) { - if (geometry_set.has(type)) { - remove_attribute(geometry_set.get_component_for_write(type), params, attribute_names); - } - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_remove_cc - -void register_node_type_geo_legacy_attribute_remove() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_remove_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.declare = file_ns::node_declare; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc deleted file mode 100644 index 42719d4bf1a..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc +++ /dev/null @@ -1,124 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_compiler_attrs.h" -#include "BLI_task.hh" - -#include "DNA_texture_types.h" - -#include "BKE_texture.h" - -#include "RE_texture.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_sample_texture_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Texture>(N_("Texture")).hide_label(); - b.add_input<decl::String>(N_("Mapping")); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const StringRef result_name, - const StringRef map_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the name of the map attribute. */ - std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name); - if (map_info) { - return map_info->domain; - } - - /* The node won't execute in this case, but we still have to return a value. */ - return ATTR_DOMAIN_POINT; -} - -static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - Tex *texture = params.get_input<Tex *>("Texture"); - if (texture == nullptr) { - return; - } - - const std::string result_attribute_name = params.get_input<std::string>("Result"); - const std::string mapping_name = params.get_input<std::string>("Mapping"); - if (!component.attribute_exists(mapping_name)) { - return; - } - - const AttributeDomain result_domain = get_result_domain( - component, result_attribute_name, mapping_name); - - OutputAttribute_Typed<ColorGeometry4f> attribute_out = - component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name, - result_domain); - if (!attribute_out) { - return; - } - - VArray<float3> mapping_attribute = component.attribute_get_for_read<float3>( - mapping_name, result_domain, {0, 0, 0}); - - MutableSpan<ColorGeometry4f> colors = attribute_out.as_span(); - threading::parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { - for (const int i : range) { - TexResult texture_result = {0}; - const float3 position = mapping_attribute[i]; - /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */ - const float3 remapped_position = position * 2.0f - float3(1.0f); - BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false); - copy_v4_v4(colors[i], texture_result.trgba); - } - }); - - attribute_out.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_sample_texture_cc - -void register_node_type_geo_sample_texture() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_sample_texture_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, - "Attribute Sample Texture", - NODE_CLASS_ATTRIBUTE); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc deleted file mode 100644 index 85ffbf6679b..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_separate_xyz_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Vector")); - b.add_input<decl::Vector>(N_("Vector"), "Vector_001"); - b.add_input<decl::String>(N_("Result X")); - b.add_input<decl::String>(N_("Result Y")); - b.add_input<decl::String>(N_("Result Z")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeSeparateXYZ *data = MEM_cnew<NodeAttributeSeparateXYZ>(__func__); - data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeAttributeSeparateXYZ *node_storage = (NodeAttributeSeparateXYZ *)node->storage; - update_attribute_input_socket_availabilities( - *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type); -} - -static void extract_input(const int index, const Span<float3> &input, MutableSpan<float> result) -{ - for (const int i : result.index_range()) { - /* Get the component of the float3. (0: X, 1: Y, 2: Z). */ - const float component = input[i][index]; - result[i] = component; - } -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - const StringRef name_x, - const StringRef name_y, - const StringRef name_z) -{ - /* Use the highest priority domain from any existing attribute outputs. */ - Vector<AttributeDomain, 3> output_domains; - std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x); - std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y); - std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z); - if (info_x) { - output_domains.append(info_x->domain); - } - if (info_y) { - output_domains.append(info_y->domain); - } - if (info_z) { - output_domains.append(info_z->domain); - } - if (output_domains.size() > 0) { - return bke::attribute_domain_highest_priority(output_domains); - } - - /* Otherwise use the domain of the input attribute, or the default. */ - return params.get_highest_priority_input_domain({"Vector"}, component, ATTR_DOMAIN_POINT); -} - -static void separate_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) -{ - const std::string result_name_x = params.get_input<std::string>("Result X"); - const std::string result_name_y = params.get_input<std::string>("Result Y"); - const std::string result_name_z = params.get_input<std::string>("Result Z"); - if (result_name_x.empty() && result_name_y.empty() && result_name_z.empty()) { - return; - } - - /* The node is only for float3 to float conversions. */ - const AttributeDomain result_domain = get_result_domain( - component, params, result_name_x, result_name_y, result_name_z); - - VArray<float3> attribute_input = params.get_input_attribute<float3>( - "Vector", component, result_domain, {0, 0, 0}); - VArray_Span<float3> input_span{attribute_input}; - - OutputAttribute_Typed<float> attribute_result_x = - component.attribute_try_get_for_output_only<float>(result_name_x, result_domain); - OutputAttribute_Typed<float> attribute_result_y = - component.attribute_try_get_for_output_only<float>(result_name_y, result_domain); - OutputAttribute_Typed<float> attribute_result_z = - component.attribute_try_get_for_output_only<float>(result_name_z, result_domain); - - /* Only extract the components for the outputs with a given attribute. */ - if (attribute_result_x) { - extract_input(0, input_span, attribute_result_x.as_span()); - attribute_result_x.save(); - } - if (attribute_result_y) { - extract_input(1, input_span, attribute_result_y.as_span()); - attribute_result_y.save(); - } - if (attribute_result_z) { - extract_input(2, input_span, attribute_result_z.as_span()); - attribute_result_z.save(); - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - separate_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_separate_xyz_cc - -void register_node_type_geo_attribute_separate_xyz() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_separate_xyz_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, - "Attribute Separate XYZ", - NODE_CLASS_ATTRIBUTE); - ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage( - &ntype, "NodeAttributeSeparateXYZ", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc deleted file mode 100644 index 802feb88ce2..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc +++ /dev/null @@ -1,515 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_kdopbvh.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_pointcloud_types.h" - -#include "BKE_bvhutils.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_sample.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_transfer_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Geometry>(N_("Source Geometry")); - b.add_input<decl::String>(N_("Source")); - b.add_input<decl::String>(N_("Destination")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE); - uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryAttributeTransfer *data = MEM_cnew<NodeGeometryAttributeTransfer>(__func__); - data->domain = ATTR_DOMAIN_AUTO; - node->storage = data; -} - -static void get_result_domain_and_data_type(const GeometrySet &src_geometry, - const GeometryComponent &dst_component, - const StringRef attribute_name, - CustomDataType *r_data_type, - AttributeDomain *r_domain) -{ - Vector<CustomDataType> data_types; - Vector<AttributeDomain> domains; - - const PointCloudComponent *pointcloud_component = - src_geometry.get_component_for_read<PointCloudComponent>(); - if (pointcloud_component != nullptr) { - std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data( - attribute_name); - if (meta_data.has_value()) { - data_types.append(meta_data->data_type); - domains.append(meta_data->domain); - } - } - - const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>(); - if (mesh_component != nullptr) { - std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data( - attribute_name); - if (meta_data.has_value()) { - data_types.append(meta_data->data_type); - domains.append(meta_data->domain); - } - } - - *r_data_type = bke::attribute_data_type_highest_complexity(data_types); - - if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) { - *r_domain = ATTR_DOMAIN_POINT; - } - else { - *r_domain = bke::attribute_domain_highest_priority(domains); - } -} - -static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, - const VArray<float3> &positions, - const MutableSpan<int> r_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty()); - BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty()); - BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty()); - - for (const int i : positions.index_range()) { - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - const float3 position = positions[i]; - BLI_bvhtree_find_nearest( - tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); - if (!r_indices.is_empty()) { - r_indices[i] = nearest.index; - } - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = nearest.dist_sq; - } - if (!r_positions.is_empty()) { - r_positions[i] = nearest.co; - } - } -} - -static void get_closest_pointcloud_points(const PointCloud &pointcloud, - const VArray<float3> &positions, - const MutableSpan<int> r_indices, - const MutableSpan<float> r_distances_sq) -{ - BLI_assert(positions.size() == r_indices.size()); - BLI_assert(pointcloud.totpoint > 0); - - BVHTreeFromPointCloud tree_data; - BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); - - for (const int i : positions.index_range()) { - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - const float3 position = positions[i]; - BLI_bvhtree_find_nearest( - tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); - r_indices[i] = nearest.index; - r_distances_sq[i] = nearest.dist_sq; - } - - free_bvhtree_from_pointcloud(&tree_data); -} - -static void get_closest_mesh_points(const Mesh &mesh, - const VArray<float3> &positions, - const MutableSpan<int> r_point_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totvert > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); - get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_edges(const Mesh &mesh, - const VArray<float3> &positions, - const MutableSpan<int> r_edge_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totedge > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); - get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_looptris(const Mesh &mesh, - const VArray<float3> &positions, - const MutableSpan<int> r_looptri_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totpoly > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); - get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_polygons(const Mesh &mesh, - const VArray<float3> &positions, - const MutableSpan<int> r_poly_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totpoly > 0); - - Array<int> looptri_indices(positions.size()); - get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions); - - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : positions.index_range()) { - const MLoopTri &looptri = looptris[looptri_indices[i]]; - r_poly_indices[i] = looptri.poly; - } -} - -/* The closest corner is defined to be the closest corner on the closest face. */ -static void get_closest_mesh_corners(const Mesh &mesh, - const VArray<float3> &positions, - const MutableSpan<int> r_corner_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totloop > 0); - Array<int> poly_indices(positions.size()); - get_closest_mesh_polygons(mesh, positions, poly_indices, {}, {}); - - for (const int i : positions.index_range()) { - const float3 position = positions[i]; - const int poly_index = poly_indices[i]; - const MPoly &poly = mesh.mpoly[poly_index]; - - /* Find the closest vertex in the polygon. */ - float min_distance_sq = FLT_MAX; - const MVert *closest_mvert; - int closest_loop_index = 0; - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const MLoop &loop = mesh.mloop[loop_index]; - const int vertex_index = loop.v; - const MVert &mvert = mesh.mvert[vertex_index]; - const float distance_sq = math::distance_squared(position, float3(mvert.co)); - if (distance_sq < min_distance_sq) { - min_distance_sq = distance_sq; - closest_loop_index = loop_index; - closest_mvert = &mvert; - } - } - if (!r_corner_indices.is_empty()) { - r_corner_indices[i] = closest_loop_index; - } - if (!r_positions.is_empty()) { - r_positions[i] = closest_mvert->co; - } - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = min_distance_sq; - } - } -} - -static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry, - GeometryComponent &dst_component, - const VArray<float3> &dst_positions, - const AttributeDomain dst_domain, - const CustomDataType data_type, - const StringRef src_name, - const StringRef dst_name) -{ - const int tot_samples = dst_positions.size(); - const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>(); - if (component == nullptr) { - return; - } - const Mesh *mesh = component->get_for_read(); - if (mesh == nullptr) { - return; - } - if (mesh->totpoly == 0) { - return; - } - - ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - dst_name, dst_domain, data_type); - if (!src_attribute || !dst_attribute) { - return; - } - - /* Find closest points on the mesh surface. */ - Array<int> looptri_indices(tot_samples); - Array<float3> positions(tot_samples); - get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); - - bke::mesh_surface_sample::MeshAttributeInterpolator interp( - mesh, IndexMask(tot_samples), positions, looptri_indices); - interp.sample_attribute( - src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED); - - dst_attribute.save(); -} - -static void transfer_attribute_nearest(const GeometrySet &src_geometry, - GeometryComponent &dst_component, - const VArray<float3> &dst_positions, - const AttributeDomain dst_domain, - const CustomDataType data_type, - const StringRef src_name, - const StringRef dst_name) -{ - const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - - /* Get pointcloud data from geometry. */ - const PointCloudComponent *pointcloud_component = - src_geometry.get_component_for_read<PointCloudComponent>(); - const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() : - nullptr; - - /* Get mesh data from geometry. */ - const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>(); - const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr; - - const int tot_samples = dst_positions.size(); - - Array<int> pointcloud_indices; - Array<float> pointcloud_distances_sq; - bool use_pointcloud = false; - - /* Depending on where what domain the source attribute lives, these indices are either vertex, - * corner, edge or polygon indices. */ - Array<int> mesh_indices; - Array<float> mesh_distances_sq; - bool use_mesh = false; - - /* If there is a pointcloud, find the closest points. */ - if (pointcloud != nullptr && pointcloud->totpoint > 0) { - if (pointcloud_component->attribute_exists(src_name)) { - use_pointcloud = true; - pointcloud_indices.reinitialize(tot_samples); - pointcloud_distances_sq.reinitialize(tot_samples); - get_closest_pointcloud_points( - *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq); - } - } - - /* If there is a mesh, find the closest mesh elements. */ - if (mesh != nullptr) { - ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name); - if (src_attribute) { - switch (src_attribute.domain) { - case ATTR_DOMAIN_POINT: { - if (mesh->totvert > 0) { - use_mesh = true; - mesh_indices.reinitialize(tot_samples); - mesh_distances_sq.reinitialize(tot_samples); - get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); - } - break; - } - case ATTR_DOMAIN_EDGE: { - if (mesh->totedge > 0) { - use_mesh = true; - mesh_indices.reinitialize(tot_samples); - mesh_distances_sq.reinitialize(tot_samples); - get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); - } - break; - } - case ATTR_DOMAIN_FACE: { - if (mesh->totpoly > 0) { - use_mesh = true; - mesh_indices.reinitialize(tot_samples); - mesh_distances_sq.reinitialize(tot_samples); - get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); - } - break; - } - case ATTR_DOMAIN_CORNER: { - if (mesh->totloop > 0) { - use_mesh = true; - mesh_indices.reinitialize(tot_samples); - mesh_distances_sq.reinitialize(tot_samples); - get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); - } - break; - } - default: { - break; - } - } - } - } - - if (!use_pointcloud && !use_mesh) { - return; - } - - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - dst_name, dst_domain, data_type); - if (!dst_attribute) { - return; - } - - /* Create a buffer for intermediate values. */ - BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - - if (use_mesh && use_pointcloud) { - /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or - * a mesh element is closer to every point. */ - ReadAttributeLookup pointcloud_src_attribute = - pointcloud_component->attribute_try_get_for_read(src_name, data_type); - ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name, - data_type); - for (const int i : IndexRange(tot_samples)) { - if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) { - /* Point cloud point is closer. */ - const int index = pointcloud_indices[i]; - pointcloud_src_attribute.varray.get(index, buffer); - dst_attribute->set_by_relocate(i, buffer); - } - else { - /* Mesh element is closer. */ - const int index = mesh_indices[i]; - mesh_src_attribute.varray.get(index, buffer); - dst_attribute->set_by_relocate(i, buffer); - } - } - } - else if (use_pointcloud) { - /* The source geometry only has a pointcloud. */ - ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read( - src_name, data_type); - for (const int i : IndexRange(tot_samples)) { - const int index = pointcloud_indices[i]; - src_attribute.varray.get(index, buffer); - dst_attribute->set_by_relocate(i, buffer); - } - } - else if (use_mesh) { - /* The source geometry only has a mesh. */ - ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name, - data_type); - for (const int i : IndexRange(tot_samples)) { - const int index = mesh_indices[i]; - src_attribute.varray.get(index, buffer); - dst_attribute->set_by_relocate(i, buffer); - } - } - - dst_attribute.save(); -} - -static void transfer_attribute(const GeoNodeExecParams ¶ms, - const GeometrySet &src_geometry, - GeometryComponent &dst_component, - const StringRef src_name, - const StringRef dst_name) -{ - const NodeGeometryAttributeTransfer &storage = - *(const NodeGeometryAttributeTransfer *)params.node().storage; - const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode) - storage.mapping; - const AttributeDomain input_domain = (AttributeDomain)storage.domain; - - CustomDataType data_type; - AttributeDomain auto_domain; - get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain); - const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain : - input_domain; - - VArray<float3> dst_positions = dst_component.attribute_get_for_read<float3>( - "position", dst_domain, {0, 0, 0}); - - switch (mapping) { - case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { - transfer_attribute_nearest_face_interpolated( - src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); - break; - } - case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST: { - transfer_attribute_nearest( - src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); - break; - } - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry"); - GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry"); - const std::string src_attribute_name = params.extract_input<std::string>("Source"); - const std::string dst_attribute_name = params.extract_input<std::string>("Destination"); - - if (src_attribute_name.empty() || dst_attribute_name.empty()) { - params.set_default_remaining_outputs(); - return; - } - - dst_geometry_set = geometry::realize_instances_legacy(dst_geometry_set); - src_geometry_set = geometry::realize_instances_legacy(src_geometry_set); - - if (dst_geometry_set.has<MeshComponent>()) { - transfer_attribute(params, - src_geometry_set, - dst_geometry_set.get_component_for_write<MeshComponent>(), - src_attribute_name, - dst_attribute_name); - } - if (dst_geometry_set.has<PointCloudComponent>()) { - transfer_attribute(params, - src_geometry_set, - dst_geometry_set.get_component_for_write<PointCloudComponent>(), - src_attribute_name, - dst_attribute_name); - } - - params.set_output("Geometry", dst_geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_transfer_cc - -void register_node_type_geo_legacy_attribute_transfer() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_transfer_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_storage(&ntype, - "NodeGeometryAttributeTransfer", - node_free_standard_storage, - node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc deleted file mode 100644 index 83ece031dd2..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc +++ /dev/null @@ -1,559 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_math_base_safe.h" -#include "BLI_task.hh" - -#include "RNA_enum_types.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "NOD_math_functions.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_attribute_vector_math_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("A")); - b.add_input<decl::Vector>(N_("A"), "A_001"); - b.add_input<decl::String>(N_("B")); - b.add_input<decl::Vector>(N_("B"), "B_001"); - b.add_input<decl::Float>(N_("B"), "B_002"); - b.add_input<decl::String>(N_("C")); - b.add_input<decl::Vector>(N_("C"), "C_001"); - b.add_input<decl::Float>(N_("C"), "C_002"); - b.add_input<decl::String>(N_("Result")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static bool operation_use_input_b(const NodeVectorMathOperation operation) -{ - return !ELEM(operation, - NODE_VECTOR_MATH_NORMALIZE, - NODE_VECTOR_MATH_FLOOR, - NODE_VECTOR_MATH_CEIL, - NODE_VECTOR_MATH_FRACTION, - NODE_VECTOR_MATH_ABSOLUTE, - NODE_VECTOR_MATH_SINE, - NODE_VECTOR_MATH_COSINE, - NODE_VECTOR_MATH_TANGENT, - NODE_VECTOR_MATH_LENGTH); -} - -static bool operation_use_input_c(const NodeVectorMathOperation operation) -{ - return ELEM(operation, - NODE_VECTOR_MATH_WRAP, - NODE_VECTOR_MATH_REFRACT, - NODE_VECTOR_MATH_FACEFORWARD, - NODE_VECTOR_MATH_MULTIPLY_ADD); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = (bNode *)ptr->data; - const NodeAttributeVectorMath &node_storage = *(NodeAttributeVectorMath *)node->storage; - const NodeVectorMathOperation operation = (const NodeVectorMathOperation)node_storage.operation; - - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); - - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "input_type_a", 0, IFACE_("A"), ICON_NONE); - if (operation_use_input_b(operation)) { - uiItemR(layout, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE); - } - if (operation_use_input_c(operation)) { - uiItemR(layout, ptr, "input_type_c", 0, IFACE_("C"), ICON_NONE); - } -} - -static CustomDataType operation_get_read_type_b(const NodeVectorMathOperation operation) -{ - if (operation == NODE_VECTOR_MATH_SCALE) { - return CD_PROP_FLOAT; - } - return CD_PROP_FLOAT3; -} - -static CustomDataType operation_get_read_type_c(const NodeVectorMathOperation operation) -{ - if (operation == NODE_VECTOR_MATH_REFRACT) { - return CD_PROP_FLOAT; - } - return CD_PROP_FLOAT3; -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeAttributeVectorMath *data = MEM_cnew<NodeAttributeVectorMath>(__func__); - - data->operation = NODE_VECTOR_MATH_ADD; - data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - node->storage = data; -} - -static CustomDataType operation_get_result_type(const NodeVectorMathOperation operation) -{ - switch (operation) { - case NODE_VECTOR_MATH_ADD: - case NODE_VECTOR_MATH_SUBTRACT: - case NODE_VECTOR_MATH_MULTIPLY: - case NODE_VECTOR_MATH_DIVIDE: - case NODE_VECTOR_MATH_CROSS_PRODUCT: - case NODE_VECTOR_MATH_PROJECT: - case NODE_VECTOR_MATH_REFLECT: - case NODE_VECTOR_MATH_SCALE: - case NODE_VECTOR_MATH_NORMALIZE: - case NODE_VECTOR_MATH_SNAP: - case NODE_VECTOR_MATH_FLOOR: - case NODE_VECTOR_MATH_CEIL: - case NODE_VECTOR_MATH_MODULO: - case NODE_VECTOR_MATH_FRACTION: - case NODE_VECTOR_MATH_ABSOLUTE: - case NODE_VECTOR_MATH_MINIMUM: - case NODE_VECTOR_MATH_MAXIMUM: - case NODE_VECTOR_MATH_WRAP: - case NODE_VECTOR_MATH_SINE: - case NODE_VECTOR_MATH_COSINE: - case NODE_VECTOR_MATH_TANGENT: - case NODE_VECTOR_MATH_REFRACT: - case NODE_VECTOR_MATH_FACEFORWARD: - case NODE_VECTOR_MATH_MULTIPLY_ADD: - return CD_PROP_FLOAT3; - case NODE_VECTOR_MATH_DOT_PRODUCT: - case NODE_VECTOR_MATH_DISTANCE: - case NODE_VECTOR_MATH_LENGTH: - return CD_PROP_FLOAT; - } - - BLI_assert(false); - return CD_PROP_FLOAT3; -} - -static void geo_node_vector_math_label(const bNodeTree *UNUSED(ntree), - const bNode *node, - char *label, - int maxlen) -{ - NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; - const char *name; - bool enum_label = RNA_enum_name(rna_enum_node_vec_math_items, node_storage.operation, &name); - if (!enum_label) { - name = "Unknown"; - } - BLI_snprintf(label, maxlen, IFACE_("Vector %s"), IFACE_(name)); -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage; - const NodeVectorMathOperation operation = (const NodeVectorMathOperation)node_storage->operation; - - update_attribute_input_socket_availabilities( - *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "B", - (GeometryNodeAttributeInputMode)node_storage->input_type_b, - operation_use_input_b(operation)); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "C", - (GeometryNodeAttributeInputMode)node_storage->input_type_c, - operation_use_input_c(operation)); -} - -static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a, - const VArray<float3> &input_b, - const VMutableArray<float3> &result, - const NodeVectorMathOperation operation) -{ - const int size = input_a.size(); - - VArray_Span<float3> span_a{input_a}; - VArray_Span<float3> span_b{input_b}; - VMutableArray_Span<float3> span_result{result, false}; - - bool success = try_dispatch_float_math_fl3_fl3_to_fl3( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float3 out = math_function(a, b); - span_result[i] = out; - } - }); - }); - - span_result.save(); - - /* The operation is not supported by this node currently. */ - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a, - const VArray<float3> &input_b, - const VArray<float3> &input_c, - const VMutableArray<float3> &result, - const NodeVectorMathOperation operation) -{ - const int size = input_a.size(); - - VArray_Span<float3> span_a{input_a}; - VArray_Span<float3> span_b{input_b}; - VArray_Span<float3> span_c{input_c}; - VMutableArray_Span<float3> span_result{result}; - - bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float3 c = span_c[i]; - const float3 out = math_function(a, b, c); - span_result[i] = out; - } - }); - }); - - span_result.save(); - - /* The operation is not supported by this node currently. */ - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a, - const VArray<float3> &input_b, - const VArray<float> &input_c, - const VMutableArray<float3> &result, - const NodeVectorMathOperation operation) -{ - const int size = input_a.size(); - - VArray_Span<float3> span_a{input_a}; - VArray_Span<float3> span_b{input_b}; - VArray_Span<float> span_c{input_c}; - VMutableArray_Span<float3> span_result{result, false}; - - bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float c = span_c[i]; - const float3 out = math_function(a, b, c); - span_result[i] = out; - } - }); - }); - - span_result.save(); - - /* The operation is not supported by this node currently. */ - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a, - const VArray<float3> &input_b, - const VMutableArray<float> &result, - const NodeVectorMathOperation operation) -{ - const int size = input_a.size(); - - VArray_Span<float3> span_a{input_a}; - VArray_Span<float3> span_b{input_b}; - VMutableArray_Span<float> span_result{result, false}; - - bool success = try_dispatch_float_math_fl3_fl3_to_fl( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float out = math_function(a, b); - span_result[i] = out; - } - }); - }); - - span_result.save(); - - /* The operation is not supported by this node currently. */ - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a, - const VArray<float> &input_b, - const VMutableArray<float3> &result, - const NodeVectorMathOperation operation) -{ - const int size = input_a.size(); - - VArray_Span<float3> span_a{input_a}; - VArray_Span<float> span_b{input_b}; - VMutableArray_Span<float3> span_result{result, false}; - - bool success = try_dispatch_float_math_fl3_fl_to_fl3( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float3 a = span_a[i]; - const float b = span_b[i]; - const float3 out = math_function(a, b); - span_result[i] = out; - } - }); - }); - - span_result.save(); - - /* The operation is not supported by this node currently. */ - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a, - const VMutableArray<float3> &result, - const NodeVectorMathOperation operation) -{ - const int size = input_a.size(); - - VArray_Span<float3> span_a{input_a}; - VMutableArray_Span<float3> span_result{result, false}; - - bool success = try_dispatch_float_math_fl3_to_fl3( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float3 in = span_a[i]; - const float3 out = math_function(in); - span_result[i] = out; - } - }); - }); - - span_result.save(); - - /* The operation is not supported by this node currently. */ - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a, - const VMutableArray<float> &result, - const NodeVectorMathOperation operation) -{ - const int size = input_a.size(); - - VArray_Span<float3> span_a{input_a}; - VMutableArray_Span<float> span_result{result, false}; - - bool success = try_dispatch_float_math_fl3_to_fl( - operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) { - for (const int i : range) { - const float3 in = span_a[i]; - const float out = math_function(in); - span_result[i] = out; - } - }); - }); - - span_result.save(); - - /* The operation is not supported by this node currently. */ - BLI_assert(success); - UNUSED_VARS_NDEBUG(success); -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - const NodeVectorMathOperation operation, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the highest priority domain from existing input attributes, or the default. */ - const AttributeDomain default_domain = ATTR_DOMAIN_POINT; - if (operation_use_input_b(operation)) { - if (operation_use_input_c(operation)) { - return params.get_highest_priority_input_domain({"A", "B", "C"}, component, default_domain); - } - return params.get_highest_priority_input_domain({"A", "B"}, component, default_domain); - } - return params.get_highest_priority_input_domain({"A"}, component, default_domain); -} - -static void attribute_vector_math_calc(GeometryComponent &component, - const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage; - const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation; - const std::string result_name = params.get_input<std::string>("Result"); - - /* The number and type of the input attribute depend on the operation. */ - const CustomDataType read_type_a = CD_PROP_FLOAT3; - const bool use_input_b = operation_use_input_b(operation); - const CustomDataType read_type_b = operation_get_read_type_b(operation); - const bool use_input_c = operation_use_input_c(operation); - const CustomDataType read_type_c = operation_get_read_type_c(operation); - - /* The result domain is always point for now. */ - const CustomDataType result_type = operation_get_result_type(operation); - const AttributeDomain result_domain = get_result_domain( - component, params, operation, result_name); - - GVArray attribute_a = params.get_input_attribute( - "A", component, result_domain, read_type_a, nullptr); - if (!attribute_a) { - return; - } - GVArray attribute_b; - GVArray attribute_c; - if (use_input_b) { - attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr); - if (!attribute_b) { - return; - } - } - if (use_input_c) { - attribute_c = params.get_input_attribute("C", component, result_domain, read_type_c, nullptr); - if (!attribute_c) { - return; - } - } - - /* Get result attribute first, in case it has to overwrite one of the existing attributes. */ - OutputAttribute attribute_result = component.attribute_try_get_for_output_only( - result_name, result_domain, result_type); - if (!attribute_result) { - return; - } - - switch (operation) { - case NODE_VECTOR_MATH_ADD: - case NODE_VECTOR_MATH_SUBTRACT: - case NODE_VECTOR_MATH_MULTIPLY: - case NODE_VECTOR_MATH_DIVIDE: - case NODE_VECTOR_MATH_CROSS_PRODUCT: - case NODE_VECTOR_MATH_PROJECT: - case NODE_VECTOR_MATH_REFLECT: - case NODE_VECTOR_MATH_SNAP: - case NODE_VECTOR_MATH_MODULO: - case NODE_VECTOR_MATH_MINIMUM: - case NODE_VECTOR_MATH_MAXIMUM: - do_math_operation_fl3_fl3_to_fl3(attribute_a.typed<float3>(), - attribute_b.typed<float3>(), - attribute_result.varray().typed<float3>(), - operation); - break; - case NODE_VECTOR_MATH_DOT_PRODUCT: - case NODE_VECTOR_MATH_DISTANCE: - do_math_operation_fl3_fl3_to_fl(attribute_a.typed<float3>(), - attribute_b.typed<float3>(), - attribute_result.varray().typed<float>(), - operation); - break; - case NODE_VECTOR_MATH_LENGTH: - do_math_operation_fl3_to_fl( - attribute_a.typed<float3>(), attribute_result.varray().typed<float>(), operation); - break; - case NODE_VECTOR_MATH_SCALE: - do_math_operation_fl3_fl_to_fl3(attribute_a.typed<float3>(), - attribute_b.typed<float>(), - attribute_result.varray().typed<float3>(), - operation); - break; - case NODE_VECTOR_MATH_NORMALIZE: - case NODE_VECTOR_MATH_FLOOR: - case NODE_VECTOR_MATH_CEIL: - case NODE_VECTOR_MATH_FRACTION: - case NODE_VECTOR_MATH_ABSOLUTE: - case NODE_VECTOR_MATH_SINE: - case NODE_VECTOR_MATH_COSINE: - case NODE_VECTOR_MATH_TANGENT: - do_math_operation_fl3_to_fl3( - attribute_a.typed<float3>(), attribute_result.varray().typed<float3>(), operation); - break; - case NODE_VECTOR_MATH_WRAP: - case NODE_VECTOR_MATH_FACEFORWARD: - case NODE_VECTOR_MATH_MULTIPLY_ADD: - do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a.typed<float3>(), - attribute_b.typed<float3>(), - attribute_c.typed<float3>(), - attribute_result.varray().typed<float3>(), - operation); - break; - case NODE_VECTOR_MATH_REFRACT: - do_math_operation_fl3_fl3_fl_to_fl3(attribute_a.typed<float3>(), - attribute_b.typed<float3>(), - attribute_c.typed<float>(), - attribute_result.varray().typed<float3>(), - operation); - break; - } - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), - params); - } - if (geometry_set.has<CurveComponent>()) { - attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_vector_math_cc - -void register_node_type_geo_attribute_vector_math() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_vector_math_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, - "Attribute Vector Math", - NODE_CLASS_ATTRIBUTE); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - ntype.labelfunc = file_ns::geo_node_vector_math_label; - node_type_update(&ntype, file_ns::node_update); - node_type_init(&ntype, file_ns::node_init); - node_type_storage( - &ntype, "NodeAttributeVectorMath", node_free_standard_storage, node_copy_standard_storage); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc deleted file mode 100644 index ccf1bdb0a19..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc +++ /dev/null @@ -1,335 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "node_geometry_util.hh" - -#include "BLI_task.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -namespace blender::nodes::node_geo_legacy_attribute_vector_rotate_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Vector")); - b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(0.0f).max(1.0f).hide_value(); - b.add_input<decl::String>(N_("Center")); - b.add_input<decl::Vector>(N_("Center"), "Center_001").subtype(PROP_XYZ); - b.add_input<decl::String>(N_("Axis")); - b.add_input<decl::Vector>(N_("Axis"), "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); - b.add_input<decl::String>(N_("Angle")); - b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); - b.add_input<decl::String>(N_("Rotation")); - b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); - b.add_input<decl::Bool>(N_("Invert")); - b.add_input<decl::String>(N_("Result")); - - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = (bNode *)ptr->data; - const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage; - const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode) - node_storage.mode; - - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiLayout *column = uiLayoutColumn(layout, false); - - uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE); - - uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE); - uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE); - if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) { - uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE); - } - if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { - uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE); - } - if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { - uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE); - } -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage; - const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode) - node_storage->mode; - - update_attribute_input_socket_availabilities( - *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); - update_attribute_input_socket_availabilities( - *ntree, *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Axis", - (GeometryNodeAttributeInputMode)node_storage->input_type_axis, - (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS)); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Angle", - (GeometryNodeAttributeInputMode)node_storage->input_type_angle, - (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Rotation", - (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, - (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); -} - -static float3 vector_rotate_around_axis(const float3 vector, - const float3 center, - const float3 axis, - const float angle) -{ - float3 result = vector - center; - float mat[3][3]; - axis_angle_to_mat3(mat, axis, angle); - mul_m3_v3(mat, result); - return result + center; -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeVectorRotate *node_storage = MEM_cnew<NodeAttributeVectorRotate>(__func__); - - node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS; - node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - - node->storage = node_storage; -} - -static float3 vector_rotate_euler(const float3 vector, - const float3 center, - const float3 rotation, - const bool invert) -{ - float mat[3][3]; - float3 result = vector - center; - eul_to_mat3(mat, rotation); - if (invert) { - invert_m3(mat); - } - mul_m3_v3(mat, result); - return result + center; -} - -static void do_vector_rotate_around_axis(const VArray<float3> &vector, - const VArray<float3> ¢er, - const VArray<float3> &axis, - const VArray<float> &angle, - MutableSpan<float3> results, - const bool invert) -{ - VArray_Span<float3> span_vector{vector}; - VArray_Span<float3> span_center{center}; - VArray_Span<float3> span_axis{axis}; - VArray_Span<float> span_angle{angle}; - - threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { - for (const int i : range) { - float angle = (invert) ? -span_angle[i] : span_angle[i]; - results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle); - } - }); -} - -static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector, - const VArray<float3> ¢er, - const float3 axis, - const VArray<float> &angle, - MutableSpan<float3> results, - const bool invert) -{ - VArray_Span<float3> span_vector{vector}; - VArray_Span<float3> span_center{center}; - VArray_Span<float> span_angle{angle}; - - threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { - for (const int i : range) { - float angle = (invert) ? -span_angle[i] : span_angle[i]; - results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle); - } - }); -} - -static void do_vector_rotate_euler(const VArray<float3> &vector, - const VArray<float3> ¢er, - const VArray<float3> &rotation, - MutableSpan<float3> results, - const bool invert) -{ - VArray_Span<float3> span_vector{vector}; - VArray_Span<float3> span_center{center}; - VArray_Span<float3> span_rotation{rotation}; - - threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { - for (const int i : range) { - results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert); - } - }); -} - -static AttributeDomain get_result_domain(const GeometryComponent &component, - const GeoNodeExecParams ¶ms, - StringRef result_name) -{ - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name); - if (meta_data) { - return meta_data->domain; - } - - /* Otherwise use the highest priority domain from existing input attributes, or the default. */ - const AttributeDomain default_domain = ATTR_DOMAIN_POINT; - return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain); -} - -static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) -{ - const bNode &node = params.node(); - const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage; - const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode) - node_storage->mode; - const std::string result_name = params.get_input<std::string>("Result"); - const AttributeDomain result_domain = get_result_domain(component, params, result_name); - const bool invert = params.get_input<bool>("Invert"); - - GVArray attribute_vector = params.get_input_attribute( - "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr); - if (!attribute_vector) { - return; - } - GVArray attribute_center = params.get_input_attribute( - "Center", component, result_domain, CD_PROP_FLOAT3, nullptr); - if (!attribute_center) { - return; - } - - OutputAttribute attribute_result = component.attribute_try_get_for_output_only( - result_name, result_domain, CD_PROP_FLOAT3); - if (!attribute_result) { - return; - } - - if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { - GVArray attribute_rotation = params.get_input_attribute( - "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr); - if (!attribute_rotation) { - return; - } - do_vector_rotate_euler(attribute_vector.typed<float3>(), - attribute_center.typed<float3>(), - attribute_rotation.typed<float3>(), - attribute_result.as_span<float3>(), - invert); - attribute_result.save(); - return; - } - - GVArray attribute_angle = params.get_input_attribute( - "Angle", component, result_domain, CD_PROP_FLOAT, nullptr); - if (!attribute_angle) { - return; - } - - switch (mode) { - case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: { - GVArray attribute_axis = params.get_input_attribute( - "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr); - if (!attribute_axis) { - return; - } - do_vector_rotate_around_axis(attribute_vector.typed<float3>(), - attribute_center.typed<float3>(), - attribute_axis.typed<float3>(), - attribute_angle.typed<float>(), - attribute_result.as_span<float3>(), - invert); - } break; - case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X: - do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(), - attribute_center.typed<float3>(), - float3(1.0f, 0.0f, 0.0f), - attribute_angle.typed<float>(), - attribute_result.as_span<float3>(), - invert); - break; - case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y: - do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(), - attribute_center.typed<float3>(), - float3(0.0f, 1.0f, 0.0f), - attribute_angle.typed<float>(), - attribute_result.as_span<float3>(), - invert); - - break; - case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z: - do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(), - attribute_center.typed<float3>(), - float3(0.0f, 0.0f, 1.0f), - attribute_angle.typed<float>(), - attribute_result.as_span<float3>(), - invert); - - break; - case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: - /* Euler is handled before other modes to avoid processing the unavailable angle socket. */ - BLI_assert_unreachable(); - break; - } - attribute_result.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); - } - if (geometry_set.has<PointCloudComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); - } - if (geometry_set.has<CurveComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_attribute_vector_rotate_cc - -void register_node_type_geo_attribute_vector_rotate() -{ - namespace file_ns = blender::nodes::node_geo_legacy_attribute_vector_rotate_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, - "Attribute Vector Rotate", - NODE_CLASS_ATTRIBUTE); - node_type_update(&ntype, file_ns::node_update); - node_type_init(&ntype, file_ns::node_init); - node_type_size(&ntype, 165, 100, 600); - node_type_storage( - &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - ntype.declare = file_ns::node_declare; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc deleted file mode 100644 index 0980c2d6e72..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc +++ /dev/null @@ -1,207 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" -#include "BLI_timeit.hh" - -#include "BKE_pointcloud.h" -#include "BKE_spline.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_curve_endpoints_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_output<decl::Geometry>(N_("Start Points")); - b.add_output<decl::Geometry>(N_("End Points")); -} - -/** - * Evaluate splines in parallel to speed up the rest of the node's execution. - */ -static void evaluate_splines(Span<SplinePtr> splines) -{ - threading::parallel_for_each(splines, [](const SplinePtr &spline) { - /* These functions fill the corresponding caches on each spline. */ - spline->evaluated_positions(); - spline->evaluated_tangents(); - spline->evaluated_normals(); - spline->evaluated_lengths(); - }); -} - -/** - * \note Use attributes from the curve component rather than the attribute data directly on the - * attribute storage to allow reading the virtual spline attributes like "cyclic" and "resolution". - */ -static void copy_spline_domain_attributes(const CurveComponent &curve_component, - Span<int> offsets, - PointCloudComponent &points) -{ - curve_component.attribute_foreach( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (meta_data.domain != ATTR_DOMAIN_CURVE) { - return true; - } - GVArray spline_attribute = curve_component.attribute_get_for_read( - attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); - - OutputAttribute result_attribute = points.attribute_try_get_for_output_only( - attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); - GMutableSpan result = result_attribute.as_span(); - - /* Only copy the attributes of splines in the offsets. */ - for (const int i : offsets.index_range()) { - spline_attribute.get(offsets[i], result[i]); - } - - result_attribute.save(); - return true; - }); -} - -/** - * Get the offsets for the splines whose endpoints we want to output. - * Filter those which are cyclic, or that evaluate to empty. - * Could be easily adapted to include a selection argument to support attribute selection. - */ -static blender::Vector<int> get_endpoint_spline_offsets(Span<SplinePtr> splines) -{ - blender::Vector<int> spline_offsets; - spline_offsets.reserve(splines.size()); - - for (const int i : splines.index_range()) { - if (!(splines[i]->is_cyclic() || splines[i]->evaluated_points_size() == 0)) { - spline_offsets.append(i); - } - } - - return spline_offsets; -} - -/** - * Copy the endpoint attributes from the correct positions at the splines at the offsets to - * the start and end attributes. - */ -static void copy_endpoint_attributes(Span<SplinePtr> splines, - Span<int> offsets, - CurveToPointsResults &start_data, - CurveToPointsResults &end_data) -{ - threading::parallel_for(offsets.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[offsets[i]]; - - /* Copy the start and end point data over. */ - start_data.positions[i] = spline.evaluated_positions().first(); - start_data.tangents[i] = spline.evaluated_tangents().first(); - start_data.normals[i] = spline.evaluated_normals().first(); - start_data.radii[i] = spline.radii().first(); - start_data.tilts[i] = spline.tilts().first(); - - end_data.positions[i] = spline.evaluated_positions().last(); - end_data.tangents[i] = spline.evaluated_tangents().last(); - end_data.normals[i] = spline.evaluated_normals().last(); - end_data.radii[i] = spline.radii().last(); - end_data.tilts[i] = spline.tilts().last(); - - /* Copy the point attribute data over. */ - for (const auto item : start_data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - GMutableSpan point_span = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline_span.type().copy_assign(spline_span[0], point_span[i]); - } - - for (const auto item : end_data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - GMutableSpan point_span = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline_span.type().copy_assign(spline_span[spline.size() - 1], point_span[i]); - } - } - }); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (!geometry_set.has_curves()) { - params.set_default_remaining_outputs(); - return; - } - - const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); - const Span<SplinePtr> splines = curve->splines(); - curve->assert_valid_point_attributes(); - - evaluate_splines(splines); - - const Vector<int> offsets = get_endpoint_spline_offsets(splines); - const int total_size = offsets.size(); - - if (total_size == 0) { - params.set_default_remaining_outputs(); - return; - } - - GeometrySet start_result = GeometrySet::create_with_pointcloud( - BKE_pointcloud_new_nomain(total_size)); - GeometrySet end_result = GeometrySet::create_with_pointcloud( - BKE_pointcloud_new_nomain(total_size)); - PointCloudComponent &start_point_component = - start_result.get_component_for_write<PointCloudComponent>(); - PointCloudComponent &end_point_component = - end_result.get_component_for_write<PointCloudComponent>(); - - CurveToPointsResults start_attributes = curve_to_points_create_result_attributes( - start_point_component, *curve); - CurveToPointsResults end_attributes = curve_to_points_create_result_attributes( - end_point_component, *curve); - - copy_endpoint_attributes(splines, offsets.as_span(), start_attributes, end_attributes); - copy_spline_domain_attributes(curve_component, offsets.as_span(), start_point_component); - curve_create_default_rotation_attribute( - start_attributes.tangents, start_attributes.normals, start_attributes.rotations); - curve_create_default_rotation_attribute( - end_attributes.tangents, end_attributes.normals, end_attributes.rotations); - - /* The default radius is way too large for points, divide by 10. */ - for (float &radius : start_attributes.radii) { - radius *= 0.1f; - } - for (float &radius : end_attributes.radii) { - radius *= 0.1f; - } - - params.set_output("Start Points", std::move(start_result)); - params.set_output("End Points", std::move(end_result)); -} - -} // namespace blender::nodes::node_geo_legacy_curve_endpoints_cc - -void register_node_type_geo_legacy_curve_endpoints() -{ - namespace file_ns = blender::nodes::node_geo_legacy_curve_endpoints_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc deleted file mode 100644 index 2c801642bd7..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" - -#include "BKE_spline.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_curve_reverse_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Curve")); - b.add_input<decl::String>(N_("Selection")); - b.add_output<decl::Geometry>(N_("Curve")); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curves()) { - params.set_output("Curve", geometry_set); - return; - } - - /* Retrieve data for write access so we can avoid new allocations for the reversed data. */ - CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); - MutableSpan<SplinePtr> splines = curve->splines(); - - const std::string selection_name = params.extract_input<std::string>("Selection"); - VArray<bool> selection = curve_component.attribute_get_for_read( - selection_name, ATTR_DOMAIN_CURVE, true); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - if (selection[i]) { - splines[i]->reverse(); - } - } - }); - - geometry_set.replace_curves(curve_eval_to_curves(*curve)); - - params.set_output("Curve", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_curve_reverse_cc - -void register_node_type_geo_legacy_curve_reverse() -{ - namespace file_ns = blender::nodes::node_geo_legacy_curve_reverse_cc; - - static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc deleted file mode 100644 index 729ccca5f04..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc +++ /dev/null @@ -1,126 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" - -#include "BKE_spline.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Selection")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); - uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__); - - data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; - data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; - node->storage = data; -} - -static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) -{ - switch (type) { - case GEO_NODE_CURVE_HANDLE_AUTO: - return BEZIER_HANDLE_AUTO; - case GEO_NODE_CURVE_HANDLE_ALIGN: - return BEZIER_HANDLE_ALIGN; - case GEO_NODE_CURVE_HANDLE_FREE: - return BEZIER_HANDLE_FREE; - case GEO_NODE_CURVE_HANDLE_VECTOR: - return BEZIER_HANDLE_VECTOR; - } - BLI_assert_unreachable(); - return BEZIER_HANDLE_AUTO; -} - -static void select_curve_by_handle_type(const CurveEval &curve, - const HandleType type, - const GeometryNodeCurveHandleMode mode, - const MutableSpan<bool> r_selection) -{ - const Array<int> offsets = curve.control_point_offsets(); - Span<SplinePtr> splines = curve.splines(); - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i_spline : range) { - const Spline &spline = *splines[i_spline]; - if (spline.type() == CURVE_TYPE_BEZIER) { - const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline); - Span<int8_t> types_left = bezier_spline.handle_types_left(); - Span<int8_t> types_right = bezier_spline.handle_types_right(); - for (const int i_point : IndexRange(bezier_spline.size())) { - r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && - types_left[i_point] == type) || - (mode & GEO_NODE_CURVE_HANDLE_RIGHT && - types_right[i_point] == type); - } - } - else { - r_selection.slice(offsets[i_spline], offsets[i_spline + 1]).fill(false); - } - } - }); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - const NodeGeometryCurveSelectHandles *storage = - (const NodeGeometryCurveSelectHandles *)params.node().storage; - const HandleType handle_type = handle_type_from_input_type( - (GeometryNodeCurveHandleType)storage->handle_type); - const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; - - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry::realize_instances_legacy(geometry_set); - - CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - if (curve_component.has_curves()) { - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); - const std::string selection_name = params.extract_input<std::string>("Selection"); - OutputAttribute_Typed<bool> selection = - curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT); - if (selection) { - select_curve_by_handle_type(*curve, handle_type, mode, selection.as_span()); - selection.save(); - } - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc - -void register_node_type_geo_legacy_select_by_handle_type() -{ - namespace file_ns = blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - node_type_init(&ntype, file_ns::node_init); - node_type_storage(&ntype, - "NodeGeometryCurveSelectHandles", - node_free_standard_storage, - node_copy_standard_storage); - ntype.draw_buttons = file_ns::node_layout; - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc deleted file mode 100644 index 56e9068882b..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BKE_spline.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_curve_set_handles_cc { - -static void node_decalre(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Curve")); - b.add_input<decl::String>(N_("Selection")); - b.add_output<decl::Geometry>(N_("Curve")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); - uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__); - - data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; - data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; - node->storage = data; -} - -static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) -{ - switch (type) { - case GEO_NODE_CURVE_HANDLE_AUTO: - return BEZIER_HANDLE_AUTO; - case GEO_NODE_CURVE_HANDLE_ALIGN: - return BEZIER_HANDLE_ALIGN; - case GEO_NODE_CURVE_HANDLE_FREE: - return BEZIER_HANDLE_FREE; - case GEO_NODE_CURVE_HANDLE_VECTOR: - return BEZIER_HANDLE_VECTOR; - } - BLI_assert_unreachable(); - return BEZIER_HANDLE_AUTO; -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - const NodeGeometryCurveSetHandles *node_storage = - (NodeGeometryCurveSetHandles *)params.node().storage; - const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)node_storage->handle_type; - const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; - - GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curves()) { - params.set_output("Curve", geometry_set); - return; - } - - /* Retrieve data for write access so we can avoid new allocations for the handles data. */ - CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); - MutableSpan<SplinePtr> splines = curve->splines(); - - const std::string selection_name = params.extract_input<std::string>("Selection"); - VArray<bool> selection = curve_component.attribute_get_for_read( - selection_name, ATTR_DOMAIN_POINT, true); - - const HandleType new_handle_type = handle_type_from_input_type(type); - int point_index = 0; - bool has_bezier_spline = false; - for (SplinePtr &spline : splines) { - if (spline->type() != CURVE_TYPE_BEZIER) { - point_index += spline->positions().size(); - continue; - } - - BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { - /* In this case the automatically calculated handle types need to be "baked", because - * they're possibly changing from a type that is calculated automatically to a type that - * is positioned manually. */ - bezier_spline.ensure_auto_handles(); - } - has_bezier_spline = true; - for (int i_point : IndexRange(bezier_spline.size())) { - if (selection[point_index]) { - if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - bezier_spline.handle_types_left()[i_point] = new_handle_type; - } - if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { - bezier_spline.handle_types_right()[i_point] = new_handle_type; - } - } - point_index++; - } - bezier_spline.mark_cache_invalid(); - } - - geometry_set.replace_curves(curve_eval_to_curves(*curve)); - - if (!has_bezier_spline) { - params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); - } - - params.set_output("Curve", geometry_set); -} -} // namespace blender::nodes::node_geo_legacy_curve_set_handles_cc - -void register_node_type_geo_legacy_curve_set_handles() -{ - namespace file_ns = blender::nodes::node_geo_legacy_curve_set_handles_cc; - - static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_decalre; - ntype.geometry_node_execute = file_ns::node_geo_exec; - node_type_init(&ntype, file_ns::node_init); - node_type_storage(&ntype, - "NodeGeometryCurveSetHandles", - node_free_standard_storage, - node_copy_standard_storage); - ntype.draw_buttons = file_ns::node_layout; - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc deleted file mode 100644 index 002c42c4c82..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc +++ /dev/null @@ -1,294 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BKE_spline.hh" - -#include "BLI_task.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_curve_spline_type_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Curve")); - b.add_input<decl::String>(N_("Selection")); - b.add_output<decl::Geometry>(N_("Curve")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__); - - data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; - node->storage = data; -} - -template<class T> -static void scale_input_assign(const Span<T> input, - const int scale, - const int offset, - const MutableSpan<T> r_output) -{ - for (const int i : IndexRange(r_output.size())) { - r_output[i] = input[i * scale + offset]; - } -} - -template<class T> -static void scale_output_assign(const Span<T> input, - const int scale, - const int offset, - const MutableSpan<T> &r_output) -{ - for (const int i : IndexRange(input.size())) { - r_output[i * scale + offset] = input[i]; - } -} - -template<typename CopyFn> -static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) -{ - input_spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id); - BLI_assert(src); - if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; - } - std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id); - if (!dst) { - BLI_assert_unreachable(); - return false; - } - - copy_fn(*src, *dst); - - return true; - }, - ATTR_DOMAIN_POINT); -} - -static SplinePtr convert_to_poly_spline(const Spline &input) -{ - std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>(); - output->resize(input.positions().size()); - output->positions().copy_from(input.positions()); - output->radii().copy_from(input.radii()); - output->tilts().copy_from(input.tilts()); - Spline::copy_base_settings(input, *output); - output->attributes = input.attributes; - return output; -} - -static SplinePtr poly_to_nurbs(const Spline &input) -{ - std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>(); - output->resize(input.positions().size()); - output->positions().copy_from(input.positions()); - output->radii().copy_from(input.radii()); - output->tilts().copy_from(input.tilts()); - output->weights().fill(1.0f); - output->set_resolution(12); - output->set_order(4); - Spline::copy_base_settings(input, *output); - output->knots_mode = NURBS_KNOT_MODE_BEZIER; - output->attributes = input.attributes; - return output; -} - -static SplinePtr bezier_to_nurbs(const Spline &input) -{ - const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input); - std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>(); - output->resize(input.size() * 3); - - scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions()); - scale_output_assign(input.radii(), 3, 0, output->radii()); - scale_output_assign(input.tilts(), 3, 0, output->tilts()); - - scale_output_assign(bezier_spline.positions(), 3, 1, output->positions()); - scale_output_assign(input.radii(), 3, 1, output->radii()); - scale_output_assign(input.tilts(), 3, 1, output->tilts()); - - scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions()); - scale_output_assign(input.radii(), 3, 2, output->radii()); - scale_output_assign(input.tilts(), 3, 2, output->tilts()); - - Spline::copy_base_settings(input, *output); - output->weights().fill(1.0f); - output->set_resolution(12); - output->set_order(4); - output->set_cyclic(input.is_cyclic()); - output->knots_mode = NURBS_KNOT_MODE_BEZIER; - output->attributes.reallocate(output->size()); - copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>()); - scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>()); - scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>()); - }); - }); - return output; -} - -static SplinePtr poly_to_bezier(const Spline &input) -{ - std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>(); - output->resize(input.size()); - output->positions().copy_from(input.positions()); - output->radii().copy_from(input.radii()); - output->tilts().copy_from(input.tilts()); - output->handle_types_left().fill(BEZIER_HANDLE_VECTOR); - output->handle_types_right().fill(BEZIER_HANDLE_VECTOR); - output->set_resolution(12); - Spline::copy_base_settings(input, *output); - output->attributes = input.attributes; - return output; -} - -static SplinePtr nurbs_to_bezier(const Spline &input) -{ - const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input); - std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>(); - output->resize(input.size() / 3); - scale_input_assign<float3>(input.positions(), 3, 1, output->positions()); - scale_input_assign<float3>(input.positions(), 3, 0, output->handle_positions_left()); - scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right()); - scale_input_assign<float>(input.radii(), 3, 2, output->radii()); - scale_input_assign<float>(input.tilts(), 3, 2, output->tilts()); - output->handle_types_left().fill(BEZIER_HANDLE_ALIGN); - output->handle_types_right().fill(BEZIER_HANDLE_ALIGN); - output->set_resolution(nurbs_spline.resolution()); - Spline::copy_base_settings(input, *output); - output->attributes.reallocate(output->size()); - copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - scale_input_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>()); - }); - }); - return output; -} - -static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) -{ - switch (input.type()) { - case CURVE_TYPE_BEZIER: - return input.copy(); - case CURVE_TYPE_POLY: - return poly_to_bezier(input); - case CURVE_TYPE_NURBS: - if (input.size() < 6) { - params.error_message_add( - NodeWarningType::Info, - TIP_("NURBS must have minimum of 6 points for Bezier Conversion")); - return input.copy(); - } - else { - if (input.size() % 3 != 0) { - params.error_message_add(NodeWarningType::Info, - TIP_("NURBS must have multiples of 3 points for full Bezier " - "conversion, curve truncated")); - } - return nurbs_to_bezier(input); - } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); - return {}; - } - } - BLI_assert_unreachable(); - return {}; -} - -static SplinePtr convert_to_nurbs(const Spline &input) -{ - switch (input.type()) { - case CURVE_TYPE_NURBS: - return input.copy(); - case CURVE_TYPE_BEZIER: - return bezier_to_nurbs(input); - case CURVE_TYPE_POLY: - return poly_to_nurbs(input); - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - return {}; - } - BLI_assert_unreachable(); - return {}; -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - const NodeGeometryCurveSplineType *storage = - (const NodeGeometryCurveSplineType *)params.node().storage; - const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type; - - GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curves()) { - params.set_output("Curve", geometry_set); - return; - } - - const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component->get_for_read()); - - const std::string selection_name = params.extract_input<std::string>("Selection"); - VArray<bool> selection = curve_component->attribute_get_for_read( - selection_name, ATTR_DOMAIN_CURVE, true); - - std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - for (const int i : curve->splines().index_range()) { - if (selection[i]) { - switch (output_type) { - case GEO_NODE_SPLINE_TYPE_POLY: - new_curve->add_spline(convert_to_poly_spline(*curve->splines()[i])); - break; - case GEO_NODE_SPLINE_TYPE_BEZIER: - new_curve->add_spline(convert_to_bezier(*curve->splines()[i], params)); - break; - case GEO_NODE_SPLINE_TYPE_NURBS: - new_curve->add_spline(convert_to_nurbs(*curve->splines()[i])); - break; - } - } - else { - new_curve->add_spline(curve->splines()[i]->copy()); - } - } - - new_curve->attributes = curve->attributes; - params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*new_curve))); -} - -} // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc - -void register_node_type_geo_legacy_curve_spline_type() -{ - namespace file_ns = blender::nodes::node_geo_legacy_curve_spline_type_cc; - - static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - node_type_init(&ntype, file_ns::node_init); - node_type_storage(&ntype, - "NodeGeometryCurveSplineType", - node_free_standard_storage, - node_copy_standard_storage); - ntype.draw_buttons = file_ns::node_layout; - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc deleted file mode 100644 index 03f7aec8838..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc +++ /dev/null @@ -1,384 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_task.hh" -#include "BLI_timeit.hh" - -#include "BKE_attribute_math.hh" -#include "BKE_spline.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_curve_subdivide_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Cuts")); - b.add_input<decl::Int>(N_("Cuts"), "Cuts_001").default_value(1).min(0).max(1000); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryCurveSubdivide *data = MEM_cnew<NodeGeometryCurveSubdivide>(__func__); - - data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; - - update_attribute_input_socket_availabilities( - *ntree, *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type); -} - -static Array<int> get_subdivided_offsets(const Spline &spline, - const VArray<int> &cuts, - const int spline_offset) -{ - Array<int> offsets(spline.segments_size() + 1); - int offset = 0; - for (const int i : IndexRange(spline.segments_size())) { - offsets[i] = offset; - offset = offset + std::max(cuts[spline_offset + i], 0) + 1; - } - offsets.last() = offset; - return offsets; -} - -template<typename T> -static void subdivide_attribute(Span<T> src, - const Span<int> offsets, - const bool is_cyclic, - MutableSpan<T> dst) -{ - const int src_size = src.size(); - threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { - for (const int i : range) { - const int cuts = offsets[i + 1] - offsets[i]; - dst[offsets[i]] = src[i]; - const float factor_delta = 1.0f / (cuts + 1.0f); - for (const int cut : IndexRange(cuts)) { - const float factor = (cut + 1) * factor_delta; - dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); - } - } - }); - - if (is_cyclic) { - const int i = src_size - 1; - const int cuts = offsets[i + 1] - offsets[i]; - dst[offsets[i]] = src.last(); - const float factor_delta = 1.0f / (cuts + 1.0f); - for (const int cut : IndexRange(cuts)) { - const float factor = (cut + 1) * factor_delta; - dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); - } - } - else { - dst.last() = src.last(); - } -} - -/** - * In order to generate a Bezier spline with the same shape as the input spline, apply the - * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the - * previous result point's right handle and the left handle at the end of the segment. - * - * \note Non-vector segments in the result spline are given free handles. This could possibly be - * improved with another pass that sets handles to aligned where possible, but currently that does - * not provide much benefit for the increased complexity. - */ -static void subdivide_bezier_segment(const BezierSpline &src, - const int index, - const int offset, - const int result_size, - Span<float3> src_positions, - Span<float3> src_handles_left, - Span<float3> src_handles_right, - MutableSpan<float3> dst_positions, - MutableSpan<float3> dst_handles_left, - MutableSpan<float3> dst_handles_right, - MutableSpan<int8_t> dst_type_left, - MutableSpan<int8_t> dst_type_right) -{ - const bool is_last_cyclic_segment = index == (src.size() - 1); - const int next_index = is_last_cyclic_segment ? 0 : index + 1; - - /* The first point in the segment is always copied. */ - dst_positions[offset] = src_positions[index]; - - if (src.segment_is_vector(index)) { - if (is_last_cyclic_segment) { - dst_type_left.first() = BEZIER_HANDLE_VECTOR; - } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); - - const float factor_delta = 1.0f / result_size; - for (const int cut : IndexRange(result_size)) { - const float factor = cut * factor_delta; - dst_positions[offset + cut] = attribute_math::mix2( - factor, src_positions[index], src_positions[next_index]); - } - } - else { - if (is_last_cyclic_segment) { - dst_type_left.first() = BEZIER_HANDLE_FREE; - } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); - - const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; - - /* Create a Bezier segment to update iteratively for every subdivision - * and references to the meaningful values for ease of use. */ - BezierSpline temp; - temp.resize(2); - float3 &segment_start = temp.positions().first(); - float3 &segment_end = temp.positions().last(); - float3 &handle_prev = temp.handle_positions_right().first(); - float3 &handle_next = temp.handle_positions_left().last(); - segment_start = src_positions[index]; - segment_end = src_positions[next_index]; - handle_prev = src_handles_right[index]; - handle_next = src_handles_left[next_index]; - - for (const int cut : IndexRange(result_size - 1)) { - const float parameter = 1.0f / (result_size - cut); - const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); - - /* Copy relevant temporary data to the result. */ - dst_handles_right[offset + cut] = insert.handle_prev; - dst_handles_left[offset + cut + 1] = insert.left_handle; - dst_positions[offset + cut + 1] = insert.position; - - /* Update the segment to prepare it for the next subdivision. */ - segment_start = insert.position; - handle_prev = insert.right_handle; - handle_next = insert.handle_next; - } - - /* Copy the handles for the last segment from the temporary spline. */ - dst_handles_right[offset + result_size - 1] = handle_prev; - dst_handles_left[i_segment_last] = handle_next; - } -} - -static void subdivide_bezier_spline(const BezierSpline &src, - const Span<int> offsets, - BezierSpline &dst) -{ - Span<float3> src_positions = src.positions(); - Span<float3> src_handles_left = src.handle_positions_left(); - Span<float3> src_handles_right = src.handle_positions_right(); - MutableSpan<float3> dst_positions = dst.positions(); - MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); - MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); - MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); - - threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { - for (const int i : range) { - subdivide_bezier_segment(src, - i, - offsets[i], - offsets[i + 1] - offsets[i], - src_positions, - src_handles_left, - src_handles_right, - dst_positions, - dst_handles_left, - dst_handles_right, - dst_type_left, - dst_type_right); - } - }); - - if (src.is_cyclic()) { - const int i_last = src.size() - 1; - subdivide_bezier_segment(src, - i_last, - offsets[i_last], - offsets.last() - offsets[i_last], - src_positions, - src_handles_left, - src_handles_right, - dst_positions, - dst_handles_left, - dst_handles_right, - dst_type_left, - dst_type_right); - } - else { - dst_positions.last() = src_positions.last(); - } -} - -static void subdivide_builtin_attributes(const Spline &src_spline, - const Span<int> offsets, - Spline &dst_spline) -{ - const bool is_cyclic = src_spline.is_cyclic(); - subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); - subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); - switch (src_spline.type()) { - case CURVE_TYPE_POLY: { - const PolySpline &src = static_cast<const PolySpline &>(src_spline); - PolySpline &dst = static_cast<PolySpline &>(dst_spline); - subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); - break; - } - case CURVE_TYPE_BEZIER: { - const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); - BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); - subdivide_bezier_spline(src, offsets, dst); - dst.mark_cache_invalid(); - break; - } - case CURVE_TYPE_NURBS: { - const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); - NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); - subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); - subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); - break; - } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); - break; - } - } -} - -static void subdivide_dynamic_attributes(const Spline &src_spline, - const Span<int> offsets, - Spline &dst_spline) -{ - const bool is_cyclic = src_spline.is_cyclic(); - src_spline.attributes.foreach_attribute( - [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id); - BLI_assert(src); - - if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) { - /* Since the source spline of the same type had the attribute, adding it should work. */ - BLI_assert_unreachable(); - } - - std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id); - BLI_assert(dst); - - attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { - using T = decltype(dummy); - subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); -} - -static SplinePtr subdivide_spline(const Spline &spline, - const VArray<int> &cuts, - const int spline_offset) -{ - if (spline.size() <= 1) { - return spline.copy(); - } - - /* Since we expect to access each value many times, it should be worth it to make sure count - * of cuts is a real span (especially considering the note below). Using the offset at each - * point facilitates subdividing in parallel later. */ - Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); - const int result_size = offsets.last() + int(!spline.is_cyclic()); - SplinePtr new_spline = spline.copy_only_settings(); - new_spline->resize(result_size); - subdivide_builtin_attributes(spline, offsets, *new_spline); - subdivide_dynamic_attributes(spline, offsets, *new_spline); - return new_spline; -} - -/** - * \note Passing the virtual array for the entire spline is possibly quite inefficient here when - * the attribute was on the point domain and stored separately for each spline already, and it - * prevents some other optimizations like skipping splines with a single attribute value of < 1. - * However, it allows the node to access builtin attribute easily, so it the makes most sense this - * way until the attribute API is refactored. - */ -static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve, - const VArray<int> &cuts) -{ - const Array<int> control_point_offsets = input_curve.control_point_offsets(); - const Span<SplinePtr> input_splines = input_curve.splines(); - - std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); - output_curve->resize(input_splines.size()); - output_curve->attributes = input_curve.attributes; - MutableSpan<SplinePtr> output_splines = output_curve->splines(); - - threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]); - } - }); - - return output_curve; -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (!geometry_set.has_curves()) { - params.set_output("Geometry", geometry_set); - return; - } - - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - VArray<int> cuts = params.get_input_attribute<int>("Cuts", component, ATTR_DOMAIN_POINT, 0); - if (cuts.is_single() && cuts.get_internal_single() < 1) { - params.set_output("Geometry", geometry_set); - return; - } - - std::unique_ptr<CurveEval> output_curve = subdivide_curve( - *curves_to_curve_eval(*component.get_for_read()), cuts); - - params.set_output("Geometry", - GeometrySet::create_with_curves(curve_eval_to_curves(*output_curve))); -} - -} // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc - -void register_node_type_geo_legacy_curve_subdivide() -{ - namespace file_ns = blender::nodes::node_geo_legacy_curve_subdivide_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.draw_buttons = file_ns::node_layout; - node_type_storage(&ntype, - "NodeGeometryCurveSubdivide", - node_free_standard_storage, - node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc deleted file mode 100644 index f8fcc3cc363..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc +++ /dev/null @@ -1,351 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_array.hh" -#include "BLI_task.hh" -#include "BLI_timeit.hh" - -#include "BKE_pointcloud.h" -#include "BKE_spline.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes { - -static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, - const AttributeIDRef &attribute_id, - const CustomDataType data_type) -{ - points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); - WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); - BLI_assert(attribute); - return attribute.varray.get_internal_span(); -} - -template<typename T> -static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points, - const AttributeIDRef &attribute_id) -{ - GMutableSpan attribute = create_attribute_and_retrieve_span( - points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>())); - return attribute.typed<T>(); -} - -CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, - const CurveEval &curve) -{ - CurveToPointsResults attributes; - - attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT); - - attributes.positions = create_attribute_and_retrieve_span<float3>(points, "position"); - attributes.radii = create_attribute_and_retrieve_span<float>(points, "radius"); - attributes.tilts = create_attribute_and_retrieve_span<float>(points, "tilt"); - - /* Because of the invariants of the curve component, we use the attributes of the - * first spline as a representative for the attribute meta data all splines. */ - curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - attributes.point_attributes.add_new( - attribute_id, - create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type)); - return true; - }, - ATTR_DOMAIN_POINT); - - attributes.tangents = create_attribute_and_retrieve_span<float3>(points, "tangent"); - attributes.normals = create_attribute_and_retrieve_span<float3>(points, "normal"); - attributes.rotations = create_attribute_and_retrieve_span<float3>(points, "rotation"); - - return attributes; -} - -} // namespace blender::nodes - -namespace blender::nodes::node_geo_legacy_curve_to_points_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000); - b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__); - - data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; - const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; - - bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; - bNodeSocket *length_socket = count_socket->next; - - nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); - nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); -} - -/** - * Evaluate splines in parallel to speed up the rest of the node's execution. - */ -static void evaluate_splines(Span<SplinePtr> splines) -{ - threading::parallel_for_each(splines, [](const SplinePtr &spline) { - /* These functions fill the corresponding caches on each spline. */ - spline->evaluated_positions(); - spline->evaluated_tangents(); - spline->evaluated_normals(); - spline->evaluated_lengths(); - }); -} - -static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, - const GeometryNodeCurveResampleMode mode, - const CurveEval &curve, - const Span<SplinePtr> splines) -{ - const int size = curve.splines().size(); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: { - const int count = params.extract_input<int>("Count"); - if (count < 1) { - return {0}; - } - Array<int> offsets(size + 1); - for (const int i : offsets.index_range()) { - offsets[i] = count * i; - } - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f); - Array<int> offsets(size + 1); - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - offset += splines[i]->length() / resolution + 1; - } - offsets.last() = offset; - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { - return curve.evaluated_point_offsets(); - } - } - BLI_assert_unreachable(); - return {0}; -} - -/** - * TODO: For non-poly splines, this has double copies that could be avoided as part - * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. - */ -static void copy_evaluated_point_attributes(Span<SplinePtr> splines, - Span<int> offsets, - CurveToPointsResults &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - - data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); - spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size)); - spline.interpolate_to_evaluated(spline.tilts()).materialize(data.tilts.slice(offset, size)); - - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - GMutableSpan point_span = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.interpolate_to_evaluated(spline_span) - .materialize(point_span.slice(offset, size).data()); - } - - data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); - data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); - } - }); -} - -static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines, - Span<int> offsets, - CurveToPointsResults &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size == 0) { - continue; - } - - const Array<float> uniform_samples = spline.sample_uniform_index_factors(size); - - spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); - - spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), - uniform_samples, - data.radii.slice(offset, size)); - - spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.tilts()), - uniform_samples, - data.tilts.slice(offset, size)); - - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - GMutableSpan point_span = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span), - uniform_samples, - point_span.slice(offset, size)); - } - - spline.sample_with_index_factors<float3>( - spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); - for (float3 &tangent : data.tangents) { - tangent = math::normalize(tangent); - } - - spline.sample_with_index_factors<float3>( - spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); - for (float3 &normals : data.normals) { - normals = math::normalize(normals); - } - } - }); -} - -/** - * \note Use attributes from the curve component rather than the attribute data directly on the - * attribute storage to allow reading the virtual spline attributes like "cyclic" and "resolution". - */ -static void copy_spline_domain_attributes(const CurveComponent &curve_component, - Span<int> offsets, - PointCloudComponent &points) -{ - curve_component.attribute_foreach( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (meta_data.domain != ATTR_DOMAIN_CURVE) { - return true; - } - GVArray spline_attribute = curve_component.attribute_get_for_read( - attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); - const CPPType &type = spline_attribute.type(); - - OutputAttribute result_attribute = points.attribute_try_get_for_output_only( - attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); - GMutableSpan result = result_attribute.as_span(); - - for (const int i : spline_attribute.index_range()) { - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size != 0) { - BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - spline_attribute.get(i, buffer); - type.fill_assign_n(buffer, result[offset], size); - } - } - - result_attribute.save(); - return true; - }); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; - const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (!geometry_set.has_curves()) { - params.set_output("Geometry", GeometrySet()); - return; - } - - const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); - const Span<SplinePtr> splines = curve->splines(); - curve->assert_valid_point_attributes(); - - evaluate_splines(splines); - - const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_size = offsets.last(); - if (total_size == 0) { - params.set_output("Geometry", GeometrySet()); - return; - } - - GeometrySet result = GeometrySet::create_with_pointcloud(BKE_pointcloud_new_nomain(total_size)); - PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>(); - - CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, - *curve); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: - case GEO_NODE_CURVE_RESAMPLE_LENGTH: - copy_uniform_sample_point_attributes(splines, offsets, new_attributes); - break; - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - copy_evaluated_point_attributes(splines, offsets, new_attributes); - break; - } - - copy_spline_domain_attributes(curve_component, offsets, point_component); - curve_create_default_rotation_attribute( - new_attributes.tangents, new_attributes.normals, new_attributes.rotations); - - /* The default radius is way too large for points, divide by 10. */ - for (float &radius : new_attributes.radii) { - radius *= 0.1f; - } - - params.set_output("Geometry", std::move(result)); -} - -} // namespace blender::nodes::node_geo_legacy_curve_to_points_cc - -void register_node_type_geo_legacy_curve_to_points() -{ - namespace file_ns = blender::nodes::node_geo_legacy_curve_to_points_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - node_type_storage( - &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc deleted file mode 100644 index ca98d83c137..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc +++ /dev/null @@ -1,726 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_array.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_customdata.h" -#include "BKE_mesh.h" -#include "BKE_pointcloud.h" -#include "BKE_spline.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_delete_geometry_cc { - -using blender::bke::CustomDataAttributes; - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Selection")); - b.add_input<decl::Bool>(N_("Invert")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask) -{ - for (const int i_out : mask.index_range()) { - r_data[i_out] = data[mask[i_out]]; - } -} - -static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map) -{ - BLI_assert(src_mesh.totvert == vertex_map.size()); - for (const int i_src : vertex_map.index_range()) { - const int i_dst = vertex_map[i_src]; - if (i_dst == -1) { - continue; - } - - const MVert &v_src = src_mesh.mvert[i_src]; - MVert &v_dst = dst_mesh.mvert[i_dst]; - - v_dst = v_src; - CustomData_copy_data(&src_mesh.vdata, &dst_mesh.vdata, i_src, i_dst, 1); - } -} - -static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map, - Span<int> edge_map) -{ - BLI_assert(src_mesh.totvert == vertex_map.size()); - BLI_assert(src_mesh.totedge == edge_map.size()); - for (const int i_src : IndexRange(src_mesh.totedge)) { - const int i_dst = edge_map[i_src]; - if (ELEM(i_dst, -1, -2)) { - continue; - } - - const MEdge &e_src = src_mesh.medge[i_src]; - MEdge &e_dst = dst_mesh.medge[i_dst]; - - CustomData_copy_data(&src_mesh.edata, &dst_mesh.edata, i_src, i_dst, 1); - e_dst = e_src; - e_dst.v1 = vertex_map[e_src.v1]; - e_dst.v2 = vertex_map[e_src.v2]; - } -} - -static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map, - Span<int> edge_map, - Span<int> masked_poly_indices, - Span<int> new_loop_starts) -{ - for (const int i_dst : masked_poly_indices.index_range()) { - const int i_src = masked_poly_indices[i_dst]; - - const MPoly &mp_src = src_mesh.mpoly[i_src]; - MPoly &mp_dst = dst_mesh.mpoly[i_dst]; - const int i_ml_src = mp_src.loopstart; - const int i_ml_dst = new_loop_starts[i_dst]; - - CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1); - CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src, i_ml_dst, mp_src.totloop); - - const MLoop *ml_src = src_mesh.mloop + i_ml_src; - MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; - - mp_dst = mp_src; - mp_dst.loopstart = i_ml_dst; - for (int i : IndexRange(mp_src.totloop)) { - ml_dst[i].v = vertex_map[ml_src[i].v]; - ml_dst[i].e = edge_map[ml_src[i].e]; - } - } -} - -static void spline_copy_builtin_attributes(const Spline &spline, - Spline &r_spline, - const IndexMask mask) -{ - copy_data(spline.positions(), r_spline.positions(), mask); - copy_data(spline.radii(), r_spline.radii(), mask); - copy_data(spline.tilts(), r_spline.tilts(), mask); - switch (spline.type()) { - case CURVE_TYPE_POLY: - break; - case CURVE_TYPE_BEZIER: { - const BezierSpline &src = static_cast<const BezierSpline &>(spline); - BezierSpline &dst = static_cast<BezierSpline &>(r_spline); - copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); - copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask); - copy_data(src.handle_types_left(), dst.handle_types_left(), mask); - copy_data(src.handle_types_right(), dst.handle_types_right(), mask); - break; - } - case CURVE_TYPE_NURBS: { - const NURBSpline &src = static_cast<const NURBSpline &>(spline); - NURBSpline &dst = static_cast<NURBSpline &>(r_spline); - copy_data(src.weights(), dst.weights(), mask); - break; - } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); - break; - } - } -} - -static void copy_dynamic_attributes(const CustomDataAttributes &src, - CustomDataAttributes &dst, - const IndexMask mask) -{ - src.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src_attribute = src.get_for_read(attribute_id); - BLI_assert(src_attribute); - - if (!dst.create(attribute_id, meta_data.data_type)) { - /* Since the source spline of the same type had the attribute, adding it should work. - */ - BLI_assert_unreachable(); - } - - std::optional<GMutableSpan> new_attribute = dst.get_for_write(attribute_id); - BLI_assert(new_attribute); - - attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { - using T = decltype(dummy); - copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask); - }); - return true; - }, - ATTR_DOMAIN_POINT); -} - -static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) -{ - SplinePtr new_spline = spline.copy_only_settings(); - new_spline->resize(mask.size()); - - spline_copy_builtin_attributes(spline, *new_spline, mask); - copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); - - return new_spline; -} - -static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve, - const StringRef name, - const bool invert) -{ - Span<SplinePtr> input_splines = input_curve.splines(); - std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); - - /* Keep track of which splines were copied to the result to copy spline domain attributes. */ - Vector<int64_t> copied_splines; - - if (input_curve.attributes.get_for_read(name)) { - VArray<bool> selection = input_curve.attributes.get_for_read<bool>(name, false); - for (const int i : input_splines.index_range()) { - if (selection[i] == invert) { - output_curve->add_spline(input_splines[i]->copy()); - copied_splines.append(i); - } - } - } - else { - /* Reuse index vector for each spline. */ - Vector<int64_t> indices_to_copy; - - for (const int i : input_splines.index_range()) { - const Spline &spline = *input_splines[i]; - VArray<bool> selection = spline.attributes.get_for_read<bool>(name, false); - - indices_to_copy.clear(); - for (const int i_point : IndexRange(spline.size())) { - if (selection[i_point] == invert) { - indices_to_copy.append(i_point); - } - } - - /* Avoid creating an empty spline. */ - if (indices_to_copy.is_empty()) { - continue; - } - - SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); - output_curve->add_spline(std::move(new_spline)); - copied_splines.append(i); - } - } - - if (copied_splines.is_empty()) { - return {}; - } - - output_curve->attributes.reallocate(output_curve->splines().size()); - copy_dynamic_attributes( - input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); - - return output_curve; -} - -static void delete_curve_selection(const CurveComponent &in_component, - CurveComponent &r_component, - const StringRef selection_name, - const bool invert) -{ - std::unique_ptr<CurveEval> r_curve = curve_delete( - *curves_to_curve_eval(*in_component.get_for_read()), selection_name, invert); - if (r_curve) { - r_component.replace(curve_eval_to_curves(*r_curve)); - } - else { - r_component.clear(); - } -} - -static void delete_point_cloud_selection(const PointCloudComponent &in_component, - PointCloudComponent &out_component, - const StringRef selection_name, - const bool invert) -{ - const VArray<bool> selection_attribute = in_component.attribute_get_for_read<bool>( - selection_name, ATTR_DOMAIN_POINT, false); - VArray_Span<bool> selection{selection_attribute}; - - const int total = selection.count(invert); - if (total == 0) { - out_component.clear(); - return; - } - out_component.replace(BKE_pointcloud_new_nomain(total)); - - /* Invert the inversion, because this deletes the selected points instead of keeping them. */ - copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert); -} - -static void compute_selected_vertices_from_vertex_selection(const VArray<bool> &vertex_selection, - const bool invert, - MutableSpan<int> r_vertex_map, - uint *r_num_selected_vertices) -{ - BLI_assert(vertex_selection.size() == r_vertex_map.size()); - - uint num_selected_vertices = 0; - for (const int i : r_vertex_map.index_range()) { - if (vertex_selection[i] != invert) { - r_vertex_map[i] = num_selected_vertices; - num_selected_vertices++; - } - else { - r_vertex_map[i] = -1; - } - } - - *r_num_selected_vertices = num_selected_vertices; -} - -static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, - const VArray<bool> &vertex_selection, - const bool invert, - MutableSpan<int> r_edge_map, - uint *r_num_selected_edges) -{ - BLI_assert(mesh.totedge == r_edge_map.size()); - - uint num_selected_edges = 0; - for (const int i : IndexRange(mesh.totedge)) { - const MEdge &edge = mesh.medge[i]; - - /* Only add the edge if both vertices will be in the new mesh. */ - if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { - r_edge_map[i] = num_selected_edges; - num_selected_edges++; - } - else { - r_edge_map[i] = -1; - } - } - - *r_num_selected_edges = num_selected_edges; -} - -static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, - const VArray<bool> &vertex_selection, - const bool invert, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - uint *r_num_selected_polys, - uint *r_num_selected_loops) -{ - BLI_assert(mesh.totvert == vertex_selection.size()); - - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - uint num_selected_loops = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; - - bool all_verts_in_selection = true; - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { - if (vertex_selection[loop.v] == invert) { - all_verts_in_selection = false; - break; - } - } - - if (all_verts_in_selection) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; - } - } - - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; -} - -/** - * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the - * edge are kept along with the edge. - */ -static void compute_selected_vertices_and_edges_from_edge_selection( - const Mesh &mesh, - const VArray<bool> &edge_selection, - const bool invert, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - uint *r_num_selected_vertices, - uint *r_num_selected_edges) -{ - BLI_assert(mesh.totedge == edge_selection.size()); - - uint num_selected_edges = 0; - uint num_selected_vertices = 0; - for (const int i : IndexRange(mesh.totedge)) { - const MEdge &edge = mesh.medge[i]; - if (edge_selection[i] != invert) { - r_edge_map[i] = num_selected_edges; - num_selected_edges++; - if (r_vertex_map[edge.v1] == -1) { - r_vertex_map[edge.v1] = num_selected_vertices; - num_selected_vertices++; - } - if (r_vertex_map[edge.v2] == -1) { - r_vertex_map[edge.v2] = num_selected_vertices; - num_selected_vertices++; - } - } - else { - r_edge_map[i] = -1; - } - } - - *r_num_selected_vertices = num_selected_vertices; - *r_num_selected_edges = num_selected_edges; -} - -/** - * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that - * polygon is kept. - */ -static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, - const VArray<bool> &edge_selection, - const bool invert, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - uint *r_num_selected_polys, - uint *r_num_selected_loops) -{ - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - uint num_selected_loops = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; - - bool all_edges_in_selection = true; - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { - if (edge_selection[loop.e] == invert) { - all_edges_in_selection = false; - break; - } - } - - if (all_edges_in_selection) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; - } - } - - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; -} - -/** - * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all - * vertices of that polygon or edge are in the selection. - */ -static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, - const VArray<bool> &vertex_selection, - const bool invert, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops) -{ - compute_selected_vertices_from_vertex_selection( - vertex_selection, invert, r_vertex_map, r_num_selected_vertices); - - compute_selected_edges_from_vertex_selection( - mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); - - compute_selected_polygons_from_vertex_selection(mesh, - vertex_selection, - invert, - r_selected_poly_indices, - r_loop_starts, - r_num_selected_polys, - r_num_selected_loops); -} - -/** - * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to - * that edge are kept as well. The polygons are kept if all edges are in the selection. - */ -static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, - const VArray<bool> &edge_selection, - const bool invert, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops) -{ - r_vertex_map.fill(-1); - compute_selected_vertices_and_edges_from_edge_selection(mesh, - edge_selection, - invert, - r_vertex_map, - r_edge_map, - r_num_selected_vertices, - r_num_selected_edges); - compute_selected_polygons_from_edge_selection(mesh, - edge_selection, - invert, - r_selected_poly_indices, - r_loop_starts, - r_num_selected_polys, - r_num_selected_loops); -} - -/** - * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices - * belonging to that polygon are kept as well. - */ -static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, - const VArray<bool> &poly_selection, - const bool invert, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops) -{ - BLI_assert(mesh.totpoly == poly_selection.size()); - BLI_assert(mesh.totedge == r_edge_map.size()); - r_vertex_map.fill(-1); - r_edge_map.fill(-1); - - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - uint num_selected_loops = 0; - uint num_selected_vertices = 0; - uint num_selected_edges = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; - /* We keep this one. */ - if (poly_selection[i] != invert) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; - - /* Add the vertices and the edges. */ - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { - /* Check first if it has not yet been added. */ - if (r_vertex_map[loop.v] == -1) { - r_vertex_map[loop.v] = num_selected_vertices; - num_selected_vertices++; - } - if (r_edge_map[loop.e] == -1) { - r_edge_map[loop.e] = num_selected_edges; - num_selected_edges++; - } - } - } - } - *r_num_selected_vertices = num_selected_vertices; - *r_num_selected_edges = num_selected_edges; - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; -} - -using FillMapsFunction = void (*)(const Mesh &mesh, - const VArray<bool> &selection, - const bool invert, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops); - -/** - * Delete the parts of the mesh that are in the selection. The `fill_maps_function` - * depends on the selection type: vertices, edges or faces. - */ -static Mesh *delete_mesh_selection(const Mesh &mesh_in, - const VArray<bool> &selection, - const bool invert, - FillMapsFunction fill_maps_function) -{ - Array<int> vertex_map(mesh_in.totvert); - uint num_selected_vertices; - - Array<int> edge_map(mesh_in.totedge); - uint num_selected_edges; - - Vector<int> selected_poly_indices; - Vector<int> new_loop_starts; - uint num_selected_polys; - uint num_selected_loops; - - /* Fill all the maps based on the selection. We delete everything - * in the selection instead of keeping it, so we need to invert it. */ - fill_maps_function(mesh_in, - selection, - !invert, - vertex_map, - edge_map, - selected_poly_indices, - new_loop_starts, - &num_selected_vertices, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); - - Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in, - num_selected_vertices, - num_selected_edges, - 0, - num_selected_loops, - num_selected_polys); - - /* Copy the selected parts of the mesh over to the new mesh. */ - copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map); - copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map); - copy_masked_polys_to_new_mesh( - mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts); - BKE_mesh_calc_edges_loose(result); - /* Tag to recalculate normals later. */ - BKE_mesh_normals_tag_dirty(result); - - return result; -} - -static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name) -{ - std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name); - if (!selection_attribute) { - /* The node will not do anything in this case, but this function must return something. */ - return ATTR_DOMAIN_POINT; - } - - /* Corners can't be deleted separately, so interpolate corner attributes - * to the face domain. Note that this choice is somewhat arbitrary. */ - if (selection_attribute->domain == ATTR_DOMAIN_CORNER) { - return ATTR_DOMAIN_FACE; - } - - return selection_attribute->domain; -} - -static void delete_mesh_selection(MeshComponent &component, - const Mesh &mesh_in, - const StringRef selection_name, - const bool invert) -{ - /* Figure out the best domain to use. */ - const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); - - /* This already checks if the attribute exists, and displays a warning in that case. */ - VArray<bool> selection = component.attribute_get_for_read<bool>( - selection_name, selection_domain, false); - - /* Check if there is anything to delete. */ - bool delete_nothing = true; - for (const int i : selection.index_range()) { - if (selection[i] != invert) { - delete_nothing = false; - break; - } - } - if (delete_nothing) { - return; - } - - Mesh *mesh_out; - switch (selection_domain) { - case ATTR_DOMAIN_POINT: - mesh_out = delete_mesh_selection( - mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection); - break; - case ATTR_DOMAIN_EDGE: - mesh_out = delete_mesh_selection( - mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection); - break; - case ATTR_DOMAIN_FACE: - mesh_out = delete_mesh_selection( - mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection); - break; - default: - BLI_assert_unreachable(); - mesh_out = nullptr; - break; - } - component.replace(mesh_out); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry::realize_instances_legacy(geometry_set); - - const bool invert = params.extract_input<bool>("Invert"); - const std::string selection_name = params.extract_input<std::string>("Selection"); - if (selection_name.empty()) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } - - GeometrySet out_set(geometry_set); - if (geometry_set.has<PointCloudComponent>()) { - delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(), - out_set.get_component_for_write<PointCloudComponent>(), - selection_name, - invert); - } - if (geometry_set.has<MeshComponent>()) { - delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(), - *geometry_set.get_mesh_for_read(), - selection_name, - invert); - } - if (geometry_set.has<CurveComponent>()) { - delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(), - out_set.get_component_for_write<CurveComponent>(), - selection_name, - invert); - } - - params.set_output("Geometry", std::move(out_set)); -} - -} // namespace blender::nodes::node_geo_legacy_delete_geometry_cc - -void register_node_type_geo_legacy_delete_geometry() -{ - namespace file_ns = blender::nodes::node_geo_legacy_delete_geometry_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY); - - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc deleted file mode 100644 index 74fea7cef22..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "DNA_modifier_types.h" - -#include "node_geometry_util.hh" - -extern "C" { -Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd); -} - -namespace blender::nodes::node_geo_legacy_edge_split_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Bool>(N_("Edge Angle")).default_value(true); - b.add_input<decl::Float>(N_("Angle")) - .default_value(DEG2RADF(30.0f)) - .min(0.0f) - .max(DEG2RADF(180.0f)) - .subtype(PROP_ANGLE); - b.add_input<decl::Bool>(N_("Sharp Edges")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (!geometry_set.has_mesh()) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } - - const bool use_sharp_flag = params.extract_input<bool>("Sharp Edges"); - const bool use_edge_angle = params.extract_input<bool>("Edge Angle"); - - if (!use_edge_angle && !use_sharp_flag) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } - - const float split_angle = params.extract_input<float>("Angle"); - const Mesh *mesh_in = geometry_set.get_mesh_for_read(); - - /* Use modifier struct to pass arguments to the modifier code. */ - EdgeSplitModifierData emd; - memset(&emd, 0, sizeof(EdgeSplitModifierData)); - emd.split_angle = split_angle; - if (use_edge_angle) { - emd.flags = MOD_EDGESPLIT_FROMANGLE; - } - if (use_sharp_flag) { - emd.flags |= MOD_EDGESPLIT_FROMFLAG; - } - - Mesh *mesh_out = doEdgeSplit(mesh_in, &emd); - geometry_set.replace_mesh(mesh_out); - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_edge_split_cc - -void register_node_type_geo_legacy_edge_split() -{ - namespace file_ns = blender::nodes::node_geo_legacy_edge_split_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.declare = file_ns::node_declare; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc deleted file mode 100644 index cb5f4044d6f..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "node_geometry_util.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_material.h" - -namespace blender::nodes::node_geo_legacy_material_assign_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Material>(N_("Material")).hide_label(true); - b.add_input<decl::String>(N_("Selection")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material) -{ - int new_material_index = -1; - for (const int i : IndexRange(mesh.totcol)) { - Material *other_material = mesh.mat[i]; - if (other_material == material) { - new_material_index = i; - break; - } - } - if (new_material_index == -1) { - /* Append a new material index. */ - new_material_index = mesh.totcol; - BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); - } - - mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); - for (const int i : IndexRange(mesh.totpoly)) { - if (face_mask[i]) { - MPoly &poly = mesh.mpoly[i]; - poly.mat_nr = new_material_index; - } - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - Material *material = params.extract_input<Material *>("Material"); - const std::string mask_name = params.extract_input<std::string>("Selection"); - - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - Mesh *mesh = mesh_component.get_for_write(); - if (mesh != nullptr) { - VArray<bool> face_mask = mesh_component.attribute_get_for_read<bool>( - mask_name, ATTR_DOMAIN_FACE, true); - assign_material_to_faces(*mesh, face_mask, material); - } - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_material_assign_cc - -void register_node_type_geo_legacy_material_assign() -{ - namespace file_ns = blender::nodes::node_geo_legacy_material_assign_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc deleted file mode 100644 index 72cb540df4a..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "GEO_mesh_to_curve.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Mesh")); - b.add_input<decl::String>(N_("Selection")); - b.add_output<decl::Geometry>(N_("Curve")); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (!geometry_set.has_mesh()) { - params.set_default_remaining_outputs(); - return; - } - - const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); - const std::string selection_name = params.extract_input<std::string>("Selection"); - if (!selection_name.empty() && !component.attribute_exists(selection_name)) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + selection_name + "\""); - } - VArray<bool> selection = component.attribute_get_for_read<bool>( - selection_name, ATTR_DOMAIN_EDGE, true); - - Vector<int64_t> selected_edge_indices; - for (const int64_t i : IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE))) { - if (selection[i]) { - selected_edge_indices.append(i); - } - } - - if (selected_edge_indices.size() == 0) { - params.set_default_remaining_outputs(); - return; - } - - Curves *curves = geometry::mesh_to_curve_convert(component, IndexMask(selected_edge_indices)); - params.set_output("Curve", GeometrySet::create_with_curves(curves)); -} - -} // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc - -void register_node_type_geo_legacy_mesh_to_curve() -{ - namespace file_ns = blender::nodes::node_geo_legacy_mesh_to_curve_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc deleted file mode 100644 index f7cd0624b53..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc +++ /dev/null @@ -1,657 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_hash.h" -#include "BLI_kdtree.h" -#include "BLI_rand.hh" -#include "BLI_timeit.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_pointcloud_types.h" - -#include "BKE_attribute_math.hh" -#include "BKE_bvhutils.h" -#include "BKE_geometry_set_instances.hh" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_sample.hh" -#include "BKE_pointcloud.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_point_distribute_cc { - -using blender::bke::GeometryInstanceGroup; - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Density Max")) - .default_value(1.0f) - .min(0.0f) - .max(100000.0f) - .subtype(PROP_NONE); - b.add_input<decl::String>(N_("Density Attribute")); - b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE); -} - -static void node_point_distribute_update(bNodeTree *ntree, bNode *node) -{ - bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1); - - nodeSetSocketAvailability( - ntree, sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON)); -} - -/** - * Use an arbitrary choice of axes for a usable rotation attribute directly out of this node. - */ -static float3 normal_to_euler_rotation(const float3 normal) -{ - float quat[4]; - vec_to_quat(quat, normal, OB_NEGZ, OB_POSY); - float3 rotation; - quat_to_eul(rotation, quat); - return rotation; -} - -static void sample_mesh_surface(const Mesh &mesh, - const float4x4 &transform, - const float base_density, - const VArray<float> *density_factors, - const int seed, - Vector<float3> &r_positions, - Vector<float3> &r_bary_coords, - Vector<int> &r_looptri_indices) -{ - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; - - for (const int looptri_index : looptris.index_range()) { - const MLoopTri &looptri = looptris[looptri_index]; - const int v0_loop = looptri.tri[0]; - const int v1_loop = looptri.tri[1]; - const int v2_loop = looptri.tri[2]; - const int v0_index = mesh.mloop[v0_loop].v; - const int v1_index = mesh.mloop[v1_loop].v; - const int v2_index = mesh.mloop[v2_loop].v; - const float3 v0_pos = transform * float3(mesh.mvert[v0_index].co); - const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co); - const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co); - - float looptri_density_factor = 1.0f; - if (density_factors != nullptr) { - const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]); - const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]); - const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]); - looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f; - } - const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); - - const int looptri_seed = BLI_hash_int(looptri_index + seed); - RandomNumberGenerator looptri_rng(looptri_seed); - - const float points_amount_fl = area * base_density * looptri_density_factor; - const float add_point_probability = fractf(points_amount_fl); - const bool add_point = add_point_probability > looptri_rng.get_float(); - const int point_amount = (int)points_amount_fl + (int)add_point; - - for (int i = 0; i < point_amount; i++) { - const float3 bary_coord = looptri_rng.get_barycentric_coordinates(); - float3 point_pos; - interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord); - r_positions.append(point_pos); - r_bary_coords.append(bary_coord); - r_looptri_indices.append(looptri_index); - } - } -} - -BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_all, - const int initial_points_len) -{ - KDTree_3d *kdtree = BLI_kdtree_3d_new(initial_points_len); - - int i_point = 0; - for (const Vector<float3> &positions : positions_all) { - for (const float3 position : positions) { - BLI_kdtree_3d_insert(kdtree, i_point, position); - i_point++; - } - } - BLI_kdtree_3d_balance(kdtree); - return kdtree; -} - -BLI_NOINLINE static void update_elimination_mask_for_close_points( - Span<Vector<float3>> positions_all, - Span<int> instance_start_offsets, - const float minimum_distance, - MutableSpan<bool> elimination_mask, - const int initial_points_len) -{ - if (minimum_distance <= 0.0f) { - return; - } - - KDTree_3d *kdtree = build_kdtree(positions_all, initial_points_len); - - /* The elimination mask is a flattened array for every point, - * so keep track of the index to it separately. */ - for (const int i_instance : positions_all.index_range()) { - Span<float3> positions = positions_all[i_instance]; - const int offset = instance_start_offsets[i_instance]; - - for (const int i : positions.index_range()) { - if (elimination_mask[offset + i]) { - continue; - } - - struct CallbackData { - int index; - MutableSpan<bool> elimination_mask; - } callback_data = {offset + i, elimination_mask}; - - BLI_kdtree_3d_range_search_cb( - kdtree, - positions[i], - minimum_distance, - [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { - CallbackData &callback_data = *static_cast<CallbackData *>(user_data); - if (index != callback_data.index) { - callback_data.elimination_mask[index] = true; - } - return true; - }, - &callback_data); - } - } - BLI_kdtree_3d_free(kdtree); -} - -BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( - const Mesh &mesh, - const VArray<float> &density_factors, - Span<float3> bary_coords, - Span<int> looptri_indices, - MutableSpan<bool> elimination_mask) -{ - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : bary_coords.index_range()) { - if (elimination_mask[i]) { - continue; - } - - const MLoopTri &looptri = looptris[looptri_indices[i]]; - const float3 bary_coord = bary_coords[i]; - - const int v0_loop = looptri.tri[0]; - const int v1_loop = looptri.tri[1]; - const int v2_loop = looptri.tri[2]; - - const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]); - const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); - const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); - - const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + - v2_density_factor * bary_coord.z; - - const float hash = BLI_hash_int_01(bary_coord.hash()); - if (hash > probablity) { - elimination_mask[i] = true; - } - } -} - -BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_mask, - Vector<float3> &positions, - Vector<float3> &bary_coords, - Vector<int> &looptri_indices) -{ - for (int i = positions.size() - 1; i >= 0; i--) { - if (elimination_mask[i]) { - positions.remove_and_reorder(i); - bary_coords.remove_and_reorder(i); - looptri_indices.remove_and_reorder(i); - } - } -} - -BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, - Span<float3> bary_coords, - Span<int> looptri_indices, - const AttributeDomain source_domain, - const GVArray &source_data, - GMutableSpan output_data) -{ - switch (source_domain) { - case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute(mesh, - looptri_indices, - bary_coords, - source_data, - IndexMask(output_data.size()), - output_data); - break; - } - case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute(mesh, - looptri_indices, - bary_coords, - source_data, - IndexMask(output_data.size()), - output_data); - break; - } - case ATTR_DOMAIN_FACE: { - bke::mesh_surface_sample::sample_face_attribute( - mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data); - break; - } - default: { - /* Not supported currently. */ - return; - } - } -} - -BLI_NOINLINE static void interpolate_existing_attributes( - Span<GeometryInstanceGroup> set_groups, - Span<int> instance_start_offsets, - const Map<AttributeIDRef, AttributeKind> &attributes, - GeometryComponent &component, - Span<Vector<float3>> bary_coords_array, - Span<Vector<int>> looptri_indices_array) -{ - for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { - const AttributeIDRef attribute_id = entry.key; - const CustomDataType output_data_type = entry.value.data_type; - /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ - OutputAttribute attribute_out = component.attribute_try_get_for_output_only( - attribute_id, ATTR_DOMAIN_POINT, output_data_type); - if (!attribute_out) { - continue; - } - - GMutableSpan out_span = attribute_out.as_span(); - - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *source_component.get_for_read(); - - std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data( - attribute_id); - if (!attribute_info) { - i_instance += set_group.transforms.size(); - continue; - } - - const AttributeDomain source_domain = attribute_info->domain; - GVArray source_attribute = source_component.attribute_get_for_read( - attribute_id, source_domain, output_data_type, nullptr); - if (!source_attribute) { - i_instance += set_group.transforms.size(); - continue; - } - - for ([[maybe_unused]] const int i_set_instance : set_group.transforms.index_range()) { - const int offset = instance_start_offsets[i_instance]; - Span<float3> bary_coords = bary_coords_array[i_instance]; - Span<int> looptri_indices = looptri_indices_array[i_instance]; - - GMutableSpan instance_span = out_span.slice(offset, bary_coords.size()); - interpolate_attribute( - mesh, bary_coords, looptri_indices, source_domain, source_attribute, instance_span); - - i_instance++; - } - - attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { - using T = decltype(dummy); - - VArray_Span source_span{source_attribute.typed<T>()}; - }); - } - - attribute_out.save(); - } -} - -BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> sets, - Span<int> instance_start_offsets, - GeometryComponent &component, - Span<Vector<float3>> bary_coords_array, - Span<Vector<int>> looptri_indices_array) -{ - OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( - "id", ATTR_DOMAIN_POINT); - OutputAttribute_Typed<float3> normal_attribute = - component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT); - OutputAttribute_Typed<float3> rotation_attribute = - component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT); - - MutableSpan<int> result_ids = id_attribute.as_span(); - MutableSpan<float3> result_normals = normal_attribute.as_span(); - MutableSpan<float3> result_rotations = rotation_attribute.as_span(); - - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : sets) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *component.get_for_read(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; - - for (const float4x4 &transform : set_group.transforms) { - const int offset = instance_start_offsets[i_instance]; - - Span<float3> bary_coords = bary_coords_array[i_instance]; - Span<int> looptri_indices = looptri_indices_array[i_instance]; - MutableSpan<int> ids = result_ids.slice(offset, bary_coords.size()); - MutableSpan<float3> normals = result_normals.slice(offset, bary_coords.size()); - MutableSpan<float3> rotations = result_rotations.slice(offset, bary_coords.size()); - - /* Use one matrix multiplication per point instead of three (for each triangle corner). */ - float rotation_matrix[3][3]; - mat4_to_rot(rotation_matrix, transform.values); - - for (const int i : bary_coords.index_range()) { - const int looptri_index = looptri_indices[i]; - const MLoopTri &looptri = looptris[looptri_index]; - const float3 &bary_coord = bary_coords[i]; - - const int v0_index = mesh.mloop[looptri.tri[0]].v; - const int v1_index = mesh.mloop[looptri.tri[1]].v; - const int v2_index = mesh.mloop[looptri.tri[2]].v; - const float3 v0_pos = float3(mesh.mvert[v0_index].co); - const float3 v1_pos = float3(mesh.mvert[v1_index].co); - const float3 v2_pos = float3(mesh.mvert[v2_index].co); - - ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index); - normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos); - mul_m3_v3(rotation_matrix, normals[i]); - rotations[i] = normal_to_euler_rotation(normals[i]); - } - - i_instance++; - } - } - - id_attribute.save(); - normal_attribute.save(); - rotation_attribute.save(); -} - -BLI_NOINLINE static void add_remaining_point_attributes( - Span<GeometryInstanceGroup> set_groups, - Span<int> instance_start_offsets, - const Map<AttributeIDRef, AttributeKind> &attributes, - GeometryComponent &component, - Span<Vector<float3>> bary_coords_array, - Span<Vector<int>> looptri_indices_array) -{ - interpolate_existing_attributes(set_groups, - instance_start_offsets, - attributes, - component, - bary_coords_array, - looptri_indices_array); - compute_special_attributes( - set_groups, instance_start_offsets, component, bary_coords_array, looptri_indices_array); -} - -static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, - const StringRef density_attribute_name, - const float density, - const int seed, - MutableSpan<Vector<float3>> positions_all, - MutableSpan<Vector<float3>> bary_coords_all, - MutableSpan<Vector<int>> looptri_indices_all) -{ - /* If there is an attribute name, the default value for the densities should be zero so that - * points are only scattered where the attribute exists. Otherwise, just "ignore" the density - * factors. */ - const bool use_one_default = density_attribute_name.is_empty(); - - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - VArray<float> density_factors = component.attribute_get_for_read<float>( - density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); - const Mesh &mesh = *component.get_for_read(); - for (const float4x4 &transform : set_group.transforms) { - Vector<float3> &positions = positions_all[i_instance]; - Vector<float3> &bary_coords = bary_coords_all[i_instance]; - Vector<int> &looptri_indices = looptri_indices_all[i_instance]; - sample_mesh_surface(mesh, - transform, - density, - &density_factors, - seed, - positions, - bary_coords, - looptri_indices); - i_instance++; - } - } -} - -static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_groups, - const StringRef density_attribute_name, - const float density, - const int seed, - const float minimum_distance, - MutableSpan<Vector<float3>> positions_all, - MutableSpan<Vector<float3>> bary_coords_all, - MutableSpan<Vector<int>> looptri_indices_all) -{ - Array<int> instance_start_offsets(positions_all.size()); - int initial_points_len = 0; - int i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *component.get_for_read(); - for (const float4x4 &transform : set_group.transforms) { - Vector<float3> &positions = positions_all[i_instance]; - Vector<float3> &bary_coords = bary_coords_all[i_instance]; - Vector<int> &looptri_indices = looptri_indices_all[i_instance]; - sample_mesh_surface( - mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices); - - instance_start_offsets[i_instance] = initial_points_len; - initial_points_len += positions.size(); - i_instance++; - } - } - - /* If there is an attribute name, the default value for the densities should be zero so that - * points are only scattered where the attribute exists. Otherwise, just "ignore" the density - * factors. */ - const bool use_one_default = density_attribute_name.is_empty(); - - /* Unlike the other result arrays, the elimination mask in stored as a flat array for every - * point, in order to simplify culling points from the KDTree (which needs to know about all - * points at once). */ - Array<bool> elimination_mask(initial_points_len, false); - update_elimination_mask_for_close_points(positions_all, - instance_start_offsets, - minimum_distance, - elimination_mask, - initial_points_len); - - i_instance = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *component.get_for_read(); - const VArray<float> density_factors = component.attribute_get_for_read<float>( - density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); - - for ([[maybe_unused]] const int i_set_instance : set_group.transforms.index_range()) { - Vector<float3> &positions = positions_all[i_instance]; - Vector<float3> &bary_coords = bary_coords_all[i_instance]; - Vector<int> &looptri_indices = looptri_indices_all[i_instance]; - - const int offset = instance_start_offsets[i_instance]; - update_elimination_mask_based_on_density_factors( - mesh, - density_factors, - bary_coords, - looptri_indices, - elimination_mask.as_mutable_span().slice(offset, positions.size())); - - eliminate_points_based_on_mask(elimination_mask.as_span().slice(offset, positions.size()), - positions, - bary_coords, - looptri_indices); - - i_instance++; - } - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - const GeometryNodePointDistributeMode distribute_method = - static_cast<GeometryNodePointDistributeMode>(params.node().custom1); - - const int seed = params.get_input<int>("Seed") * 5383843; - const float density = params.extract_input<float>("Density Max"); - const std::string density_attribute_name = params.extract_input<std::string>( - "Density Attribute"); - - if (density <= 0.0f) { - params.set_default_remaining_outputs(); - return; - } - - Vector<GeometryInstanceGroup> set_groups; - geometry_set_gather_instances(geometry_set, set_groups); - if (set_groups.is_empty()) { - params.set_default_remaining_outputs(); - return; - } - - /* Remove any set inputs that don't contain a mesh, to avoid checking later on. */ - for (int i = set_groups.size() - 1; i >= 0; i--) { - const GeometrySet &set = set_groups[i].geometry_set; - if (!set.has_mesh()) { - set_groups.remove_and_reorder(i); - } - } - - if (set_groups.is_empty()) { - params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh")); - params.set_default_remaining_outputs(); - return; - } - - int instances_len = 0; - for (GeometryInstanceGroup &set_group : set_groups) { - instances_len += set_group.transforms.size(); - } - - /* Store data per-instance in order to simplify attribute access after the scattering, - * and to make the point elimination simpler for the poisson disk mode. Note that some - * vectors will be empty if any instances don't contain mesh data. */ - Array<Vector<float3>> positions_all(instances_len); - Array<Vector<float3>> bary_coords_all(instances_len); - Array<Vector<int>> looptri_indices_all(instances_len); - - switch (distribute_method) { - case GEO_NODE_POINT_DISTRIBUTE_RANDOM: { - distribute_points_random(set_groups, - density_attribute_name, - density, - seed, - positions_all, - bary_coords_all, - looptri_indices_all); - break; - } - case GEO_NODE_POINT_DISTRIBUTE_POISSON: { - const float minimum_distance = params.extract_input<float>("Distance Min"); - distribute_points_poisson_disk(set_groups, - density_attribute_name, - density, - seed, - minimum_distance, - positions_all, - bary_coords_all, - looptri_indices_all); - break; - } - } - - int final_points_len = 0; - Array<int> instance_start_offsets(set_groups.size()); - for (const int i : positions_all.index_range()) { - Vector<float3> &positions = positions_all[i]; - instance_start_offsets[i] = final_points_len; - final_points_len += positions.size(); - } - - if (final_points_len == 0) { - params.set_default_remaining_outputs(); - return; - } - - PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len); - for (const int instance_index : positions_all.index_range()) { - const int offset = instance_start_offsets[instance_index]; - Span<float3> positions = positions_all[instance_index]; - memcpy(pointcloud->co + offset, positions.data(), sizeof(float3) * positions.size()); - } - - uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); - - GeometrySet geometry_set_out = GeometrySet::create_with_pointcloud(pointcloud); - PointCloudComponent &point_component = - geometry_set_out.get_component_for_write<PointCloudComponent>(); - - Map<AttributeIDRef, AttributeKind> attributes; - bke::geometry_set_gather_instances_attribute_info( - set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes); - add_remaining_point_attributes(set_groups, - instance_start_offsets, - attributes, - point_component, - bary_coords_all, - looptri_indices_all); - - params.set_output("Geometry", std::move(geometry_set_out)); -} - -} // namespace blender::nodes::node_geo_legacy_point_distribute_cc - -void register_node_type_geo_point_distribute() -{ - namespace file_ns = blender::nodes::node_geo_legacy_point_distribute_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY); - node_type_update(&ntype, file_ns::node_point_distribute_update); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc deleted file mode 100644 index 3d31773300d..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc +++ /dev/null @@ -1,268 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "DNA_collection_types.h" - -#include "BLI_hash.h" -#include "BLI_task.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_point_instance_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Object>(N_("Object")).hide_label(); - b.add_input<decl::Collection>(N_("Collection")).hide_label(); - b.add_input<decl::Geometry>(N_("Instance Geometry")); - b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "instance_type", 0, "", ICON_NONE); - if (RNA_enum_get(ptr, "instance_type") == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) { - uiItemR(layout, ptr, "use_whole_collection", 0, nullptr, ICON_NONE); - } -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryPointInstance *data = MEM_cnew<NodeGeometryPointInstance>(__func__); - data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT; - data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); - bNodeSocket *collection_socket = object_socket->next; - bNodeSocket *instance_geometry_socket = collection_socket->next; - bNodeSocket *seed_socket = instance_geometry_socket->next; - - NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node->storage; - GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node_storage->instance_type; - const bool use_whole_collection = (node_storage->flag & - GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; - - nodeSetSocketAvailability(ntree, object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); - nodeSetSocketAvailability( - ntree, collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION); - nodeSetSocketAvailability( - ntree, instance_geometry_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY); - nodeSetSocketAvailability(ntree, - seed_socket, - type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && - !use_whole_collection); -} - -static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams ¶ms) -{ - Object *object = params.extract_input<Object *>("Object"); - if (object == params.self_object()) { - return {}; - } - if (object != nullptr) { - return {*object}; - } - return {}; -} - -static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; - - Collection *collection = params.get_input<Collection *>("Collection"); - if (collection == nullptr) { - return {}; - } - - if (BLI_listbase_is_empty(&collection->children) && - BLI_listbase_is_empty(&collection->gobject)) { - params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty")); - return {}; - } - - if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) { - return {*collection}; - } - - Vector<InstanceReference> references; - /* Direct child objects are instanced as objects. */ - LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - references.append(*cob->ob); - } - /* Direct child collections are instanced as collections. */ - LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - references.append(*child->collection); - } - - return references; -} - -static Vector<InstanceReference> get_instance_references__geometry(GeoNodeExecParams ¶ms) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Instance Geometry"); - geometry_set.ensure_owns_direct_data(); - return {std::move(geometry_set)}; -} - -static Vector<InstanceReference> get_instance_references(GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; - const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType) - node_storage->instance_type; - - switch (type) { - case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: { - return get_instance_references__object(params); - } - case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: { - return get_instance_references__collection(params); - } - case GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY: { - return get_instance_references__geometry(params); - } - } - return {}; -} - -/** - * Add the instance references to the component as a separate step from actually creating the - * instances in order to avoid a map lookup for every transform. While this might add some - * unnecessary references if they are not chosen while adding transforms, in the common cases - * there are many more transforms than there are references, so that isn't likely. - */ -static Array<int> add_instance_references(InstancesComponent &instance_component, - Span<InstanceReference> possible_references) -{ - Array<int> possible_handles(possible_references.size()); - for (const int i : possible_references.index_range()) { - possible_handles[i] = instance_component.add_reference(possible_references[i]); - } - return possible_handles; -} - -static void add_instances_from_component(InstancesComponent &instances, - const GeometryComponent &src_geometry, - Span<int> possible_handles, - const GeoNodeExecParams ¶ms) -{ - const AttributeDomain domain = ATTR_DOMAIN_POINT; - - const int domain_size = src_geometry.attribute_domain_size(domain); - if (domain_size == 0) { - return; - } - - VArray<float3> positions = src_geometry.attribute_get_for_read<float3>( - "position", domain, {0, 0, 0}); - VArray<float3> rotations = src_geometry.attribute_get_for_read<float3>( - "rotation", domain, {0, 0, 0}); - VArray<float3> scales = src_geometry.attribute_get_for_read<float3>("scale", domain, {1, 1, 1}); - VArray<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1); - - /* The initial size of the component might be non-zero if there are two component types. */ - const int start_len = instances.instances_amount(); - instances.resize(start_len + domain_size); - MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size); - MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size); - OutputAttribute_Typed<int> instance_id_attribute = - instances.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_INSTANCE); - MutableSpan<int> instance_ids = instance_id_attribute.as_span(); - - /* Skip all of the randomness handling if there is only a single possible instance - * (anything except for collection mode with "Whole Collection" turned off). */ - if (possible_handles.size() == 1) { - const int handle = possible_handles.first(); - threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { - for (const int i : range) { - handles[i] = handle; - transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); - instance_ids[i] = id_attribute[i]; - } - }); - } - else { - const int seed = params.get_input<int>("Seed"); - Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT); - threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { - for (const int i : range) { - const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size(); - const int handle = possible_handles[index]; - handles[i] = handle; - transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); - instance_ids[i] = id_attribute[i]; - } - }); - } - - instance_id_attribute.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_out; - - /* TODO: This node should be able to instance on the input instances component - * rather than making the entire input geometry set real. */ - geometry_set = geometry::realize_instances_legacy(geometry_set); - - const Vector<InstanceReference> possible_references = get_instance_references(params); - if (possible_references.is_empty()) { - params.set_output("Geometry", std::move(geometry_set_out)); - return; - } - - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); - Array<int> possible_handles = add_instance_references(instances, possible_references); - - if (geometry_set.has<MeshComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<MeshComponent>(), - possible_handles, - params); - } - if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<PointCloudComponent>(), - possible_handles, - params); - } - if (geometry_set.has<CurveComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<CurveComponent>(), - possible_handles, - params); - } - - params.set_output("Geometry", std::move(geometry_set_out)); -} - -} // namespace blender::nodes::node_geo_legacy_point_instance_cc - -void register_node_type_geo_point_instance() -{ - namespace file_ns = blender::nodes::node_geo_legacy_point_instance_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_storage( - &ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.draw_buttons = file_ns::node_layout; - node_type_update(&ntype, file_ns::node_update); - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc deleted file mode 100644 index 2895c15cb8c..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc +++ /dev/null @@ -1,223 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_math_rotation.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_point_rotate_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Axis")); - b.add_input<decl::Vector>(N_("Axis"), "Axis_001") - .default_value({0.0, 0.0, 1.0}) - .subtype(PROP_XYZ); - b.add_input<decl::String>(N_("Angle")); - b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); - b.add_input<decl::String>(N_("Rotation")); - b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - NodeGeometryRotatePoints *storage = (NodeGeometryRotatePoints *)((bNode *)ptr->data)->storage; - - uiItemR(layout, ptr, "type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); - uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); - - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - - uiLayout *col = uiLayoutColumn(layout, false); - if (storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) { - uiItemR(col, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE); - uiItemR(col, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE); - } - else { - uiItemR(col, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE); - } -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryRotatePoints *node_storage = MEM_cnew<NodeGeometryRotatePoints>(__func__); - - node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER; - node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT; - node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - - node->storage = node_storage; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage; - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Axis", - (GeometryNodeAttributeInputMode)node_storage->input_type_axis, - node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Angle", - (GeometryNodeAttributeInputMode)node_storage->input_type_angle, - node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Rotation", - (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, - node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER); -} - -static void point_rotate__axis_angle__object_space(const int domain_size, - const VArray<float3> &axis, - const VArray<float> &angles, - MutableSpan<float3> rotations) -{ - for (const int i : IndexRange(domain_size)) { - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float rotation[3][3]; - axis_angle_to_mat3(rotation, axis[i], angles[i]); - float new_rotation[3][3]; - mul_m3_m3m3(new_rotation, rotation, old_rotation); - mat3_to_eul(rotations[i], new_rotation); - } -} - -static void point_rotate__axis_angle__point_space(const int domain_size, - const VArray<float3> &axis, - const VArray<float> &angles, - MutableSpan<float3> rotations) -{ - for (const int i : IndexRange(domain_size)) { - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float rotation[3][3]; - axis_angle_to_mat3(rotation, axis[i], angles[i]); - float new_rotation[3][3]; - mul_m3_m3m3(new_rotation, old_rotation, rotation); - mat3_to_eul(rotations[i], new_rotation); - } -} - -static void point_rotate__euler__object_space(const int domain_size, - const VArray<float3> &eulers, - MutableSpan<float3> rotations) -{ - for (const int i : IndexRange(domain_size)) { - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float rotation[3][3]; - eul_to_mat3(rotation, eulers[i]); - float new_rotation[3][3]; - mul_m3_m3m3(new_rotation, rotation, old_rotation); - mat3_to_eul(rotations[i], new_rotation); - } -} - -static void point_rotate__euler__point_space(const int domain_size, - const VArray<float3> &eulers, - MutableSpan<float3> rotations) -{ - for (const int i : IndexRange(domain_size)) { - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float rotation[3][3]; - eul_to_mat3(rotation, eulers[i]); - float new_rotation[3][3]; - mul_m3_m3m3(new_rotation, old_rotation, rotation); - mat3_to_eul(rotations[i], new_rotation); - } -} - -static void point_rotate_on_component(GeometryComponent &component, - const GeoNodeExecParams ¶ms) -{ - const bNode &node = params.node(); - const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage; - - OutputAttribute_Typed<float3> rotation_attribute = - component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0}); - if (!rotation_attribute) { - return; - } - - MutableSpan<float3> rotations = rotation_attribute.as_span(); - const int domain_size = rotations.size(); - - if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) { - VArray<float3> axis = params.get_input_attribute<float3>( - "Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1}); - VArray<float> angles = params.get_input_attribute<float>( - "Angle", component, ATTR_DOMAIN_POINT, 0); - - if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { - point_rotate__axis_angle__object_space(domain_size, axis, angles, rotations); - } - else { - point_rotate__axis_angle__point_space(domain_size, axis, angles, rotations); - } - } - else { - VArray<float3> eulers = params.get_input_attribute<float3>( - "Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); - - if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { - point_rotate__euler__object_space(domain_size, eulers, rotations); - } - else { - point_rotate__euler__point_space(domain_size, eulers, rotations); - } - } - - rotation_attribute.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - point_rotate_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); - } - if (geometry_set.has<PointCloudComponent>()) { - point_rotate_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); - } - if (geometry_set.has<CurveComponent>()) { - point_rotate_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_point_rotate_cc - -void register_node_type_geo_point_rotate() -{ - namespace file_ns = blender::nodes::node_geo_legacy_point_rotate_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage( - &ntype, "NodeGeometryRotatePoints", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc deleted file mode 100644 index b51ef22fb49..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc +++ /dev/null @@ -1,126 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BKE_colorband.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_point_scale_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Factor")); - b.add_input<decl::Vector>(N_("Factor"), "Factor_001") - .default_value({1.0f, 1.0f, 1.0f}) - .subtype(PROP_XYZ); - b.add_input<decl::Float>(N_("Factor"), "Factor_002").default_value(1.0f).min(0.0f); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryPointScale *data = MEM_cnew<NodeGeometryPointScale>(__func__); - - data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage; - - update_attribute_input_socket_availabilities( - *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type); -} - -static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component) -{ - /* Note that scale doesn't necessarily need to be created with a vector type-- it could also use - * the highest complexity of the existing attribute's type (if it exists) and the data type used - * for the factor. But for it's simpler to simply always use float3, since that is usually - * expected anyway. */ - static const float3 scale_default = float3(1.0f); - OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output( - "scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default); - if (!scale_attribute) { - return; - } - - const bNode &node = params.node(); - const NodeGeometryPointScale &node_storage = *(const NodeGeometryPointScale *)node.storage; - const GeometryNodeAttributeInputMode input_type = (GeometryNodeAttributeInputMode) - node_storage.input_type; - const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT : - CD_PROP_FLOAT3; - - GVArray attribute = params.get_input_attribute( - "Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr); - if (!attribute) { - return; - } - - MutableSpan<float3> scale_span = scale_attribute.as_span(); - if (data_type == CD_PROP_FLOAT) { - VArray<float> factors = attribute.typed<float>(); - for (const int i : scale_span.index_range()) { - scale_span[i] = scale_span[i] * factors[i]; - } - } - else if (data_type == CD_PROP_FLOAT3) { - VArray<float3> factors = attribute.typed<float3>(); - for (const int i : scale_span.index_range()) { - scale_span[i] = scale_span[i] * factors[i]; - } - } - - scale_attribute.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); - } - if (geometry_set.has<PointCloudComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); - } - if (geometry_set.has<CurveComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_point_scale_cc - -void register_node_type_geo_point_scale() -{ - namespace file_ns = blender::nodes::node_geo_legacy_point_scale_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY); - - ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage( - &ntype, "NodeGeometryPointScale", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc deleted file mode 100644 index 541be1933af..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BKE_attribute_math.hh" -#include "BKE_mesh.h" -#include "BKE_pointcloud.h" - -#include "DNA_mesh_types.h" -#include "DNA_pointcloud_types.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes { - -template<typename T> -static void copy_data_based_on_mask(Span<T> data, - Span<bool> masks, - const bool invert, - MutableSpan<T> out_data) -{ - int offset = 0; - for (const int i : data.index_range()) { - if (masks[i] != invert) { - out_data[offset] = data[i]; - offset++; - } - } -} - -void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, - GeometryComponent &result_component, - Span<bool> masks, - const bool invert) -{ - for (const AttributeIDRef &attribute_id : in_component.attribute_ids()) { - ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); - - /* Only copy point attributes. Theoretically this could interpolate attributes on other - * domains to the point domain, but that would conflict with attributes that are built-in - * on other domains, which causes creating the attributes to fail. */ - if (attribute.domain != ATTR_DOMAIN_POINT) { - continue; - } - - OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( - attribute_id, ATTR_DOMAIN_POINT, data_type); - - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray_Span span{attribute.varray.typed<T>()}; - MutableSpan<T> out_span = result_attribute.as_span<T>(); - copy_data_based_on_mask(span, masks, invert, out_span); - }); - - result_attribute.save(); - } -} - -} // namespace blender::nodes - -namespace blender::nodes::node_geo_legacy_point_separate_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Mask")); - b.add_output<decl::Geometry>(N_("Geometry 1")); - b.add_output<decl::Geometry>(N_("Geometry 2")); -} - -static void create_component_points(GeometryComponent &component, const int total) -{ - switch (component.type()) { - case GEO_COMPONENT_TYPE_MESH: - static_cast<MeshComponent &>(component).replace(BKE_mesh_new_nomain(total, 0, 0, 0, 0)); - break; - case GEO_COMPONENT_TYPE_POINT_CLOUD: - static_cast<PointCloudComponent &>(component).replace(BKE_pointcloud_new_nomain(total)); - break; - default: - BLI_assert(false); - break; - } -} - -static void separate_points_from_component(const GeometryComponent &in_component, - GeometryComponent &out_component, - const StringRef mask_name, - const bool invert) -{ - if (!in_component.attribute_domain_supported(ATTR_DOMAIN_POINT) || - in_component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0) { - return; - } - - const VArray<bool> mask_attribute = in_component.attribute_get_for_read<bool>( - mask_name, ATTR_DOMAIN_POINT, false); - VArray_Span<bool> masks{mask_attribute}; - - const int total = masks.count(!invert); - if (total == 0) { - return; - } - - create_component_points(out_component, total); - - copy_point_attributes_based_on_mask(in_component, out_component, masks, invert); -} - -static GeometrySet separate_geometry_set(const GeometrySet &set_in, - const StringRef mask_name, - const bool invert) -{ - GeometrySet set_out; - for (const GeometryComponent *component : set_in.get_components_for_read()) { - if (component->type() == GEO_COMPONENT_TYPE_CURVE) { - /* Don't support the curve component for now, even though it has a point domain. */ - continue; - } - GeometryComponent &out_component = set_out.get_component_for_write(component->type()); - separate_points_from_component(*component, out_component, mask_name, invert); - } - return set_out; -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - bool wait_for_inputs = false; - wait_for_inputs |= params.lazy_require_input("Geometry"); - wait_for_inputs |= params.lazy_require_input("Mask"); - if (wait_for_inputs) { - return; - } - const std::string mask_attribute_name = params.get_input<std::string>("Mask"); - GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry"); - - /* TODO: This is not necessary-- the input geometry set can be read only, - * but it must be rewritten to handle instance groups. */ - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (params.lazy_output_is_required("Geometry 1")) { - params.set_output("Geometry 1", - separate_geometry_set(geometry_set, mask_attribute_name, true)); - } - if (params.lazy_output_is_required("Geometry 2")) { - params.set_output("Geometry 2", - separate_geometry_set(geometry_set, mask_attribute_name, false)); - } -} - -} // namespace blender::nodes::node_geo_legacy_point_separate_cc - -void register_node_type_geo_point_separate() -{ - namespace file_ns = blender::nodes::node_geo_legacy_point_separate_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.geometry_node_execute_supports_laziness = true; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc deleted file mode 100644 index c1486da3c2e..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_point_translate_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Translation")); - b.add_input<decl::Vector>(N_("Translation"), "Translation_001").subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); -} - -static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component) -{ - OutputAttribute_Typed<float3> position_attribute = - component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0}); - if (!position_attribute) { - return; - } - VArray<float3> attribute = params.get_input_attribute<float3>( - "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); - - for (const int i : attribute.index_range()) { - position_attribute->set(i, position_attribute->get(i) + attribute[i]); - } - - position_attribute.save(); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); - } - if (geometry_set.has<PointCloudComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); - } - if (geometry_set.has<CurveComponent>()) { - execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryPointTranslate *data = MEM_cnew<NodeGeometryPointTranslate>(__func__); - - data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; - - update_attribute_input_socket_availabilities( - *ntree, *node, "Translation", (GeometryNodeAttributeInputMode)node_storage.input_type); -} - -} // namespace blender::nodes::node_geo_legacy_point_translate_cc - -void register_node_type_geo_point_translate() -{ - namespace file_ns = blender::nodes::node_geo_legacy_point_translate_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage(&ntype, - "NodeGeometryPointTranslate", - node_free_standard_storage, - node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc deleted file mode 100644 index c83571f1967..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc +++ /dev/null @@ -1,266 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#ifdef WITH_OPENVDB -# include <openvdb/openvdb.h> -# include <openvdb/tools/LevelSetUtil.h> -# include <openvdb/tools/ParticlesToLevelSet.h> -#endif - -#include "node_geometry_util.hh" - -#include "BKE_lib_id.h" -#include "BKE_volume.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -namespace blender::nodes::node_geo_legacy_points_to_volume_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f); - b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); - b.add_input<decl::String>(N_("Radius")); - b.add_input<decl::Float>(N_("Radius"), "Radius_001").default_value(0.5f).min(0.0f); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); - uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__); - data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; - data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node->storage = data; - - bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius"); - bNodeSocketValueString *radius_attribute_socket_value = - (bNodeSocketValueString *)radius_attribute_socket->default_value; - STRNCPY(radius_attribute_socket_value->value, "radius"); -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage; - bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); - bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); - nodeSetSocketAvailability(ntree, - voxel_amount_socket, - data->resolution_mode == - GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); - nodeSetSocketAvailability(ntree, - voxel_size_socket, - data->resolution_mode == - GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); - - update_attribute_input_socket_availabilities( - *ntree, *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); -} - -#ifdef WITH_OPENVDB -namespace { -/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */ -struct ParticleList { - using PosType = openvdb::Vec3R; - - Span<float3> positions; - Span<float> radii; - - size_t size() const - { - return (size_t)positions.size(); - } - - void getPos(size_t n, openvdb::Vec3R &xyz) const - { - xyz = &positions[n].x; - } - - void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const - { - xyz = &positions[n].x; - radius = radii[n]; - } -}; -} // namespace - -static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span<float3> positions, - const Span<float> radii, - const float density) -{ - /* Create a new grid that will be filled. #ParticlesToLevelSet requires the background value to - * be positive. It will be set to zero later on. */ - openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f); - - /* Create a narrow-band level set grid based on the positions and radii. */ - openvdb::tools::ParticlesToLevelSet op{*new_grid}; - /* Don't ignore particles based on their radius. */ - op.setRmin(0.0f); - op.setRmax(FLT_MAX); - ParticleList particles{positions, radii}; - op.rasterizeSpheres(particles); - op.finalize(); - - /* Convert the level set to a fog volume. This also sets the background value to zero. Inside the - * fog there will be a density of 1. */ - openvdb::tools::sdfToFogVolume(*new_grid); - - /* Take the desired density into account. */ - openvdb::tools::foreach (new_grid->beginValueOn(), - [&](const openvdb::FloatGrid::ValueOnIter &iter) { - iter.modifyValue([&](float &value) { value *= density; }); - }); - return new_grid; -} - -static float compute_voxel_size(const GeoNodeExecParams ¶ms, - Span<float3> positions, - const float radius) -{ - const NodeGeometryPointsToVolume &storage = - *(const NodeGeometryPointsToVolume *)params.node().storage; - - if (storage.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE) { - return params.get_input<float>("Voxel Size"); - } - - if (positions.is_empty()) { - return 0.0f; - } - - float3 min, max; - INIT_MINMAX(min, max); - minmax_v3v3_v3_array(min, max, (float(*)[3])positions.data(), positions.size()); - - const float voxel_amount = params.get_input<float>("Voxel Amount"); - if (voxel_amount <= 1) { - return 0.0f; - } - - /* The voxel size adapts to the final size of the volume. */ - const float diagonal = math::distance(min, max); - const float extended_diagonal = diagonal + 2.0f * radius; - const float voxel_size = extended_diagonal / voxel_amount; - return voxel_size; -} - -static void gather_point_data_from_component(const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - Vector<float3> &r_positions, - Vector<float> &r_radii) -{ - VArray<float3> positions = component.attribute_get_for_read<float3>( - "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - VArray<float> radii = params.get_input_attribute<float>( - "Radius", component, ATTR_DOMAIN_POINT, 0.0f); - - for (const int i : positions.index_range()) { - r_positions.append(positions[i]); - r_radii.append(radii[i]); - } -} - -static void convert_to_grid_index_space(const float voxel_size, - MutableSpan<float3> positions, - MutableSpan<float> radii) -{ - const float voxel_size_inv = 1.0f / voxel_size; - for (const int i : positions.index_range()) { - positions[i] *= voxel_size_inv; - /* Better align generated grid with source points. */ - positions[i] -= float3(0.5f); - radii[i] *= voxel_size_inv; - } -} - -static void initialize_volume_component_from_points(const GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - const GeoNodeExecParams ¶ms) -{ - Vector<float3> positions; - Vector<float> radii; - - if (geometry_set_in.has<MeshComponent>()) { - gather_point_data_from_component( - params, *geometry_set_in.get_component_for_read<MeshComponent>(), positions, radii); - } - if (geometry_set_in.has<PointCloudComponent>()) { - gather_point_data_from_component( - params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii); - } - if (geometry_set_in.has<CurveComponent>()) { - gather_point_data_from_component( - params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii); - } - - const float max_radius = *std::max_element(radii.begin(), radii.end()); - const float voxel_size = compute_voxel_size(params, positions, max_radius); - if (voxel_size == 0.0f || positions.is_empty()) { - return; - } - - Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); - BKE_volume_init_grids(volume); - - VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT); - openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>( - BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); - - const float density = params.get_input<float>("Density"); - convert_to_grid_index_space(voxel_size, positions, radii); - openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); - /* This merge is cheap, because the #density_grid is empty. */ - density_grid->merge(*new_grid); - density_grid->transform().postScale(voxel_size); - - VolumeComponent &volume_component = geometry_set_out.get_component_for_write<VolumeComponent>(); - volume_component.replace(volume); -} -#endif - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_out; - - /* TODO: Read-only access to instances should be supported here, for now they are made real. */ - geometry_set_in = geometry::realize_instances_legacy(geometry_set_in); - -#ifdef WITH_OPENVDB - initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params); -#endif - - params.set_output("Geometry", std::move(geometry_set_out)); -} - -} // namespace blender::nodes::node_geo_legacy_points_to_volume_cc - -void register_node_type_geo_legacy_points_to_volume() -{ - namespace file_ns = blender::nodes::node_geo_legacy_points_to_volume_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY); - node_type_storage(&ntype, - "NodeGeometryPointsToVolume", - node_free_standard_storage, - node_copy_standard_storage); - node_type_size(&ntype, 170, 120, 700); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc deleted file mode 100644 index 1420edcca0b..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc +++ /dev/null @@ -1,313 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "DNA_mesh_types.h" - -#include "BKE_bvhutils.h" -#include "BKE_mesh_sample.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_raycast_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Geometry>(N_("Target Geometry")); - b.add_input<decl::String>(N_("Ray Direction")); - b.add_input<decl::Vector>(N_("Ray Direction"), "Ray Direction_001") - .default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::String>(N_("Ray Length")); - b.add_input<decl::Float>(N_("Ray Length"), "Ray Length_001") - .default_value(100.0f) - .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input<decl::String>(N_("Target Attribute")); - b.add_input<decl::String>(N_("Is Hit")); - b.add_input<decl::String>(N_("Hit Position")); - b.add_input<decl::String>(N_("Hit Normal")); - b.add_input<decl::String>(N_("Hit Distance")); - b.add_input<decl::String>(N_("Hit Attribute")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); - uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE); - uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__); - data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Ray Direction", - (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); - update_attribute_input_socket_availabilities( - *ntree, - *node, - "Ray Length", - (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); -} - -static void raycast_to_mesh(const Mesh &mesh, - const VArray<float3> &ray_origins, - const VArray<float3> &ray_directions, - const VArray<float> &ray_lengths, - const MutableSpan<bool> r_hit, - const MutableSpan<int> r_hit_indices, - const MutableSpan<float3> r_hit_positions, - const MutableSpan<float3> r_hit_normals, - const MutableSpan<float> r_hit_distances) -{ - BLI_assert(ray_origins.size() == ray_directions.size()); - BLI_assert(ray_origins.size() == ray_lengths.size()); - BLI_assert(ray_origins.size() == r_hit.size() || r_hit.is_empty()); - BLI_assert(ray_origins.size() == r_hit_indices.size() || r_hit_indices.is_empty()); - BLI_assert(ray_origins.size() == r_hit_positions.size() || r_hit_positions.is_empty()); - BLI_assert(ray_origins.size() == r_hit_normals.size() || r_hit_normals.is_empty()); - BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty()); - - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); - if (tree_data.tree == nullptr) { - free_bvhtree_from_mesh(&tree_data); - return; - } - - for (const int i : ray_origins.index_range()) { - const float ray_length = ray_lengths[i]; - const float3 ray_origin = ray_origins[i]; - const float3 ray_direction = math::normalize(ray_directions[i]); - - BVHTreeRayHit hit; - hit.index = -1; - hit.dist = ray_length; - if (BLI_bvhtree_ray_cast(tree_data.tree, - ray_origin, - ray_direction, - 0.0f, - &hit, - tree_data.raycast_callback, - &tree_data) != -1) { - if (!r_hit.is_empty()) { - r_hit[i] = hit.index >= 0; - } - if (!r_hit_indices.is_empty()) { - /* Index should always be a valid looptri index, use 0 when hit failed. */ - r_hit_indices[i] = max_ii(hit.index, 0); - } - if (!r_hit_positions.is_empty()) { - r_hit_positions[i] = hit.co; - } - if (!r_hit_normals.is_empty()) { - r_hit_normals[i] = hit.no; - } - if (!r_hit_distances.is_empty()) { - r_hit_distances[i] = hit.dist; - } - } - else { - if (!r_hit.is_empty()) { - r_hit[i] = false; - } - if (!r_hit_indices.is_empty()) { - r_hit_indices[i] = 0; - } - if (!r_hit_positions.is_empty()) { - r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); - } - if (!r_hit_normals.is_empty()) { - r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); - } - if (!r_hit_distances.is_empty()) { - r_hit_distances[i] = ray_length; - } - } - } - - free_bvhtree_from_mesh(&tree_data); -} - -static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( - GeometryNodeRaycastMapMode map_mode) -{ - switch (map_mode) { - case GEO_NODE_RAYCAST_INTERPOLATED: - return bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED; - default: - case GEO_NODE_RAYCAST_NEAREST: - return bke::mesh_surface_sample::eAttributeMapMode::NEAREST; - } -} - -static void raycast_from_points(const GeoNodeExecParams ¶ms, - const GeometrySet &target_geometry, - GeometryComponent &dst_component, - const StringRef hit_name, - const StringRef hit_position_name, - const StringRef hit_normal_name, - const StringRef hit_distance_name, - const Span<std::string> hit_attribute_names, - const Span<std::string> hit_attribute_output_names) -{ - BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); - - const MeshComponent *src_mesh_component = - target_geometry.get_component_for_read<MeshComponent>(); - if (src_mesh_component == nullptr) { - return; - } - const Mesh *src_mesh = src_mesh_component->get_for_read(); - if (src_mesh == nullptr) { - return; - } - if (src_mesh->totpoly == 0) { - return; - } - - const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; - bke::mesh_surface_sample::eAttributeMapMode map_mode = get_map_mode( - (GeometryNodeRaycastMapMode)storage.mapping); - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; - - VArray<float3> ray_origins = dst_component.attribute_get_for_read<float3>( - "position", result_domain, {0, 0, 0}); - VArray<float3> ray_directions = params.get_input_attribute<float3>( - "Ray Direction", dst_component, result_domain, {0, 0, 0}); - VArray<float> ray_lengths = params.get_input_attribute<float>( - "Ray Length", dst_component, result_domain, 0); - - OutputAttribute_Typed<bool> hit_attribute = - dst_component.attribute_try_get_for_output_only<bool>(hit_name, result_domain); - OutputAttribute_Typed<float3> hit_position_attribute = - dst_component.attribute_try_get_for_output_only<float3>(hit_position_name, result_domain); - OutputAttribute_Typed<float3> hit_normal_attribute = - dst_component.attribute_try_get_for_output_only<float3>(hit_normal_name, result_domain); - OutputAttribute_Typed<float> hit_distance_attribute = - dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain); - - /* Positions and looptri indices are always needed for interpolation, - * so create temporary arrays if no output attribute is given. */ - Array<int> hit_indices; - Array<float3> hit_positions_internal; - if (!hit_attribute_names.is_empty()) { - hit_indices.reinitialize(ray_origins.size()); - - if (!hit_position_attribute) { - hit_positions_internal.reinitialize(ray_origins.size()); - } - } - const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>(); - const MutableSpan<float3> hit_positions = hit_position_attribute ? - hit_position_attribute.as_span() : - hit_positions_internal; - const MutableSpan<float3> hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() : - MutableSpan<float3>(); - const MutableSpan<float> hit_distances = hit_distance_attribute ? - hit_distance_attribute.as_span() : - MutableSpan<float>(); - - raycast_to_mesh(*src_mesh, - ray_origins, - ray_directions, - ray_lengths, - is_hit, - hit_indices, - hit_positions, - hit_normals, - hit_distances); - - hit_attribute.save(); - hit_position_attribute.save(); - hit_normal_attribute.save(); - hit_distance_attribute.save(); - - /* Custom interpolated attributes */ - bke::mesh_surface_sample::MeshAttributeInterpolator interp( - src_mesh, IndexMask(ray_origins.size()), hit_positions, hit_indices); - for (const int i : hit_attribute_names.index_range()) { - const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data( - hit_attribute_names[i]); - if (meta_data) { - ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( - hit_attribute_names[i]); - OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( - hit_attribute_output_names[i], result_domain, meta_data->data_type); - - interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); - - hit_attribute_output.save(); - } - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); - - const std::string hit_name = params.extract_input<std::string>("Is Hit"); - const std::string hit_position_name = params.extract_input<std::string>("Hit Position"); - const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal"); - const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance"); - - const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")}; - const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")}; - - geometry_set = geometry::realize_instances_legacy(geometry_set); - target_geometry_set = geometry::realize_instances_legacy(target_geometry_set); - - static const Array<GeometryComponentType> types = { - GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; - for (const GeometryComponentType type : types) { - if (geometry_set.has(type)) { - raycast_from_points(params, - target_geometry_set, - geometry_set.get_component_for_write(type), - hit_name, - hit_position_name, - hit_normal_name, - hit_distance_name, - hit_names, - hit_output_names); - } - } - - params.set_output("Geometry", geometry_set); -} - -} // namespace blender::nodes::node_geo_legacy_raycast_cc - -void register_node_type_geo_legacy_raycast() -{ - namespace file_ns = blender::nodes::node_geo_legacy_raycast_cc; - - static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage( - &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc deleted file mode 100644 index 150fd56abe3..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "node_geometry_util.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_task.hh" - -#include "BKE_material.h" - -namespace blender::nodes::node_geo_legacy_select_by_material_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Material>(N_("Material")).hide_label(); - b.add_input<decl::String>(N_("Selection")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void select_mesh_by_material(const Mesh &mesh, - const Material *material, - const MutableSpan<bool> r_selection) -{ - BLI_assert(mesh.totpoly == r_selection.size()); - Vector<int> material_indices; - for (const int i : IndexRange(mesh.totcol)) { - if (mesh.mat[i] == material) { - material_indices.append(i); - } - } - threading::parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr); - } - }); -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - Material *material = params.extract_input<Material *>("Material"); - const std::string selection_name = params.extract_input<std::string>("Selection"); - - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (geometry_set.has<MeshComponent>()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh != nullptr) { - OutputAttribute_Typed<bool> selection = - mesh_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_FACE); - if (selection) { - select_mesh_by_material(*mesh, material, selection.as_span()); - selection.save(); - } - } - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_select_by_material_cc - -void register_node_type_geo_legacy_select_by_material() -{ - namespace file_ns = blender::nodes::node_geo_legacy_select_by_material_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc deleted file mode 100644 index 9eef4b84c36..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc +++ /dev/null @@ -1,132 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BKE_mesh.h" -#include "BKE_subdiv.h" -#include "BKE_subdiv_mesh.h" - -#include "DNA_modifier_types.h" -#include "UI_interface.h" -#include "UI_resources.h" -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_legacy_subdivision_surface_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); - b.add_input<decl::Bool>(N_("Use Creases")); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ -#ifdef WITH_OPENSUBDIV - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); - uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); -#else - UNUSED_VARS(layout, ptr); -#endif -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__); - data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; - data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; - node->storage = data; -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set = geometry::realize_instances_legacy(geometry_set); - - if (!geometry_set.has_mesh()) { - params.set_output("Geometry", geometry_set); - return; - } - -#ifndef WITH_OPENSUBDIV - params.error_message_add(NodeWarningType::Error, - TIP_("Disabled, Blender was compiled without OpenSubdiv")); -#else - const NodeGeometrySubdivisionSurface &storage = - *(const NodeGeometrySubdivisionSurface *)params.node().storage; - const int uv_smooth = storage.uv_smooth; - const int boundary_smooth = storage.boundary_smooth; - const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30); - - /* Only process subdivision if level is greater than 0. */ - if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } - - const bool use_crease = params.extract_input<bool>("Use Creases"); - const Mesh *mesh_in = geometry_set.get_mesh_for_read(); - - /* Initialize mesh settings. */ - SubdivToMeshSettings mesh_settings; - mesh_settings.resolution = (1 << subdiv_level) + 1; - mesh_settings.use_optimal_display = false; - - /* Initialize subdivision settings. */ - SubdivSettings subdiv_settings; - subdiv_settings.is_simple = false; - subdiv_settings.is_adaptive = false; - subdiv_settings.use_creases = use_crease; - subdiv_settings.level = subdiv_level; - - subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( - boundary_smooth); - subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( - uv_smooth); - - /* Apply subdivision to mesh. */ - Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); - - /* In case of bad topology, skip to input mesh. */ - if (subdiv == nullptr) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } - - Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_normals_tag_dirty(mesh_out); - - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace(mesh_out); - - // BKE_subdiv_stats_print(&subdiv->stats); - BKE_subdiv_free(subdiv); - -#endif - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes::node_geo_legacy_subdivision_surface_cc - -void register_node_type_geo_legacy_subdivision_surface() -{ - namespace file_ns = blender::nodes::node_geo_legacy_subdivision_surface_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - node_type_init(&ntype, file_ns::node_init); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_storage(&ntype, - "NodeGeometrySubdivisionSurface", - node_free_standard_storage, - node_copy_standard_storage); - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc deleted file mode 100644 index 768932a7fe7..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc +++ /dev/null @@ -1,162 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "DEG_depsgraph_query.h" -#ifdef WITH_OPENVDB -# include <openvdb/tools/GridTransformer.h> -# include <openvdb/tools/VolumeToMesh.h> -#endif - -#include "node_geometry_util.hh" - -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_volume.h" -#include "BKE_volume_to_mesh.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -namespace blender::nodes::node_geo_legacy_volume_to_mesh_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Geometry")); - b.add_input<decl::String>(N_("Density")); - b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); - b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f); - b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Geometry>(N_("Geometry")); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); -} - -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__); - data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; - - bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); - bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; - STRNCPY(grid_socket_value->value, "density"); - - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; - - bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); - bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); - nodeSetSocketAvailability(ntree, - voxel_amount_socket, - data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); - nodeSetSocketAvailability(ntree, - voxel_size_socket, - data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); -} - -#ifdef WITH_OPENVDB - -static void create_mesh_from_volume(GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - GeoNodeExecParams ¶ms) -{ - if (!geometry_set_in.has<VolumeComponent>()) { - return; - } - - const NodeGeometryVolumeToMesh &storage = - *(const NodeGeometryVolumeToMesh *)params.node().storage; - - bke::VolumeToMeshResolution resolution; - resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; - if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { - resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount"); - if (resolution.settings.voxel_amount <= 0.0f) { - return; - } - } - else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { - resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); - if (resolution.settings.voxel_size <= 0.0f) { - return; - } - } - - const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); - const Volume *volume = component->get_for_read(); - if (volume == nullptr) { - return; - } - - const Main *bmain = DEG_get_bmain(params.depsgraph()); - BKE_volume_load(volume, bmain); - - const std::string grid_name = params.get_input<std::string>("Density"); - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); - if (volume_grid == nullptr) { - return; - } - - float threshold = params.get_input<float>("Threshold"); - float adaptivity = params.get_input<float>("Adaptivity"); - - const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); - if (mesh == nullptr) { - return; - } - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); - dst_component.replace(mesh); -} - -#endif /* WITH_OPENVDB */ - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_out; - -#ifdef WITH_OPENVDB - create_mesh_from_volume(geometry_set_in, geometry_set_out, params); -#else - params.error_message_add(NodeWarningType::Error, - TIP_("Disabled, Blender was compiled without OpenVDB")); -#endif - - params.set_output("Geometry", geometry_set_out); -} - -} // namespace blender::nodes::node_geo_legacy_volume_to_mesh_cc - -void register_node_type_geo_legacy_volume_to_mesh() -{ - namespace file_ns = blender::nodes::node_geo_legacy_volume_to_mesh_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY); - ntype.declare = file_ns::node_declare; - node_type_storage( - &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); - node_type_size(&ntype, 170, 120, 700); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 412f35d62fd..6794671f707 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -4,9 +4,9 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BKE_curves.hh" #include "BKE_material.h" #include "BKE_mesh.h" -#include "BKE_spline.hh" #include "node_geometry_util.hh" @@ -162,14 +162,12 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) } if (geometry_set.has_curves()) { - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - for (const SplinePtr &spline : curve->splines()) { - positions_span = spline->evaluated_positions(); - total_size += positions_span.size(); - count++; - span_count++; - } + count++; + span_count++; + const Curves &curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + positions_span = curves.evaluated_positions(); + total_size += positions_span.size(); } if (count == 0) { @@ -203,13 +201,11 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) } if (geometry_set.has_curves()) { - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - for (const SplinePtr &spline : curve->splines()) { - Span<float3> array = spline->evaluated_positions(); - positions.as_mutable_span().slice(offset, array.size()).copy_from(array); - offset += array.size(); - } + const Curves &curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + Span<float3> array = curves.evaluated_positions(); + positions.as_mutable_span().slice(offset, array.size()).copy_from(array); + offset += array.size(); } return hull_from_bullet(geometry_set.get_mesh_for_read(), positions); @@ -240,20 +236,16 @@ static void read_positions(const GeometryComponent &component, } } -static void read_curve_positions(const CurveEval &curve, +static void read_curve_positions(const Curves &curves_id, Span<float4x4> transforms, Vector<float3> *r_coords) { - const Array<int> offsets = curve.evaluated_point_offsets(); - const int total_size = offsets.last(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + const int total_size = curves.evaluated_points_num(); r_coords->reserve(r_coords->size() + total_size * transforms.size()); - for (const SplinePtr &spline : curve.splines()) { - Span<float3> positions = spline->evaluated_positions(); - for (const float4x4 &transform : transforms) { - for (const float3 &position : positions) { - r_coords->append(transform * position); - } - } + r_coords->as_mutable_span().take_back(total_size).copy_from(curves.evaluated_positions()); + for (const float3 &position : curves.evaluated_positions()) { + r_coords->append(transform * postition); } } @@ -275,7 +267,7 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set) read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords); } if (set.has_curves()) { - read_curve_positions(*curves_to_curve_eval(*set.get_curves_for_read()), transforms, &coords); + read_curve_positions(*set.get_curves_for_read(), transforms, &coords); } } return hull_from_bullet(nullptr, coords); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index 2c72e5f14f5..bbc8758952d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -55,24 +55,24 @@ class EndpointFieldInput final : public GeometryFieldInput { const Curves &curves_id = *curve_component.get_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - if (curves.points_size() == 0) { + if (curves.points_num() == 0) { return nullptr; } GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{size_context, curves.curves_size()}; + fn::FieldEvaluator evaluator{size_context, curves.curves_num()}; evaluator.add(start_size_); evaluator.add(end_size_); evaluator.evaluate(); const VArray<int> &start_size = evaluator.get_evaluated<int>(0); const VArray<int> &end_size = evaluator.get_evaluated<int>(1); - Array<bool> selection(curves.points_size(), false); + Array<bool> selection(curves.points_num(), false); MutableSpan<bool> selection_span = selection.as_mutable_span(); devirtualize_varray2(start_size, end_size, [&](const auto &start_size, const auto &end_size) { threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange curves_range) { for (const int i : curves_range) { - const IndexRange range = curves.range_for_curve(i); + const IndexRange range = curves.points_for_curve(i); const int start = std::max(start_size[i], 0); const int end = std::max(end_size[i], 0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 08aa7415073..f29b193d98b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -7,8 +7,8 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_curves.hh" #include "BKE_mesh.h" -#include "BKE_spline.hh" #include "BLI_task.hh" @@ -40,43 +40,42 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } -static blender::meshintersect::CDT_result<double> do_cdt(const CurveEval &curve, - const CDT_output_type output_type) +static meshintersect::CDT_result<double> do_cdt(const bke::CurvesGeometry &curves, + const CDT_output_type output_type) { - Span<SplinePtr> splines = curve.splines(); - blender::meshintersect::CDT_input<double> input; + meshintersect::CDT_input<double> input; input.need_ids = false; - Array<int> offsets = curve.evaluated_point_offsets(); - input.vert.reinitialize(offsets.last()); - input.face.reinitialize(splines.size()); + input.vert.reinitialize(curves.evaluated_points_num()); + input.face.reinitialize(curves.curves_num()); - for (const int i_spline : splines.index_range()) { - const SplinePtr &spline = splines[i_spline]; - const int vert_offset = offsets[i_spline]; + VArray<bool> cyclic = curves.cyclic(); + Span<float3> positions = curves.evaluated_positions(); - Span<float3> positions = spline->evaluated_positions(); - for (const int i : positions.index_range()) { - input.vert[vert_offset + i] = double2(positions[i].x, positions[i].y); + for (const int i_curve : curves.curves_range()) { + const IndexRange points = curves.evaluated_points_for_curve(i_curve); + const int segment_size = bke::curves::curve_segment_size(points.size(), cyclic[i_curve]); + + for (const int i : points) { + input.vert[i] = double2(positions[i].x, positions[i].y); } - input.face[i_spline].resize(spline->evaluated_edges_size()); - MutableSpan<int> face_verts = input.face[i_spline]; - for (const int i : IndexRange(spline->evaluated_edges_size())) { - face_verts[i] = vert_offset + i; + input.face[i_curve].resize(segment_size); + MutableSpan<int> face_verts = input.face[i_curve]; + for (const int i : IndexRange(segment_size)) { + face_verts[i] = points[i]; } } - blender::meshintersect::CDT_result<double> result = delaunay_2d_calc(input, output_type); + meshintersect::CDT_result<double> result = delaunay_2d_calc(input, output_type); return result; } /* Converts the CDT result into a Mesh. */ -static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &result) +static Mesh *cdt_to_mesh(const meshintersect::CDT_result<double> &result) { - int vert_len = result.vert.size(); - int edge_len = result.edge.size(); - int poly_len = result.face.size(); + const int vert_len = result.vert.size(); + const int edge_len = result.edge.size(); + const int poly_len = result.face.size(); int loop_len = 0; - for (const Vector<int> &face : result.face) { loop_len += face.size(); } @@ -117,9 +116,9 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu return; } - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - if (curve->splines().is_empty()) { + const Curves &curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + if (curves.curves_num() == 0) { geometry_set.replace_curves(nullptr); return; } @@ -128,7 +127,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : CDT_INSIDE_WITH_HOLES; - const blender::meshintersect::CDT_result<double> results = do_cdt(*curve, output_type); + const meshintersect::CDT_result<double> results = do_cdt(curves, output_type); Mesh *mesh = cdt_to_mesh(results); geometry_set.replace_mesh(mesh); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 0aa603a7736..dc2b9d40894 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -47,24 +47,24 @@ static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType return BEZIER_HANDLE_AUTO; } -static void select_by_handle_type(const CurveEval &curve, +static void select_by_handle_type(const bke::CurvesGeometry &curves, const HandleType type, const GeometryNodeCurveHandleMode mode, const MutableSpan<bool> r_selection) { - int offset = 0; - for (const SplinePtr &spline : curve.splines()) { - if (spline->type() != CURVE_TYPE_BEZIER) { - r_selection.slice(offset, spline->size()).fill(false); - offset += spline->size(); + VArray<int8_t> curve_types = curves.curve_types(); + VArray<int8_t> left = curves.handle_types_left(); + VArray<int8_t> right = curves.handle_types_right(); + + for (const int i_curve : curves.curves_range()) { + const IndexRange points = curves.points_for_curve(i_curve); + if (curve_types[i_curve] != CURVE_TYPE_BEZIER) { + r_selection.slice(points).fill(false); } else { - BezierSpline *b = static_cast<BezierSpline *>(spline.get()); - for (int i : IndexRange(b->size())) { - r_selection[offset++] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && - b->handle_types_left()[i] == type) || - (mode & GEO_NODE_CURVE_HANDLE_RIGHT && - b->handle_types_right()[i] == type); + for (const int i_point : points) { + r_selection[i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && left[i_point] == type) || + (mode & GEO_NODE_CURVE_HANDLE_RIGHT && right[i_point] == type); } } } @@ -87,22 +87,19 @@ class HandleTypeFieldInput final : public GeometryFieldInput { const AttributeDomain domain, IndexMask mask) const final { - if (component.type() != GEO_COMPONENT_TYPE_CURVE) { + if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { return {}; } const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const Curves *curve = curve_component.get_for_read(); - if (curve == nullptr) { + const Curves *curves_id = curve_component.get_for_read(); + if (curves_id == nullptr) { return {}; } - if (domain == ATTR_DOMAIN_POINT) { - Array<bool> selection(mask.min_array_size()); - select_by_handle_type(*curves_to_curve_eval(*curve), type_, mode_, selection); - return VArray<bool>::ForContainer(std::move(selection)); - } - return {}; + Array<bool> selection(mask.min_array_size()); + select_by_handle_type(bke::CurvesGeometry::wrap(curves_id->geometry), type_, mode_, selection); + return VArray<bool>::ForContainer(std::move(selection)); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc index 6c7d7ed375b..38d81c54933 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc @@ -17,6 +17,13 @@ NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveArc) static void node_declare(NodeDeclarationBuilder &b) { + auto enable_points = [](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; + }; + auto enable_radius = [](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS; + }; + b.add_input<decl::Int>(N_("Resolution")) .default_value(16) .min(2) @@ -26,34 +33,41 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Vector>(N_("Start")) .default_value({-1.0f, 0.0f, 0.0f}) .subtype(PROP_TRANSLATION) - .description(N_("Position of the first control point")); + .description(N_("Position of the first control point")) + .make_available(enable_points); b.add_input<decl::Vector>(N_("Middle")) .default_value({0.0f, 2.0f, 0.0f}) .subtype(PROP_TRANSLATION) - .description(N_("Position of the middle control point")); + .description(N_("Position of the middle control point")) + .make_available(enable_points); b.add_input<decl::Vector>(N_("End")) .default_value({1.0f, 0.0f, 0.0f}) .subtype(PROP_TRANSLATION) - .description(N_("Position of the last control point")); + .description(N_("Position of the last control point")) + .make_available(enable_points); b.add_input<decl::Float>(N_("Radius")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description(N_("Distance of the points from the origin")); + .description(N_("Distance of the points from the origin")) + .make_available(enable_radius); b.add_input<decl::Float>(N_("Start Angle")) .default_value(0.0f) .subtype(PROP_ANGLE) - .description(N_("Starting angle of the arc")); + .description(N_("Starting angle of the arc")) + .make_available(enable_radius); b.add_input<decl::Float>(N_("Sweep Angle")) .default_value(1.75f * M_PI) .min(-2 * M_PI) .max(2 * M_PI) .subtype(PROP_ANGLE) - .description(N_("Length of the arc")); + .description(N_("Length of the arc")) + .make_available(enable_radius); b.add_input<decl::Float>(N_("Offset Angle")) .default_value(0.0f) .subtype(PROP_ANGLE) - .description(N_("Offset angle of the arc")); + .description(N_("Offset angle of the arc")) + .make_available(enable_points); b.add_input<decl::Bool>(N_("Connect Center")) .default_value(false) .description(N_("Connect the arc at the center")); @@ -64,17 +78,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); b.add_output<decl::Vector>(N_("Center")) .description(N_("The center of the circle described by the three points")) - .make_available( - [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; }); + .make_available(enable_points); b.add_output<decl::Vector>(N_("Normal")) .description(N_("The normal direction of the plane described by the three points, pointing " "towards the positive Z axis")) - .make_available( - [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; }); + .make_available(enable_points); b.add_output<decl::Float>(N_("Radius")) .description(N_("The radius of the circle described by the three points")) - .make_available( - [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; }); + .make_available(enable_points); } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index 78e1613b630..297674e11f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -55,30 +55,26 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static std::unique_ptr<CurveEval> create_bezier_segment_curve( - const float3 start, - const float3 start_handle_right, - const float3 end, - const float3 end_handle_left, - const int resolution, - const GeometryNodeCurvePrimitiveBezierSegmentMode mode) +static Curves *create_bezier_segment_curve(const float3 start, + const float3 start_handle_right, + const float3 end, + const float3 end_handle_left, + const int resolution, + const GeometryNodeCurvePrimitiveBezierSegmentMode mode) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); - spline->set_resolution(resolution); + Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_BEZIER); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.resolution().fill(resolution); - spline->resize(2); - MutableSpan<float3> positions = spline->positions(); - spline->handle_types_left().fill(BEZIER_HANDLE_ALIGN); - spline->handle_types_right().fill(BEZIER_HANDLE_ALIGN); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); + curves.handle_types_left().fill(BEZIER_HANDLE_ALIGN); + curves.handle_types_right().fill(BEZIER_HANDLE_ALIGN); positions.first() = start; positions.last() = end; - MutableSpan<float3> handles_right = spline->handle_positions_right(); - MutableSpan<float3> handles_left = spline->handle_positions_left(); + MutableSpan<float3> handles_right = curves.handle_positions_right(); + MutableSpan<float3> handles_left = curves.handle_positions_left(); if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) { handles_left.first() = 2.0f * start - start_handle_right; @@ -95,9 +91,7 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve( handles_right.last() = end - end_handle_left; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(1); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -106,14 +100,14 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurvePrimitiveBezierSegmentMode mode = (const GeometryNodeCurvePrimitiveBezierSegmentMode)storage.mode; - std::unique_ptr<CurveEval> curve = create_bezier_segment_curve( + Curves *curves = create_bezier_segment_curve( params.extract_input<float3>("Start"), params.extract_input<float3>("Start Handle"), params.extract_input<float3>("End"), params.extract_input<float3>("End Handle"), std::max(params.extract_input<int>("Resolution"), 1), mode); - params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 2fb9f724130..aa4b3a785f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -13,6 +13,13 @@ NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveCircle) static void node_declare(NodeDeclarationBuilder &b) { + auto endable_points = [](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS; + }; + auto enable_radius = [](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS; + }; + b.add_input<decl::Int>(N_("Resolution")) .default_value(32) .min(3) @@ -23,28 +30,30 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_TRANSLATION) .description( N_("One of the three points on the circle. The point order determines the circle's " - "direction")); + "direction")) + .make_available(endable_points); b.add_input<decl::Vector>(N_("Point 2")) .default_value({0.0f, 1.0f, 0.0f}) .subtype(PROP_TRANSLATION) .description( N_("One of the three points on the circle. The point order determines the circle's " - "direction")); + "direction")) + .make_available(endable_points); b.add_input<decl::Vector>(N_("Point 3")) .default_value({1.0f, 0.0f, 0.0f}) .subtype(PROP_TRANSLATION) .description( N_("One of the three points on the circle. The point order determines the circle's " - "direction")); + "direction")) + .make_available(endable_points); b.add_input<decl::Float>(N_("Radius")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description(N_("Distance of the points from the origin")); + .description(N_("Distance of the points from the origin")) + .make_available(enable_radius); b.add_output<decl::Geometry>(N_("Curve")); - b.add_output<decl::Vector>(N_("Center")).make_available([](bNode &node) { - node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS; - }); + b.add_output<decl::Vector>(N_("Center")).make_available(endable_points); } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index 2e2f4254752..62dbcc91cc6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -13,21 +13,29 @@ NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveLine) static void node_declare(NodeDeclarationBuilder &b) { + auto enable_direction = [](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION; + }; + b.add_input<decl::Vector>(N_("Start")) .subtype(PROP_TRANSLATION) .description(N_("Position of the first control point")); b.add_input<decl::Vector>(N_("End")) .default_value({0.0f, 0.0f, 1.0f}) .subtype(PROP_TRANSLATION) - .description(N_("Position of the second control point")); + .description(N_("Position of the second control point")) + .make_available([](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS; + }); b.add_input<decl::Vector>(N_("Direction")) .default_value({0.0f, 0.0f, 1.0f}) - .description( - N_("Direction the line is going in. The length of this vector does not matter")); + .description(N_("Direction the line is going in. The length of this vector does not matter")) + .make_available(enable_direction); b.add_input<decl::Float>(N_("Length")) .default_value(1.0f) .subtype(PROP_DISTANCE) - .description(N_("Distance between the two points")); + .description(N_("Distance between the two points")) + .make_available(enable_direction); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 8393f9615aa..de29735bd2d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -2,7 +2,7 @@ #include "BLI_task.hh" -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params) } Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); - CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); + const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); @@ -33,16 +33,13 @@ static void node_geo_exec(GeoNodeExecParams params) selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + if (selection.is_empty()) { + return; + } - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_write()); - MutableSpan<SplinePtr> splines = curve->splines(); - threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - splines[selection[i]]->reverse(); - } - }); - - component.replace(curve_eval_to_curves(*curve)); + Curves &curves_id = *geometry_set.get_curves_for_write(); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + curves.reverse_curves(selection); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 1eb18b2f910..894580f2932 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -12,20 +12,6 @@ #include "node_geometry_util.hh" -namespace blender::nodes { -void curve_create_default_rotation_attribute(Span<float3> tangents, - Span<float3> normals, - MutableSpan<float3> rotations) -{ - threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { - for (const int i : range) { - rotations[i] = - float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler(); - } - }); -} -} // namespace blender::nodes - namespace blender::nodes::node_geo_curve_to_points_cc { NODE_STORAGE_FUNCS(NodeGeometryCurveToPoints) @@ -76,6 +62,18 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } +static void curve_create_default_rotation_attribute(Span<float3> tangents, + Span<float3> normals, + MutableSpan<float3> rotations) +{ + threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { + for (const int i : range) { + rotations[i] = + float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler(); + } + }); +} + static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, const GeometryNodeCurveResampleMode mode, const CurveEval &curve, diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index bdd4d74fe4b..d9f29d1ef1c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -26,17 +26,35 @@ namespace blender::nodes::node_geo_distribute_points_on_faces_cc { static void node_declare(NodeDeclarationBuilder &b) { + auto enable_random = [](bNode &node) { + node.custom1 = GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM; + }; + auto enable_poisson = [](bNode &node) { + node.custom1 = GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON; + }; + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); - b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Density Max")).default_value(10.0f).min(0.0f); - b.add_input<decl::Float>(N_("Density")).default_value(10.0f).min(0.0f).supports_field(); + b.add_input<decl::Float>(N_("Distance Min")) + .min(0.0f) + .subtype(PROP_DISTANCE) + .make_available(enable_poisson); + b.add_input<decl::Float>(N_("Density Max")) + .default_value(10.0f) + .min(0.0f) + .make_available(enable_poisson); + b.add_input<decl::Float>(N_("Density")) + .default_value(10.0f) + .min(0.0f) + .supports_field() + .make_available(enable_random); b.add_input<decl::Float>(N_("Density Factor")) .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR) - .supports_field(); + .supports_field() + .make_available(enable_poisson); b.add_input<decl::Int>(N_("Seed")); b.add_output<decl::Geometry>(N_("Points")); @@ -114,10 +132,8 @@ static void sample_mesh_surface(const Mesh &mesh, const int looptri_seed = noise::hash(looptri_index, seed); RandomNumberGenerator looptri_rng(looptri_seed); - const float points_amount_fl = area * base_density * looptri_density_factor; - const float add_point_probability = fractf(points_amount_fl); - const bool add_point = add_point_probability > looptri_rng.get_float(); - const int point_amount = (int)points_amount_fl + (int)add_point; + const int point_amount = looptri_rng.round_probabilistic(area * base_density * + looptri_density_factor); for (int i = 0; i < point_amount; i++) { const float3 bary_coord = looptri_rng.get_barycentric_coordinates(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 8beb6dcb5ce..2aa768129cd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -10,9 +10,9 @@ #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" +#include "BKE_curves.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" -#include "BKE_spline.hh" #include "node_geometry_util.hh" @@ -34,8 +34,7 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The number of duplicates to create for each element")); b.add_output<decl::Geometry>(N_("Geometry")) - .description( - N_("The duplicated geometry only. The output does not contain the original geometry")); + .description(N_("The duplicated geometry, not including the original geometry")); b.add_output<decl::Int>(N_("Duplicate Index")) .field_source() .description(N_("The indices of the duplicates for each element")); @@ -58,21 +57,19 @@ struct IndexAttributes { }; /* -------------------------------------------------------------------- */ -/** \name Attribute Copy/Creation Functions +/** \name Utility Functions * \{ */ -static void gather_attributes_without_id(const GeometrySet &geometry_set, - const GeometryComponentType component_type, - const Span<std::string> skip_attributes, - const bool include_instances, - Map<AttributeIDRef, AttributeKind> &r_gathered_attributes) +static Map<AttributeIDRef, AttributeKind> gather_attributes_without_id( + const GeometrySet &geometry_set, + const GeometryComponentType component_type, + const bool include_instances) { + Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( - {component_type}, component_type, include_instances, r_gathered_attributes); - for (const std::string &attribute : skip_attributes) { - r_gathered_attributes.remove(attribute); - } - r_gathered_attributes.remove("id"); + {component_type}, component_type, include_instances, attributes); + attributes.remove("id"); + return attributes; }; static IndexRange range_for_offsets_index(const Span<int> offsets, const int index) @@ -84,12 +81,12 @@ static Array<int> accumulate_counts_to_offsets(const IndexMask selection, const VArray<int> &counts) { Array<int> offsets(selection.size() + 1); - int dst_points_size = 0; - for (const int i_point : selection.index_range()) { - offsets[i_point] = dst_points_size; - dst_points_size += std::max(counts[selection[i_point]], 0); + int total = 0; + for (const int i : selection.index_range()) { + offsets[i] = total; + total += std::max(counts[selection[i]], 0); } - offsets.last() = dst_points_size; + offsets.last() = total; return offsets; } @@ -100,7 +97,7 @@ static void threaded_slice_fill(Span<int> offsets, Span<T> src, MutableSpan<T> d BLI_assert(offsets.last() == dst.size()); threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) { for (const int i : range) { - dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + dst.slice(range_for_offsets_index(offsets, i)).fill(src[i]); } }); } @@ -115,6 +112,13 @@ static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, Mut }); } +static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst) +{ + for (const int i : src.index_range()) { + dst[i] = noise::hash(src[i], hash); + } +} + static void threaded_id_offset_copy(const Span<int> offsets, const Span<int> src, MutableSpan<int> dst) @@ -135,11 +139,11 @@ static void threaded_id_offset_copy(const Span<int> offsets, static void create_duplicate_index_attribute(GeometryComponent &component, const AttributeDomain output_domain, const IndexMask selection, - const IndexAttributes &attributes, + const IndexAttributes &attribute_outputs, const Span<int> offsets) { OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>( - attributes.duplicate_index.get(), output_domain); + attribute_outputs.duplicate_index.get(), output_domain); MutableSpan<int> duplicate_indices = copy_attribute.as_span(); for (const int i : IndexRange(selection.size())) { const IndexRange range = range_for_offsets_index(offsets, i); @@ -175,156 +179,6 @@ static void copy_stable_id_point(const Span<int> offsets, dst_attribute.save(); } -/** - * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id - * and the duplicate number. This function is used for points when duplicating the edge domain. - */ -static void copy_stable_id_edges(const Mesh &mesh, - const IndexMask selection, - const Span<int> edge_offsets, - const GeometryComponent &src_component, - GeometryComponent &dst_component) -{ - ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); - if (!src_attribute) { - return; - } - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); - if (!dst_attribute) { - return; - } - - Span<MEdge> edges(mesh.medge, mesh.totedge); - - VArray_Span<int> src{src_attribute.varray.typed<int>()}; - MutableSpan<int> dst = dst_attribute.as_span<int>(); - threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) { - for (const int i_edge : range) { - const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); - if (edge_range.size() == 0) { - continue; - } - const MEdge &edge = edges[i_edge]; - const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2}; - - dst[vert_range[0]] = src[edge.v1]; - dst[vert_range[1]] = src[edge.v2]; - for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) { - dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge.v1], i_duplicate); - dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge.v2], i_duplicate); - } - } - }); - dst_attribute.save(); -} - -/** - * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id - * and the duplicate number. This function is used for points when duplicating the face domain. - * - * This function could be threaded in the future, but since it is only 1 attribute and the - * `face->edge->vert` mapping would mean creating a 1/1 mapping to allow for it, is it worth it? - */ -static void copy_stable_id_faces(const Mesh &mesh, - const IndexMask selection, - const Span<int> poly_offsets, - const Span<int> vert_mapping, - const GeometryComponent &src_component, - GeometryComponent &dst_component) -{ - ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); - if (!src_attribute) { - return; - } - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); - if (!dst_attribute) { - return; - } - - VArray_Span<int> src{src_attribute.varray.typed<int>()}; - MutableSpan<int> dst = dst_attribute.as_span<int>(); - - Span<MPoly> polys(mesh.mpoly, mesh.totpoly); - int loop_index = 0; - for (const int i_poly : selection.index_range()) { - const IndexRange range = range_for_offsets_index(poly_offsets, i_poly); - if (range.size() == 0) { - continue; - } - const MPoly &source = polys[i_poly]; - for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) { - for ([[maybe_unused]] const int i_loops : IndexRange(source.totloop)) { - if (i_duplicate == 0) { - dst[loop_index] = src[vert_mapping[loop_index]]; - } - else { - dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate); - } - loop_index++; - } - } - } - - dst_attribute.save(); -} - -/** - * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id - * and the duplicate number. In the spline case, copy the entire spline's points to the - * destination, - * then loop over the remaining ones point by point, hashing their ids to the new ids. - */ -static void copy_stable_id_splines(const CurveEval &curve, - const IndexMask selection, - const Span<int> curve_offsets, - const GeometryComponent &src_component, - GeometryComponent &dst_component) -{ - ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); - if (!src_attribute) { - return; - } - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); - if (!dst_attribute) { - return; - } - - Array<int> control_point_offsets = curve.control_point_offsets(); - VArray_Span<int> src{src_attribute.varray.typed<int>()}; - MutableSpan<int> dst = dst_attribute.as_span<int>(); - - Array<int> curve_point_offsets(selection.size() + 1); - int dst_point_size = 0; - for (const int i_curve : selection.index_range()) { - const int spline_size = curve.splines()[i_curve]->size(); - const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); - - curve_point_offsets[i_curve] = dst_point_size; - dst_point_size += curve_range.size() * spline_size; - } - curve_point_offsets.last() = dst_point_size; - - threading::parallel_for(IndexRange(curve_point_offsets.size() - 1), 512, [&](IndexRange range) { - for (const int i_curve : range) { - const int spline_size = curve.splines()[i_curve]->size(); - const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); - - dst.slice(curve_point_offsets[i_curve], spline_size) - .copy_from(src.slice(control_point_offsets[i_curve], spline_size)); - for (const int i_duplicate : IndexRange(1, curve_range.size() - 1)) { - for (const int i_point : IndexRange(spline_size)) { - dst[curve_point_offsets[i_curve] + i_duplicate * spline_size + i_point] = noise::hash( - src[control_point_offsets[i_curve] + i_point], i_duplicate); - } - } - } - }); - dst_attribute.save(); -} - /* The attributes for the point (also instance) duplicated elements are stored sequentially * (1,1,1,2,2,2,3,3,3,etc) They can be copied by using a simple offset array. For each domain, if * elements are ordered differently a custom function is called to copy the attributes. @@ -337,11 +191,10 @@ static void copy_point_attributes_without_id(GeometrySet &geometry_set, const GeometryComponent &src_component, GeometryComponent &dst_component) { - Map<AttributeIDRef, AttributeKind> gathered_attributes; - gather_attributes_without_id( - geometry_set, component_type, {}, include_instances, gathered_attributes); + Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id( + geometry_set, component_type, include_instances); - for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); if (!src_attribute || src_attribute.domain != ATTR_DOMAIN_POINT) { @@ -365,23 +218,28 @@ static void copy_point_attributes_without_id(GeometrySet &geometry_set, } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Curves + * \{ */ + /** - * Copies the attributes for spline duplicates. If copying the spline domain, the attributes are + * Copies the attributes for curve duplicates. If copying the curve domain, the attributes are * copied with an offset fill, otherwise a mapping is used. */ -static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, - const Span<int> point_mapping, - const Span<int> offsets, - const Span<std::string> attributes_to_ignore, - const GeometryComponent &src_component, - GeometryComponent &dst_component) +static void copy_curve_attributes_without_id(const GeometrySet &geometry_set, + const CurveComponent &src_component, + const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const Span<int> curve_offsets, + bke::CurvesGeometry &dst_curves, + CurveComponent &dst_component) { - Map<AttributeIDRef, AttributeKind> gathered_attributes; - gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_CURVE, attributes_to_ignore, false, gathered_attributes); - - for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, false); + for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); if (!src_attribute) { @@ -404,10 +262,18 @@ static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, switch (out_domain) { case ATTR_DOMAIN_CURVE: - threaded_slice_fill<T>(offsets, src, dst); + threaded_slice_fill<T>(curve_offsets, src, dst); break; case ATTR_DOMAIN_POINT: - threaded_mapped_copy<T>(point_mapping, src, dst); + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i_src_curve = selection[i_selection]; + const Span<T> curve_src = src.slice(src_curves.points_for_curve(i_src_curve)); + for (const int i_dst_curve : range_for_offsets_index(curve_offsets, i_selection)) { + dst.slice(dst_curves.points_for_curve(i_dst_curve)).copy_from(curve_src); + } + } + }); break; default: break; @@ -418,54 +284,126 @@ static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, } /** - * Copies the attributes for edge duplicates. If copying the edge domain, the attributes are - * copied with an offset fill, for point domain a mapping is used. + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. In the curve case, copy the entire curve's points to the + * destination, + * then loop over the remaining ones point by point, hashing their ids to the new ids. */ -static void copy_edge_attributes_without_id(GeometrySet &geometry_set, - const Span<int> point_mapping, - const Span<int> offsets, - const GeometryComponent &src_component, - GeometryComponent &dst_component) +static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const Span<int> curve_offsets, + const CurveComponent &src_component, + bke::CurvesGeometry &dst_curves, + CurveComponent &dst_component) { - Map<AttributeIDRef, AttributeKind> gathered_attributes; - gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } - for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { - const AttributeIDRef attribute_id = entry.key; - ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); - if (!src_attribute) { - continue; - } + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); - const AttributeDomain out_domain = src_attribute.domain; - const CustomDataType data_type = bke::cpp_type_to_custom_data_type( - src_attribute.varray.type()); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - attribute_id, out_domain, data_type); - if (!dst_attribute) { - continue; + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i_src_curve = selection[i_selection]; + const Span<int> curve_src = src.slice(src_curves.points_for_curve(i_src_curve)); + const IndexRange duplicates_range = range_for_offsets_index(curve_offsets, i_selection); + for (const int i_duplicate : IndexRange(duplicates_range.size()).drop_front(1)) { + const int i_dst_curve = duplicates_range[i_duplicate]; + copy_hashed_ids( + curve_src, i_duplicate, dst.slice(dst_curves.points_for_curve(i_dst_curve))); + } } - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray_Span<T> src{src_attribute.varray.typed<T>()}; - MutableSpan<T> dst = dst_attribute.as_span<T>(); + }); + dst_attribute.save(); +} - switch (out_domain) { - case ATTR_DOMAIN_EDGE: - threaded_slice_fill<T>(offsets, src, dst); - break; - case ATTR_DOMAIN_POINT: - threaded_mapped_copy<T>(point_mapping, src, dst); - break; - default: - break; +static void duplicate_curves(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + const IndexAttributes &attribute_outputs) +{ + if (!geometry_set.has_curves()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + + const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); + const Curves &curves_id = *src_component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + FieldEvaluator evaluator{field_context, curves.curves_num()}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + /* The offset in the result curve domain at every selected input curve. */ + Array<int> curve_offsets(selection.size() + 1); + Array<int> point_offsets(selection.size() + 1); + + int dst_curves_size = 0; + int dst_points_size = 0; + for (const int i_curve : selection.index_range()) { + const int count = std::max(counts[selection[i_curve]], 0); + curve_offsets[i_curve] = dst_curves_size; + point_offsets[i_curve] = dst_points_size; + dst_curves_size += count; + dst_points_size += count * curves.points_for_curve(selection[i_curve]).size(); + } + curve_offsets.last() = dst_curves_size; + point_offsets.last() = dst_points_size; + + Curves *new_curves_id = bke::curves_new_nomain(dst_points_size, dst_curves_size); + bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); + MutableSpan<int> all_dst_offsets = new_curves.offsets(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i_src_curve = selection[i_selection]; + const IndexRange src_curve_range = curves.points_for_curve(i_src_curve); + const IndexRange dst_curves_range = range_for_offsets_index(curve_offsets, i_selection); + MutableSpan<int> dst_offsets = all_dst_offsets.slice(dst_curves_range); + for (const int i_duplicate : IndexRange(dst_curves_range.size())) { + dst_offsets[i_duplicate] = point_offsets[i_selection] + + src_curve_range.size() * i_duplicate; } - }); - dst_attribute.save(); + } + }); + all_dst_offsets.last() = dst_points_size; + + CurveComponent dst_component; + dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); + + copy_curve_attributes_without_id( + geometry_set, src_component, curves, selection, curve_offsets, new_curves, dst_component); + + copy_stable_id_curves( + curves, selection, curve_offsets, src_component, new_curves, dst_component); + + if (attribute_outputs.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_CURVE, selection, attribute_outputs, curve_offsets); } + + geometry_set.replace_curves(new_curves_id); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Faces + * \{ */ + /** * Copies the attributes for face duplicates. If copying the face domain, the attributes are * copied with an offset fill, otherwise a mapping is used. @@ -478,11 +416,10 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set, const GeometryComponent &src_component, GeometryComponent &dst_component) { - Map<AttributeIDRef, AttributeKind> gathered_attributes; - gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, false); - for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); if (!src_attribute) { @@ -524,88 +461,61 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set, } } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Duplication Functions - * \{ */ - -static void duplicate_splines(GeometrySet &geometry_set, - const Field<int> &count_field, - const Field<bool> &selection_field, - IndexAttributes &attributes) +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the face domain. + * + * This function could be threaded in the future, but since it is only 1 attribute and the + * `face->edge->vert` mapping would mean creating a 1/1 mapping to allow for it, is it worth it? + */ +static void copy_stable_id_faces(const Mesh &mesh, + const IndexMask selection, + const Span<int> poly_offsets, + const Span<int> vert_mapping, + const MeshComponent &src_component, + MeshComponent &dst_component) { - if (!geometry_set.has_curves()) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { return; } - geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); - - const GeometryComponent &src_component = *geometry_set.get_component_for_read( - GEO_COMPONENT_TYPE_CURVE); - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_CURVE); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add(count_field); - evaluator.set_selection(selection_field); - evaluator.evaluate(); - const VArray<int> counts = evaluator.get_evaluated<int>(0); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - Array<int> curve_offsets(selection.size() + 1); + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); - int dst_splines_size = 0; - int dst_points_size = 0; - for (const int i_spline : selection.index_range()) { - int count = std::max(counts[selection[i_spline]], 0); - curve_offsets[i_spline] = dst_splines_size; - dst_splines_size += count; - dst_points_size += count * curve->splines()[selection[i_spline]]->size(); - } - curve_offsets.last() = dst_splines_size; - - Array<int> control_point_offsets = curve->control_point_offsets(); - Array<int> point_mapping(dst_points_size); - - std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - int point_index = 0; - for (const int i_spline : selection.index_range()) { - const IndexRange spline_range = range_for_offsets_index(curve_offsets, i_spline); - for ([[maybe_unused]] const int i_duplicate : IndexRange(spline_range.size())) { - SplinePtr spline = curve->splines()[selection[i_spline]]->copy(); - for (const int i_point : IndexRange(curve->splines()[selection[i_spline]]->size())) { - point_mapping[point_index++] = control_point_offsets[selection[i_spline]] + i_point; + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + int loop_index = 0; + for (const int i_poly : selection.index_range()) { + const IndexRange range = range_for_offsets_index(poly_offsets, i_poly); + if (range.size() == 0) { + continue; + } + const MPoly &source = polys[i_poly]; + for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) { + for ([[maybe_unused]] const int i_loops : IndexRange(source.totloop)) { + if (i_duplicate == 0) { + dst[loop_index] = src[vert_mapping[loop_index]]; + } + else { + dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate); + } + loop_index++; } - new_curve->add_spline(std::move(spline)); } } - new_curve->attributes.reallocate(new_curve->splines().size()); - - CurveComponent dst_component; - dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); - - Vector<std::string> skip( - {"position", "radius", "resolution", "cyclic", "tilt", "handle_left", "handle_right"}); - - copy_spline_attributes_without_id( - geometry_set, point_mapping, curve_offsets, skip, src_component, dst_component); - - copy_stable_id_splines(*curve, selection, curve_offsets, src_component, dst_component); - - if (attributes.duplicate_index) { - create_duplicate_index_attribute( - dst_component, ATTR_DOMAIN_CURVE, selection, attributes, curve_offsets); - } - geometry_set.replace_curves(dst_component.get_for_write()); + dst_attribute.save(); } static void duplicate_faces(GeometrySet &geometry_set, const Field<int> &count_field, const Field<bool> &selection_field, - IndexAttributes &attributes) + const IndexAttributes &attribute_outputs) { if (!geometry_set.has_mesh()) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); @@ -613,25 +523,21 @@ static void duplicate_faces(GeometrySet &geometry_set, } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); - GeometryComponent &component = geometry_set.get_component_for_write(GEO_COMPONENT_TYPE_MESH); - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator evaluator(field_context, domain_size); + const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *src_component.get_for_read(); + Span<MVert> verts(mesh.mvert, mesh.totvert); + Span<MEdge> edges(mesh.medge, mesh.totedge); + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + Span<MLoop> loops(mesh.mloop, mesh.totloop); + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator(field_context, polys.size()); evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); const VArray<int> counts = evaluator.get_evaluated<int>(0); - MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - const Mesh &mesh = *mesh_component.get_for_read(); - Span<MVert> verts(mesh.mvert, mesh.totvert); - Span<MEdge> edges(mesh.medge, mesh.totedge); - Span<MPoly> polys(mesh.mpoly, mesh.totpoly); - Span<MLoop> loops(mesh.mloop, mesh.totloop); - int total_polys = 0; int total_loops = 0; Array<int> offsets(selection.size() + 1); @@ -643,17 +549,16 @@ static void duplicate_faces(GeometrySet &geometry_set, } offsets[selection.size()] = total_polys; - Array<int> vert_mapping(total_loops); - Array<int> edge_mapping(total_loops); - Array<int> loop_mapping(total_loops); - Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys); - MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop); MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly); + Array<int> vert_mapping(new_verts.size()); + Array<int> edge_mapping(new_edges.size()); + Array<int> loop_mapping(new_loops.size()); + int poly_index = 0; int loop_index = 0; for (const int i_selection : selection.index_range()) { @@ -684,6 +589,7 @@ static void duplicate_faces(GeometrySet &geometry_set, poly_index++; } } + MeshComponent dst_component; dst_component.replace(new_mesh, GeometryOwnershipType::Editable); @@ -692,33 +598,133 @@ static void duplicate_faces(GeometrySet &geometry_set, vert_mapping, loop_mapping, offsets, - mesh_component, + src_component, dst_component); - copy_stable_id_faces(mesh, selection, offsets, vert_mapping, mesh_component, dst_component); - mesh_component.replace(dst_component.get_for_write()); + copy_stable_id_faces(mesh, selection, offsets, vert_mapping, src_component, dst_component); - if (attributes.duplicate_index) { + if (attribute_outputs.duplicate_index) { create_duplicate_index_attribute( - dst_component, ATTR_DOMAIN_FACE, selection, attributes, offsets); + dst_component, ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets); + } + + geometry_set.replace_mesh(new_mesh); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Edges + * \{ */ + +/** + * Copies the attributes for edge duplicates. If copying the edge domain, the attributes are + * copied with an offset fill, for point domain a mapping is used. + */ +static void copy_edge_attributes_without_id(GeometrySet &geometry_set, + const Span<int> point_mapping, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, false); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + const AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_EDGE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(point_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the edge domain. + */ +static void copy_stable_id_edges(const Mesh &mesh, + const IndexMask selection, + const Span<int> edge_offsets, + const MeshComponent &src_component, + MeshComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + Span<MEdge> edges(mesh.medge, mesh.totedge); + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + if (edge_range.size() == 0) { + continue; + } + const MEdge &edge = edges[i_edge]; + const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2}; + + dst[vert_range[0]] = src[edge.v1]; + dst[vert_range[1]] = src[edge.v2]; + for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) { + dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge.v1], i_duplicate); + dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge.v2], i_duplicate); + } + } + }); + dst_attribute.save(); } static void duplicate_edges(GeometrySet &geometry_set, const Field<int> &count_field, const Field<bool> &selection_field, - IndexAttributes &attributes) + const IndexAttributes &attribute_outputs) { if (!geometry_set.has_mesh()) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; }; - const GeometryComponent &src_component = *geometry_set.get_component_for_read( - GEO_COMPONENT_TYPE_MESH); - const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *src_component.get_for_read(); + Span<MVert> verts(mesh.mvert, mesh.totvert); + Span<MEdge> edges(mesh.medge, mesh.totedge); GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE}; - FieldEvaluator evaluator{field_context, domain_size}; + FieldEvaluator evaluator{field_context, edges.size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -727,10 +733,6 @@ static void duplicate_edges(GeometrySet &geometry_set, Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts); - const Mesh *mesh = geometry_set.get_mesh_for_read(); - Span<MVert> verts(mesh->mvert, mesh->totvert); - Span<MEdge> edges(mesh->medge, mesh->totedge); - Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0); MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); @@ -767,31 +769,36 @@ static void duplicate_edges(GeometrySet &geometry_set, copy_edge_attributes_without_id( geometry_set, vert_orig_indices, edge_offsets, src_component, dst_component); - copy_stable_id_edges(*mesh, selection, edge_offsets, src_component, dst_component); + copy_stable_id_edges(mesh, selection, edge_offsets, src_component, dst_component); - if (attributes.duplicate_index) { + if (attribute_outputs.duplicate_index) { create_duplicate_index_attribute( - dst_component, ATTR_DOMAIN_EDGE, selection, attributes, edge_offsets); + dst_component, ATTR_DOMAIN_EDGE, selection, attribute_outputs, edge_offsets); } - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace(dst_component.get_for_write()); + geometry_set.replace_mesh(new_mesh); } -static void duplicate_points_curve(const GeometryComponentType component_type, +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Points (Curves) + * \{ */ + +static void duplicate_points_curve(GeometrySet &geometry_set, const Field<int> &count_field, const Field<bool> &selection_field, - GeometrySet &geometry_set, - IndexAttributes &attributes) + const IndexAttributes &attribute_outputs) { - const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); - const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); + const Curves &src_curves_id = *src_component.get_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + if (src_curves.points_num() == 0) { return; } GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, domain_size}; + FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -799,82 +806,98 @@ static void duplicate_points_curve(const GeometryComponentType component_type, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + const int dst_size = offsets.last(); - CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - Array<int> control_point_offsets = curve->control_point_offsets(); - std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - - Array<int> parent(domain_size); - int spline = 0; - for (const int i_spline : IndexRange(domain_size)) { - if (i_spline == control_point_offsets[spline + 1]) { - spline++; + Array<int> point_to_curve_map(src_curves.points_num()); + threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) { + for (const int i_curve : range) { + const IndexRange point_range = src_curves.points_for_curve(i_curve); + point_to_curve_map.as_mutable_span().slice(point_range).fill(i_curve); } - parent[i_spline] = spline; + }); + + Curves *new_curves_id = bke::curves_new_nomain(dst_size, dst_size); + bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); + MutableSpan<int> new_curve_offsets = new_curves.offsets(); + for (const int i : new_curves.curves_range()) { + new_curve_offsets[i] = i; } + new_curve_offsets.last() = dst_size; - for (const int i_point : selection) { - const IndexRange point_range = range_for_offsets_index(offsets, i_point); - for ([[maybe_unused]] const int i_duplicate : IndexRange(point_range.size())) { - const SplinePtr &parent_spline = curve->splines()[parent[i_point]]; - switch (parent_spline->type()) { - case CurveType::CURVE_TYPE_BEZIER: { - std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); - spline->resize(1); - spline->set_resolution(2); - new_curve->add_spline(std::move(spline)); - break; - } - case CurveType::CURVE_TYPE_NURBS: { - std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); - spline->resize(1); - spline->set_resolution(2); - new_curve->add_spline(std::move(spline)); + CurveComponent dst_component; + dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); + + Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, false); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (domain) { + case ATTR_DOMAIN_CURVE: + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const T &src_value = src[point_to_curve_map[selection[i_selection]]]; + const IndexRange duplicate_range = range_for_offsets_index(offsets, i_selection); + dst.slice(duplicate_range).fill(src_value); + } + }); break; - } - case CurveType::CURVE_TYPE_POLY: { - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->resize(1); - new_curve->add_spline(std::move(spline)); + case ATTR_DOMAIN_POINT: + threaded_slice_fill(offsets, src, dst); break; - } - case CurveType::CURVE_TYPE_CATMULL_ROM: { - /* Catmull Rom curves are not supported yet. */ + default: break; - } } - } + }); + dst_attribute.save(); } - new_curve->attributes.reallocate(new_curve->splines().size()); - CurveComponent dst_component; - dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); - - copy_point_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_CURVE, false, offsets, src_component, dst_component); copy_stable_id_point(offsets, src_component, dst_component); - if (attributes.duplicate_index) { + if (attribute_outputs.duplicate_index) { create_duplicate_index_attribute( - dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span()); } - curve_component.replace(dst_component.get_for_write()); + geometry_set.replace_curves(new_curves_id); } -static void duplicate_points_mesh(const GeometryComponentType component_type, +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Points (Mesh) + * \{ */ + +static void duplicate_points_mesh(GeometrySet &geometry_set, const Field<int> &count_field, const Field<bool> &selection_field, - GeometrySet &geometry_set, - IndexAttributes &attributes) + const IndexAttributes &attribute_outputs) { - const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); - const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *geometry_set.get_mesh_for_read(); + Span<MVert> src_verts(mesh.mvert, mesh.totvert); GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, domain_size}; + FieldEvaluator evaluator{field_context, src_verts.size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -883,13 +906,10 @@ static void duplicate_points_mesh(const GeometryComponentType component_type, Array<int> offsets = accumulate_counts_to_offsets(selection, counts); - const Mesh *mesh = geometry_set.get_mesh_for_read(); - Span<MVert> src_verts(mesh->mvert, mesh->totvert); - Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0); MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert); - threaded_slice_fill<MVert>(offsets.as_span(), src_verts, dst_verts); + threaded_slice_fill(offsets.as_span(), src_verts, dst_verts); MeshComponent dst_component; dst_component.replace(new_mesh, GeometryOwnershipType::Editable); @@ -898,26 +918,31 @@ static void duplicate_points_mesh(const GeometryComponentType component_type, copy_stable_id_point(offsets, src_component, dst_component); - if (attributes.duplicate_index) { + if (attribute_outputs.duplicate_index) { create_duplicate_index_attribute( - dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span()); } - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace(dst_component.get_for_write()); + geometry_set.replace_mesh(new_mesh); } -static void duplicate_points_pointcloud(const GeometryComponentType component_type, +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Points (Point Cloud) + * \{ */ + +static void duplicate_points_pointcloud(GeometrySet &geometry_set, const Field<int> &count_field, const Field<bool> &selection_field, - GeometrySet &geometry_set, - IndexAttributes &attributes) + const IndexAttributes &attribute_outputs) { - const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); - const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + const PointCloudComponent &src_points = + *geometry_set.get_component_for_read<PointCloudComponent>(); + const int point_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, domain_size}; + GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, point_size}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -931,58 +956,65 @@ static void duplicate_points_pointcloud(const GeometryComponentType component_ty dst_component.replace(pointcloud, GeometryOwnershipType::Editable); copy_point_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_POINT_CLOUD, false, offsets, src_component, dst_component); + geometry_set, GEO_COMPONENT_TYPE_POINT_CLOUD, false, offsets, src_points, dst_component); - copy_stable_id_point(offsets, src_component, dst_component); + copy_stable_id_point(offsets, src_points, dst_component); - if (attributes.duplicate_index) { + if (attribute_outputs.duplicate_index) { create_duplicate_index_attribute( - dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets); + dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets); } geometry_set.replace_pointcloud(pointcloud); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Points + * \{ */ + static void duplicate_points(GeometrySet &geometry_set, const Field<int> &count_field, const Field<bool> &selection_field, - IndexAttributes &attributes) + const IndexAttributes &attribute_outputs) { - if (!geometry_set.has_mesh() && !geometry_set.has_curves() && !geometry_set.has_pointcloud()) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); - return; - } - Vector<GeometryComponentType> component_types = geometry_set.gather_component_types(true, true); - Vector<GeometryComponentType> types_to_keep; for (const GeometryComponentType component_type : component_types) { switch (component_type) { case GEO_COMPONENT_TYPE_POINT_CLOUD: - types_to_keep.append(component_type); - duplicate_points_pointcloud( - component_type, count_field, selection_field, geometry_set, attributes); + if (geometry_set.has_pointcloud()) { + duplicate_points_pointcloud( + geometry_set, count_field, selection_field, attribute_outputs); + } break; case GEO_COMPONENT_TYPE_MESH: - types_to_keep.append(component_type); - duplicate_points_mesh( - component_type, count_field, selection_field, geometry_set, attributes); + if (geometry_set.has_mesh()) { + duplicate_points_mesh(geometry_set, count_field, selection_field, attribute_outputs); + } break; case GEO_COMPONENT_TYPE_CURVE: - types_to_keep.append(component_type); - duplicate_points_curve( - component_type, count_field, selection_field, geometry_set, attributes); + if (geometry_set.has_curves()) { + duplicate_points_curve(geometry_set, count_field, selection_field, attribute_outputs); + } break; default: break; } } - types_to_keep.append(GEO_COMPONENT_TYPE_INSTANCES); - geometry_set.keep_only(types_to_keep); + component_types.append(GEO_COMPONENT_TYPE_INSTANCES); + geometry_set.keep_only(component_types); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Instances + * \{ */ + static void duplicate_instances(GeometrySet &geometry_set, const Field<int> &count_field, const Field<bool> &selection_field, - IndexAttributes &attributes) + const IndexAttributes &attribute_outputs) { if (!geometry_set.has_instances()) { geometry_set.clear(); @@ -992,9 +1024,8 @@ static void duplicate_instances(GeometrySet &geometry_set, const InstancesComponent &src_instances = *geometry_set.get_component_for_read<InstancesComponent>(); - const int domain_size = src_instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; - FieldEvaluator evaluator{field_context, domain_size}; + FieldEvaluator evaluator{field_context, src_instances.instances_amount()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -1002,41 +1033,44 @@ static void duplicate_instances(GeometrySet &geometry_set, const VArray<int> counts = evaluator.get_evaluated<int>(0); Array<int> offsets = accumulate_counts_to_offsets(selection, counts); - if (offsets.last() == 0) { geometry_set.clear(); return; } - GeometrySet instances_geometry; - InstancesComponent &dst_instances = - instances_geometry.get_component_for_write<InstancesComponent>(); + GeometrySet dst_geometry; + InstancesComponent &dst_instances = dst_geometry.get_component_for_write<InstancesComponent>(); dst_instances.resize(offsets.last()); for (const int i_selection : selection.index_range()) { - const int count = offsets[i_selection + 1] - offsets[i_selection]; - if (count == 0) { + const IndexRange range = range_for_offsets_index(offsets, i_selection); + if (range.size() == 0) { continue; } const int old_handle = src_instances.instance_reference_handles()[i_selection]; const InstanceReference reference = src_instances.references()[old_handle]; const int new_handle = dst_instances.add_reference(reference); const float4x4 transform = src_instances.instance_transforms()[i_selection]; - dst_instances.instance_transforms().slice(offsets[i_selection], count).fill(transform); - dst_instances.instance_reference_handles().slice(offsets[i_selection], count).fill(new_handle); + dst_instances.instance_transforms().slice(range).fill(transform); + dst_instances.instance_reference_handles().slice(range).fill(new_handle); } copy_point_attributes_without_id( geometry_set, GEO_COMPONENT_TYPE_INSTANCES, true, offsets, src_instances, dst_instances); - if (attributes.duplicate_index) { + if (attribute_outputs.duplicate_index) { create_duplicate_index_attribute( - dst_instances, ATTR_DOMAIN_INSTANCE, selection, attributes, offsets); + dst_instances, ATTR_DOMAIN_INSTANCE, selection, attribute_outputs, offsets); } - geometry_set.remove(GEO_COMPONENT_TYPE_INSTANCES); - geometry_set.add(dst_instances); + geometry_set = std::move(dst_geometry); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Entry Point + * \{ */ + static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -1046,33 +1080,28 @@ static void node_geo_exec(GeoNodeExecParams params) Field<int> count_field = params.extract_input<Field<int>>("Amount"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); - IndexAttributes attributes; + IndexAttributes attribute_outputs; if (params.output_is_required("Duplicate Index")) { - attributes.duplicate_index = StrongAnonymousAttributeID("duplicate_index"); + attribute_outputs.duplicate_index = StrongAnonymousAttributeID("duplicate_index"); } if (duplicate_domain == ATTR_DOMAIN_INSTANCE) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); - duplicate_instances(geometry_set, count_field, selection_field, attributes); + duplicate_instances(geometry_set, count_field, selection_field, attribute_outputs); } else { - if (geometry_set.is_empty()) { - params.set_default_remaining_outputs(); - return; - } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { switch (duplicate_domain) { case ATTR_DOMAIN_CURVE: - duplicate_splines(geometry_set, count_field, selection_field, attributes); + duplicate_curves(geometry_set, count_field, selection_field, attribute_outputs); break; case ATTR_DOMAIN_FACE: - duplicate_faces(geometry_set, count_field, selection_field, attributes); + duplicate_faces(geometry_set, count_field, selection_field, attribute_outputs); break; case ATTR_DOMAIN_EDGE: - duplicate_edges(geometry_set, count_field, selection_field, attributes); + duplicate_edges(geometry_set, count_field, selection_field, attribute_outputs); break; case ATTR_DOMAIN_POINT: - duplicate_points(geometry_set, count_field, selection_field, attributes); + duplicate_points(geometry_set, count_field, selection_field, attribute_outputs); break; default: BLI_assert_unreachable(); @@ -1086,13 +1115,13 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - if (attributes.duplicate_index) { + if (attribute_outputs.duplicate_index) { params.set_output( "Duplicate Index", - AnonymousAttributeFieldInput::Create<int>(std::move(attributes.duplicate_index), + AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.duplicate_index), params.attribute_producer_name())); } - params.set_output("Geometry", geometry_set); + params.set_output("Geometry", std::move(geometry_set)); } /** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index 6e131dac3de..3ba1378abe1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" - #include "node_geometry_util.hh" namespace blender::nodes::node_geo_input_curve_handles_cc { diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index f6c45c4bca3..756f9123f2b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -1,13 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_mesh.h" -#include "BKE_spline.hh" - #include "node_geometry_util.hh" namespace blender::nodes::node_geo_input_normal_cc { diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index c3d87055745..ab6f6b40d5e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -2,6 +2,7 @@ #include "node_geometry_util.hh" +#include "BKE_curves.hh" #include "BKE_spline.hh" namespace blender::nodes::node_geo_input_spline_length_cc { @@ -82,16 +83,16 @@ static VArray<int> construct_spline_count_gvarray(const CurveComponent &componen if (!component.has_curves()) { return {}; } - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); + const Curves &curves_id = *component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - Span<SplinePtr> splines = curve->splines(); - auto count_fn = [splines](int i) { return splines[i]->size(); }; + auto count_fn = [curves](int64_t i) { return curves.points_for_curve(i).size(); }; if (domain == ATTR_DOMAIN_CURVE) { - return VArray<int>::ForFunc(splines.size(), count_fn); + return VArray<int>::ForFunc(curves.curves_num(), count_fn); } if (domain == ATTR_DOMAIN_POINT) { - VArray<int> count = VArray<int>::ForFunc(splines.size(), count_fn); + VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn); return component.attribute_try_adapt_domain<int>( std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 05cc91b41e3..0e2803cd035 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -1,15 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_pointcloud.h" -#include "BKE_spline.hh" -#include "BKE_type_conversions.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - #include "GEO_realize_instances.hh" #include "node_geometry_util.hh" diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc index f9d9ed75f30..7923ad6264d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc @@ -31,7 +31,8 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Vector>(N_("Axis")) .default_value({1.0f, 0.0f, 0.0f}) .supports_field() - .description(N_("Direction in which to scale the element")); + .description(N_("Direction in which to scale the element")) + .make_available([](bNode &node) { node.custom2 = GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS; }); b.add_output<decl::Geometry>(N_("Geometry")); }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 301410f5126..271dd824d27 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -106,10 +106,12 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, for (const int i : bezier.positions().index_range()) { if (current_mask < selection.size() && selection[current_mask] == current_point) { if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - bezier.set_handle_position_left(i, positions_input[i] + offsets_input[i]); + bezier.set_handle_position_left( + i, positions_input[current_point] + offsets_input[current_point]); } else { - bezier.set_handle_position_right(i, positions_input[i] + offsets_input[i]); + bezier.set_handle_position_right( + i, positions_input[current_point] + offsets_input[current_point]); } current_mask++; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index 543e57d38ad..0892e068ce2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -16,8 +16,11 @@ static void set_id_in_component(GeometryComponent &component, const Field<bool> &selection_field, const Field<int> &id_field) { - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const AttributeDomain domain = (component.type() == GEO_COMPONENT_TYPE_INSTANCES) ? + ATTR_DOMAIN_INSTANCE : + ATTR_DOMAIN_POINT; + GeometryComponentFieldContext field_context{component, domain}; + const int domain_size = component.attribute_domain_size(domain); if (domain_size == 0) { return; } @@ -30,7 +33,7 @@ static void set_id_in_component(GeometryComponent &component, * the field. However, as an optimization, use a faster code path when it already exists. */ if (component.attribute_exists("id")) { OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( - "id", ATTR_DOMAIN_POINT); + "id", domain); evaluator.add_with_destination(id_field, id_attribute.varray()); evaluator.evaluate(); id_attribute.save(); @@ -41,7 +44,7 @@ static void set_id_in_component(GeometryComponent &component, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); const VArray<int> &result_ids = evaluator.get_evaluated<int>(0); OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( - "id", ATTR_DOMAIN_POINT); + "id", domain); result_ids.materialize(selection, id_attribute.as_span()); id_attribute.save(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index fccfed21ef4..f3031ff3678 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" - #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_spline_resolution_cc { diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 2d5b0e58367..7f0ba950490 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_generic_array.hh" #include "BLI_kdopbvh.h" #include "BLI_task.hh" @@ -12,8 +13,6 @@ #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" -#include "FN_generic_array.hh" - #include "UI_interface.h" #include "UI_resources.h" @@ -24,7 +23,6 @@ namespace blender::nodes::node_geo_transfer_attribute_cc { using namespace blender::bke::mesh_surface_sample; -using blender::fn::GArray; NODE_STORAGE_FUNCS(NodeGeometryTransferAttribute) diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 9159ac081e0..a04544e2814 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -13,7 +13,6 @@ #include "BKE_curves.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" -#include "BKE_spline.hh" #include "BKE_volume.h" #include "DEG_depsgraph_query.h" @@ -127,9 +126,7 @@ static void translate_geometry_set(GeometrySet &geometry, const Depsgraph &depsgraph) { if (Curves *curves = geometry.get_curves_for_write()) { - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); - curve->translate(translation); - geometry.replace_curves(curve_eval_to_curves(*curve)); + bke::CurvesGeometry::wrap(curves->geometry).translate(translation); } if (Mesh *mesh = geometry.get_mesh_for_write()) { translate_mesh(*mesh, translation); @@ -150,9 +147,7 @@ void transform_geometry_set(GeometrySet &geometry, const Depsgraph &depsgraph) { if (Curves *curves = geometry.get_curves_for_write()) { - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); - curve->transform(transform); - geometry.replace_curves(curve_eval_to_curves(*curve)); + bke::CurvesGeometry::wrap(curves->geometry).transform(transform); } if (Mesh *mesh = geometry.get_mesh_for_write()) { transform_mesh(*mesh, transform); diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 5ded8413cf9..76cdbfb140f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -44,7 +44,13 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, CustomData_MeshMasks cd_mask_extra = { CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, 0, CD_MASK_ORIGINDEX}; BMeshCreateParams create_params{0}; - BMeshFromMeshParams from_mesh_params{true, 1, 1, 1, cd_mask_extra}; + BMeshFromMeshParams from_mesh_params{}; + from_mesh_params.calc_face_normal = true; + from_mesh_params.calc_vert_normal = true; + from_mesh_params.add_key_index = true; + from_mesh_params.use_shapekey = true; + from_mesh_params.active_shapekey = 1; + from_mesh_params.cd_mask_extra = cd_mask_extra; BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params); /* Tag faces to be triangulated from the selection mask. */ diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 5b1b0a21614..5c8f4c52f75 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -15,7 +15,6 @@ namespace blender::nodes::geometry_nodes_eval_log { -using fn::CPPType; using fn::FieldCPPType; using fn::FieldInput; using fn::GField; diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 948c8376460..0ab446d8b0c 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -10,6 +10,7 @@ #include "DNA_node_types.h" #include "BLI_color.hh" +#include "BLI_cpp_type_make.hh" #include "BLI_listbase.h" #include "BLI_math_vec_types.hh" #include "BLI_string.h" @@ -30,7 +31,6 @@ #include "NOD_node_declaration.hh" #include "NOD_socket.h" -#include "FN_cpp_type_make.hh" #include "FN_field.hh" using namespace blender; @@ -683,11 +683,11 @@ static bNodeSocketType *make_socket_type_virtual() static bNodeSocketType *make_socket_type_bool() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<bool>(); + socktype->base_cpp_type = &blender::CPPType::get<bool>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; }; - socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<bool>>(); + socktype->geometry_nodes_cpp_type = &blender::CPPType::get<ValueOrField<bool>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { bool value; socket.typeinfo->get_base_cpp_value(socket, &value); @@ -699,11 +699,11 @@ static bNodeSocketType *make_socket_type_bool() static bNodeSocketType *make_socket_type_float(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype); - socktype->base_cpp_type = &blender::fn::CPPType::get<float>(); + socktype->base_cpp_type = &blender::CPPType::get<float>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; }; - socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<float>>(); + socktype->geometry_nodes_cpp_type = &blender::CPPType::get<ValueOrField<float>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { float value; socket.typeinfo->get_base_cpp_value(socket, &value); @@ -715,11 +715,11 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype) static bNodeSocketType *make_socket_type_int(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype); - socktype->base_cpp_type = &blender::fn::CPPType::get<int>(); + socktype->base_cpp_type = &blender::CPPType::get<int>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; }; - socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<int>>(); + socktype->geometry_nodes_cpp_type = &blender::CPPType::get<ValueOrField<int>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { int value; socket.typeinfo->get_base_cpp_value(socket, &value); @@ -731,11 +731,11 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype) static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype); - socktype->base_cpp_type = &blender::fn::CPPType::get<blender::float3>(); + socktype->base_cpp_type = &blender::CPPType::get<blender::float3>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; }; - socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<blender::float3>>(); + socktype->geometry_nodes_cpp_type = &blender::CPPType::get<ValueOrField<blender::float3>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { blender::float3 value; socket.typeinfo->get_base_cpp_value(socket, &value); @@ -747,12 +747,12 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<blender::ColorGeometry4f>(); + socktype->base_cpp_type = &blender::CPPType::get<blender::ColorGeometry4f>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; socktype->geometry_nodes_cpp_type = - &blender::fn::CPPType::get<ValueOrField<blender::ColorGeometry4f>>(); + &blender::CPPType::get<ValueOrField<blender::ColorGeometry4f>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { blender::ColorGeometry4f value; socket.typeinfo->get_base_cpp_value(socket, &value); @@ -764,11 +764,11 @@ static bNodeSocketType *make_socket_type_rgba() static bNodeSocketType *make_socket_type_string() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<std::string>(); + socktype->base_cpp_type = &blender::CPPType::get<std::string>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); }; - socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<std::string>>(); + socktype->geometry_nodes_cpp_type = &blender::CPPType::get<ValueOrField<std::string>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { std::string value; value.~basic_string(); @@ -778,16 +778,16 @@ static bNodeSocketType *make_socket_type_string() return socktype; } -MAKE_CPP_TYPE(Object, Object *, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(Image, Image *, CPPTypeFlags::BasicType) -MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Object, Object *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Collection, Collection *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Texture, Tex *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Image, Image *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Material, Material *, CPPTypeFlags::BasicType) static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<Object *>(); + socktype->base_cpp_type = &blender::CPPType::get<Object *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value; }; @@ -799,7 +799,7 @@ static bNodeSocketType *make_socket_type_object() static bNodeSocketType *make_socket_type_geometry() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<GeometrySet>(); + socktype->base_cpp_type = &blender::CPPType::get<GeometrySet>(); socktype->get_base_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) { new (r_value) GeometrySet(); }; @@ -811,7 +811,7 @@ static bNodeSocketType *make_socket_type_geometry() static bNodeSocketType *make_socket_type_collection() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<Collection *>(); + socktype->base_cpp_type = &blender::CPPType::get<Collection *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value; }; @@ -823,7 +823,7 @@ static bNodeSocketType *make_socket_type_collection() static bNodeSocketType *make_socket_type_texture() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<Tex *>(); + socktype->base_cpp_type = &blender::CPPType::get<Tex *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value; }; @@ -835,7 +835,7 @@ static bNodeSocketType *make_socket_type_texture() static bNodeSocketType *make_socket_type_image() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_IMAGE, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<Image *>(); + socktype->base_cpp_type = &blender::CPPType::get<Image *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Image **)r_value = ((bNodeSocketValueImage *)socket.default_value)->value; }; @@ -847,7 +847,7 @@ static bNodeSocketType *make_socket_type_image() static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); - socktype->base_cpp_type = &blender::fn::CPPType::get<Material *>(); + socktype->base_cpp_type = &blender::CPPType::get<Material *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value; }; diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 9a3663b51c2..9b4ea0e0db6 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -127,6 +127,7 @@ set(SRC set(LIB bf_functions bf_intern_sky + bf_nodes ) if(WITH_PYTHON) diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 9b1d2ee1d63..c8785721dfb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -42,12 +42,12 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } -static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_clamp_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{ + static 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{ + static 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); diff --git a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc index 0c8cc209d5a..3723480ffa3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc @@ -87,37 +87,35 @@ static int gpu_shader_valtorgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "valtorgb", in, out, tex, GPU_constant(&layer)); } -class ColorBandFunction : public blender::fn::MultiFunction { +class ColorBandFunction : public fn::MultiFunction { private: const ColorBand &color_band_; public: ColorBandFunction(const ColorBand &color_band) : color_band_(color_band) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Color Band"}; + fn::MFSignatureBuilder signature{"Color Band"}; signature.single_input<float>("Value"); - signature.single_output<blender::ColorGeometry4f>("Color"); + signature.single_output<ColorGeometry4f>("Color"); signature.single_output<float>("Alpha"); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - blender::MutableSpan<blender::ColorGeometry4f> colors = - params.uninitialized_single_output<blender::ColorGeometry4f>(1, "Color"); - blender::MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha"); + const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); + MutableSpan<ColorGeometry4f> colors = params.uninitialized_single_output<ColorGeometry4f>( + 1, "Color"); + MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha"); for (int64_t i : mask) { - blender::ColorGeometry4f color; + ColorGeometry4f color; BKE_colorband_evaluate(&color_band_, values[i], color); colors[i] = color; alphas[i] = color.a; @@ -125,8 +123,7 @@ class ColorBandFunction : public blender::fn::MultiFunction { } }; -static void sh_node_valtorgb_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_valtorgb_build_multi_function(nodes::NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); const ColorBand *color_band = (const ColorBand *)bnode.storage; diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 64a5cd97250..b1db0248d9f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -72,35 +72,31 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, GPU_uniform(ext_xyz[2])); } -class CurveVecFunction : public blender::fn::MultiFunction { +class CurveVecFunction : public fn::MultiFunction { private: const CurveMapping &cumap_; public: CurveVecFunction(const CurveMapping &cumap) : cumap_(cumap) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Curve Vec"}; + fn::MFSignatureBuilder signature{"Curve Vec"}; signature.single_input<float>("Fac"); - signature.single_input<blender::float3>("Vector"); - signature.single_output<blender::float3>("Vector"); + signature.single_input<float3>("Vector"); + signature.single_output<float3>("Vector"); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); - const blender::VArray<blender::float3> &vec_in = params.readonly_single_input<blender::float3>( - 1, "Vector"); - blender::MutableSpan<blender::float3> vec_out = - params.uninitialized_single_output<blender::float3>(2, "Vector"); + const VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); + const VArray<float3> &vec_in = params.readonly_single_input<float3>(1, "Vector"); + MutableSpan<float3> vec_out = params.uninitialized_single_output<float3>(2, "Vector"); for (int64_t i : mask) { BKE_curvemapping_evaluate3F(&cumap_, vec_out[i], vec_in[i]); @@ -111,8 +107,7 @@ class CurveVecFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_vec_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_curve_vec_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; @@ -230,35 +225,33 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, GPU_uniform(ext_rgba[3])); } -class CurveRGBFunction : public blender::fn::MultiFunction { +class CurveRGBFunction : public fn::MultiFunction { private: const CurveMapping &cumap_; public: CurveRGBFunction(const CurveMapping &cumap) : cumap_(cumap) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Curve RGB"}; + fn::MFSignatureBuilder signature{"Curve RGB"}; signature.single_input<float>("Fac"); - signature.single_input<blender::ColorGeometry4f>("Color"); - signature.single_output<blender::ColorGeometry4f>("Color"); + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<ColorGeometry4f>("Color"); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); - const blender::VArray<blender::ColorGeometry4f> &col_in = - params.readonly_single_input<blender::ColorGeometry4f>(1, "Color"); - blender::MutableSpan<blender::ColorGeometry4f> col_out = - params.uninitialized_single_output<blender::ColorGeometry4f>(2, "Color"); + const VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); + const VArray<ColorGeometry4f> &col_in = params.readonly_single_input<ColorGeometry4f>(1, + "Color"); + MutableSpan<ColorGeometry4f> col_out = params.uninitialized_single_output<ColorGeometry4f>( + 2, "Color"); for (int64_t i : mask) { BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]); @@ -269,8 +262,7 @@ class CurveRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_rgb_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_curve_rgb_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; @@ -360,33 +352,31 @@ static int gpu_shader_curve_float(GPUMaterial *mat, GPU_uniform(ext_xyz)); } -class CurveFloatFunction : public blender::fn::MultiFunction { +class CurveFloatFunction : public fn::MultiFunction { private: const CurveMapping &cumap_; public: CurveFloatFunction(const CurveMapping &cumap) : cumap_(cumap) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Curve Float"}; + fn::MFSignatureBuilder signature{"Curve Float"}; signature.single_input<float>("Factor"); signature.single_input<float>("Value"); signature.single_output<float>("Value"); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Factor"); - const blender::VArray<float> &val_in = params.readonly_single_input<float>(1, "Value"); - blender::MutableSpan<float> val_out = params.uninitialized_single_output<float>(2, "Value"); + const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor"); + const VArray<float> &val_in = params.readonly_single_input<float>(1, "Value"); + MutableSpan<float> val_out = params.uninitialized_single_output<float>(2, "Value"); for (int64_t i : mask) { val_out[i] = BKE_curvemapping_evaluateF(&cumap_, 0, val_in[i]); @@ -397,8 +387,7 @@ class CurveFloatFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_float_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_curve_float_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index f922499b910..8e7934bf34e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -223,7 +223,7 @@ static float3 clamp_range(const float3 value, const float3 min, const float3 max clamp_range(value.z, min.z, max.z)); } -static void map_range_vector_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps) +static void map_range_vector_signature(fn::MFSignatureBuilder *signature, bool use_steps) { signature->single_input<float3>("Vector"); signature->single_input<float3>("From Min"); @@ -236,34 +236,32 @@ static void map_range_vector_signature(blender::fn::MFSignatureBuilder *signatur signature->single_output<float3>("Vector"); } -class MapRangeVectorFunction : public blender::fn::MultiFunction { +class MapRangeVectorFunction : public fn::MultiFunction { private: bool clamp_; public: MapRangeVectorFunction(bool clamp) : clamp_(clamp) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Vector Map Range"}; + fn::MFSignatureBuilder signature{"Vector Map Range"}; map_range_vector_signature(&signature, false); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); for (int64_t i : mask) { float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -278,35 +276,33 @@ class MapRangeVectorFunction : public blender::fn::MultiFunction { } }; -class MapRangeSteppedVectorFunction : public blender::fn::MultiFunction { +class MapRangeSteppedVectorFunction : public fn::MultiFunction { private: bool clamp_; public: MapRangeSteppedVectorFunction(bool clamp) : clamp_(clamp) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Vector Map Range Stepped"}; + fn::MFSignatureBuilder signature{"Vector Map Range Stepped"}; map_range_vector_signature(&signature, true); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - const blender::VArray<float3> &steps = params.readonly_single_input<float3>(5, "Steps"); - blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(6, "Vector"); + const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + const VArray<float3> &steps = params.readonly_single_input<float3>(5, "Steps"); + MutableSpan<float3> results = params.uninitialized_single_output<float3>(6, "Vector"); for (int64_t i : mask) { float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -322,31 +318,29 @@ class MapRangeSteppedVectorFunction : public blender::fn::MultiFunction { } }; -class MapRangeSmoothstepVectorFunction : public blender::fn::MultiFunction { +class MapRangeSmoothstepVectorFunction : public fn::MultiFunction { public: MapRangeSmoothstepVectorFunction() { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; + fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; map_range_vector_signature(&signature, false); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); for (int64_t i : mask) { float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -357,31 +351,29 @@ class MapRangeSmoothstepVectorFunction : public blender::fn::MultiFunction { } }; -class MapRangeSmootherstepVectorFunction : public blender::fn::MultiFunction { +class MapRangeSmootherstepVectorFunction : public fn::MultiFunction { public: MapRangeSmootherstepVectorFunction() { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; + fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; map_range_vector_signature(&signature, false); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); for (int64_t i : mask) { float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -392,7 +384,7 @@ class MapRangeSmootherstepVectorFunction : public blender::fn::MultiFunction { } }; -static void map_range_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps) +static void map_range_signature(fn::MFSignatureBuilder *signature, bool use_steps) { signature->single_input<float>("Value"); signature->single_input<float>("From Min"); @@ -405,34 +397,32 @@ static void map_range_signature(blender::fn::MFSignatureBuilder *signature, bool signature->single_output<float>("Result"); } -class MapRangeFunction : public blender::fn::MultiFunction { +class MapRangeFunction : public fn::MultiFunction { private: bool clamp_; public: MapRangeFunction(bool clamp) : clamp_(clamp) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Map Range"}; + fn::MFSignatureBuilder signature{"Map Range"}; map_range_signature(&signature, false); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); + const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); + const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); + const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); + const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); + const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); + MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); for (int64_t i : mask) { float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -447,35 +437,33 @@ class MapRangeFunction : public blender::fn::MultiFunction { } }; -class MapRangeSteppedFunction : public blender::fn::MultiFunction { +class MapRangeSteppedFunction : public fn::MultiFunction { private: bool clamp_; public: MapRangeSteppedFunction(bool clamp) : clamp_(clamp) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Map Range Stepped"}; + fn::MFSignatureBuilder signature{"Map Range Stepped"}; map_range_signature(&signature, true); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - const blender::VArray<float> &steps = params.readonly_single_input<float>(5, "Steps"); - blender::MutableSpan<float> results = params.uninitialized_single_output<float>(6, "Result"); + const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); + const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); + const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); + const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); + const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); + const VArray<float> &steps = params.readonly_single_input<float>(5, "Steps"); + MutableSpan<float> results = params.uninitialized_single_output<float>(6, "Result"); for (int64_t i : mask) { float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -491,31 +479,29 @@ class MapRangeSteppedFunction : public blender::fn::MultiFunction { } }; -class MapRangeSmoothstepFunction : public blender::fn::MultiFunction { +class MapRangeSmoothstepFunction : public fn::MultiFunction { public: MapRangeSmoothstepFunction() { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"}; + fn::MFSignatureBuilder signature{"Map Range Smoothstep"}; map_range_signature(&signature, false); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); + const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); + const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); + const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); + const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); + const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); + MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); for (int64_t i : mask) { float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -526,31 +512,29 @@ class MapRangeSmoothstepFunction : public blender::fn::MultiFunction { } }; -class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { +class MapRangeSmootherstepFunction : public fn::MultiFunction { public: MapRangeSmootherstepFunction() { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"}; + fn::MFSignatureBuilder signature{"Map Range Smoothstep"}; map_range_signature(&signature, false); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); + const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); + const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); + const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); + const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); + const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); + MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); for (int64_t i : mask) { float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); @@ -561,8 +545,7 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { } }; -static void sh_node_map_range_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &builder) { const NodeMapRange &storage = node_storage(builder.node()); bool clamp = storage.clamp != 0; diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 83d0ff8177b..a828011a3ab 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -69,8 +69,7 @@ static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams ¶ms) static const char *gpu_shader_get_name(int mode) { - const blender::nodes::FloatMathOperationInfo *info = - blender::nodes::get_float_math_operation_info(mode); + const FloatMathOperationInfo *info = get_float_math_operation_info(mode); if (!info) { return nullptr; } @@ -102,34 +101,32 @@ static int gpu_shader_math(GPUMaterial *mat, return 0; } -static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) +static const fn::MultiFunction *get_base_multi_function(bNode &node) { const int mode = node.custom1; - const blender::fn::MultiFunction *base_fn = nullptr; + const fn::MultiFunction *base_fn = nullptr; - blender::nodes::try_dispatch_float_math_fl_to_fl( - mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SO<float, float> fn{info.title_case_name.c_str(), - function}; - base_fn = &fn; - }); + try_dispatch_float_math_fl_to_fl(mode, [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SO<float, float> fn{info.title_case_name.c_str(), function}; + base_fn = &fn; + }); if (base_fn != nullptr) { return base_fn; } - blender::nodes::try_dispatch_float_math_fl_fl_to_fl( - mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{info.title_case_name.c_str(), - function}; - base_fn = &fn; - }); + try_dispatch_float_math_fl_fl_to_fl(mode, + [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float, float, float> fn{ + info.title_case_name.c_str(), function}; + base_fn = &fn; + }); if (base_fn != nullptr) { return base_fn; } - blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl( - mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{ + try_dispatch_float_math_fl_fl_fl_to_fl( + mode, [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{ info.title_case_name.c_str(), function}; base_fn = &fn; }); @@ -140,27 +137,24 @@ static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) return nullptr; } -class ClampWrapperFunction : public blender::fn::MultiFunction { +class ClampWrapperFunction : public fn::MultiFunction { private: - const blender::fn::MultiFunction &fn_; + const fn::MultiFunction &fn_; public: - ClampWrapperFunction(const blender::fn::MultiFunction &fn) : fn_(fn) + ClampWrapperFunction(const fn::MultiFunction &fn) : fn_(fn) { this->set_signature(&fn.signature()); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext context) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext context) const override { fn_.call(mask, params, context); /* Assumes the output parameter is the last one. */ const int output_param_index = this->param_amount() - 1; /* This has actually been initialized in the call above. */ - blender::MutableSpan<float> results = params.uninitialized_single_output<float>( - output_param_index); + MutableSpan<float> results = params.uninitialized_single_output<float>(output_param_index); for (const int i : mask) { float &value = results[i]; @@ -169,9 +163,9 @@ class ClampWrapperFunction : public blender::fn::MultiFunction { } }; -static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_math_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *base_function = get_base_multi_function(builder.node()); + const fn::MultiFunction *base_function = get_base_multi_function(builder.node()); const bool clamp_output = builder.node().custom2 != 0; if (clamp_output) { diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index fc6ce6a11f7..12707623049 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -84,7 +84,7 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat, return 0; } -class MixRGBFunction : public blender::fn::MultiFunction { +class MixRGBFunction : public fn::MultiFunction { private: bool clamp_; int type_; @@ -92,31 +92,29 @@ class MixRGBFunction : public blender::fn::MultiFunction { public: MixRGBFunction(bool clamp, int type) : clamp_(clamp), type_(type) { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"MixRGB"}; + fn::MFSignatureBuilder signature{"MixRGB"}; signature.single_input<float>("Fac"); - signature.single_input<blender::ColorGeometry4f>("Color1"); - signature.single_input<blender::ColorGeometry4f>("Color2"); - signature.single_output<blender::ColorGeometry4f>("Color"); + signature.single_input<ColorGeometry4f>("Color1"); + signature.single_input<ColorGeometry4f>("Color2"); + signature.single_output<ColorGeometry4f>("Color"); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); - const blender::VArray<blender::ColorGeometry4f> &col1 = - params.readonly_single_input<blender::ColorGeometry4f>(1, "Color1"); - const blender::VArray<blender::ColorGeometry4f> &col2 = - params.readonly_single_input<blender::ColorGeometry4f>(2, "Color2"); - blender::MutableSpan<blender::ColorGeometry4f> results = - params.uninitialized_single_output<blender::ColorGeometry4f>(3, "Color"); + const VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); + const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1, + "Color1"); + const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2, + "Color2"); + MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>( + 3, "Color"); for (int64_t i : mask) { results[i] = col1[i]; @@ -131,7 +129,7 @@ class MixRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_mix_rgb_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &node = builder.node(); bool clamp = node.custom2 & SHD_MIXRGB_CLAMP; diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index 2a88c10b61a..657f591a50c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -27,36 +27,34 @@ static int gpu_shader_seprgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "separate_rgb", in, out); } -class SeparateRGBFunction : public blender::fn::MultiFunction { +class SeparateRGBFunction : public fn::MultiFunction { public: SeparateRGBFunction() { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Separate RGB"}; - signature.single_input<blender::ColorGeometry4f>("Color"); + fn::MFSignatureBuilder signature{"Separate RGB"}; + signature.single_input<ColorGeometry4f>("Color"); signature.single_output<float>("R"); signature.single_output<float>("G"); signature.single_output<float>("B"); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<blender::ColorGeometry4f> &colors = - params.readonly_single_input<blender::ColorGeometry4f>(0, "Color"); - blender::MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R"); - blender::MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G"); - blender::MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B"); + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R"); + MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G"); + MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B"); for (int64_t i : mask) { - blender::ColorGeometry4f color = colors[i]; + ColorGeometry4f color = colors[i]; rs[i] = color.r; gs[i] = color.g; bs[i] = color.b; @@ -64,7 +62,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_seprgb_build_multi_function(NodeMultiFunctionBuilder &builder) { static SeparateRGBFunction fn; builder.set_matching_fn(fn); @@ -106,11 +104,10 @@ static int gpu_shader_combrgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_rgb", in, out); } -static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_combrgb_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{ - "Combine RGB", - [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }}; + static fn::CustomMF_SI_SI_SI_SO<float, float, float, ColorGeometry4f> fn{ + "Combine RGB", [](float r, float g, float b) { return ColorGeometry4f(r, g, b, 1.0f); }}; builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc index 94eb961c25d..0d751157817 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc @@ -27,36 +27,33 @@ static int gpu_shader_sepxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "separate_xyz", in, out); } -class MF_SeparateXYZ : public blender::fn::MultiFunction { +class MF_SeparateXYZ : public fn::MultiFunction { public: MF_SeparateXYZ() { - static blender::fn::MFSignature signature = create_signature(); + static fn::MFSignature signature = create_signature(); this->set_signature(&signature); } - static blender::fn::MFSignature create_signature() + static fn::MFSignature create_signature() { - blender::fn::MFSignatureBuilder signature{"Separate XYZ"}; - signature.single_input<blender::float3>("XYZ"); + fn::MFSignatureBuilder signature{"Separate XYZ"}; + signature.single_input<float3>("XYZ"); signature.single_output<float>("X"); signature.single_output<float>("Y"); signature.single_output<float>("Z"); return signature.build(); } - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { - const blender::VArray<blender::float3> &vectors = - params.readonly_single_input<blender::float3>(0, "XYZ"); - blender::MutableSpan<float> xs = params.uninitialized_single_output<float>(1, "X"); - blender::MutableSpan<float> ys = params.uninitialized_single_output<float>(2, "Y"); - blender::MutableSpan<float> zs = params.uninitialized_single_output<float>(3, "Z"); + const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "XYZ"); + MutableSpan<float> xs = params.uninitialized_single_output<float>(1, "X"); + MutableSpan<float> ys = params.uninitialized_single_output<float>(2, "Y"); + MutableSpan<float> zs = params.uninitialized_single_output<float>(3, "Z"); for (int64_t i : mask) { - blender::float3 xyz = vectors[i]; + float3 xyz = vectors[i]; xs[i] = xyz.x; ys[i] = xyz.y; zs[i] = xyz.z; @@ -64,7 +61,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction { } }; -static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_sepxyz_build_multi_function(NodeMultiFunctionBuilder &builder) { static MF_SeparateXYZ separate_fn; builder.set_matching_fn(separate_fn); @@ -106,10 +103,10 @@ static int gpu_shader_combxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_xyz", in, out); } -static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_combxyz_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{ - "Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }}; + static fn::CustomMF_SI_SI_SI_SO<float, float, float, float3> fn{ + "Combine Vector", [](float x, float y, float z) { return float3(x, y, z); }}; builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc index 66d1ace2222..cad9e1b33f2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -259,7 +259,7 @@ class BrickFunction : public fn::MultiFunction { } }; -static void sh_node_brick_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_brick_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &node = builder.node(); NodeTexBrick *tex = (NodeTexBrick *)node.storage; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc index 047ae303260..b0e5639c893 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc @@ -61,9 +61,7 @@ class NodeTexChecker : public fn::MultiFunction { return signature.build(); } - void call(blender::IndexMask mask, - fn::MFParams params, - fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override { const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); const VArray<ColorGeometry4f> &color1 = params.readonly_single_input<ColorGeometry4f>( @@ -94,8 +92,7 @@ class NodeTexChecker : public fn::MultiFunction { } }; -static void sh_node_tex_checker_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_tex_checker_build_multi_function(NodeMultiFunctionBuilder &builder) { static NodeTexChecker fn; builder.set_matching_fn(fn); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index f92ad4d9fd0..8478cbd406b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -137,8 +137,7 @@ class GradientFunction : public fn::MultiFunction { } }; -static void sh_node_gradient_tex_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_gradient_tex_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &node = builder.node(); NodeTexGradient *tex = (NodeTexGradient *)node.storage; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc index 982d80811fb..95c4a8b8e46 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -159,8 +159,7 @@ class MagicFunction : public fn::MultiFunction { } }; -static void sh_node_magic_tex_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_magic_tex_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &node = builder.node(); NodeTexMagic *tex = (NodeTexMagic *)node.storage; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index 8a672063943..c13ce3c3df3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -514,8 +514,7 @@ class MusgraveFunction : public fn::MultiFunction { } }; -static void sh_node_musgrave_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_musgrave_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &node = builder.node(); NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 9b27c805a64..87fb1aeac29 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -232,7 +232,7 @@ class NoiseFunction : public fn::MultiFunction { } }; -static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder) { const NodeTexNoise &storage = node_storage(builder.node()); builder.construct_and_set_matching_fn<NoiseFunction>(storage.dimensions); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc index d4a0c092acd..f5a4d087dbd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + namespace blender::nodes::node_shader_tex_sky_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -229,6 +231,24 @@ static void node_shader_update_sky(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, sockVector, !(tex->sky_model == 2 && tex->sun_disc == 1)); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + if (params.in_out() == SOCK_OUT) { + search_link_ops_for_declarations(params, declaration.outputs()); + return; + } + if (params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + params.add_item(IFACE_("Vector"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeTexSky"); + NodeTexSky *tex = (NodeTexSky *)node.storage; + tex->sun_disc = false; + params.update_and_connect_available_socket(node, "Vector"); + }); + } +} + } // namespace blender::nodes::node_shader_tex_sky_cc /* node type definition */ @@ -247,6 +267,7 @@ void register_node_type_sh_tex_sky() node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_sky); /* Remove vector input for Nishita sky model. */ node_type_update(&ntype, file_ns::node_shader_update_sky); + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 1d6d16fa5e1..fc6a5ef72b6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -1312,7 +1312,7 @@ class VoronoiEdgeFunction : public fn::MultiFunction { } }; -static void sh_node_voronoi_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_voronoi_build_multi_function(NodeMultiFunctionBuilder &builder) { const NodeTexVoronoi &storage = node_storage(builder.node()); bool minowski = diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc index 68668fbe5be..ad24224dc7f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -204,8 +204,7 @@ class WaveFunction : public fn::MultiFunction { } }; -static void sh_node_wave_tex_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_wave_tex_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &node = builder.node(); NodeTexWave *tex = (NodeTexWave *)node.storage; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 0e2321b9cf9..6d4c491046b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -174,7 +174,7 @@ class WhiteNoiseFunction : public fn::MultiFunction { } }; -static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &node = builder.node(); builder.construct_and_set_matching_fn<WhiteNoiseFunction>((int)node.custom1); diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index a1e2565aec1..362cdf58052 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -24,11 +24,11 @@ static int gpu_shader_value(GPUMaterial *mat, return GPU_stack_link(mat, node, "set_value", in, out, link); } -static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_value_build_multi_function(NodeMultiFunctionBuilder &builder) { const bNodeSocket *bsocket = (bNodeSocket *)builder.node().outputs.first; const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value; - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value); + builder.construct_and_set_matching_fn<fn::CustomMF_Constant<float>>(value->value); } } // namespace blender::nodes::node_shader_value_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 49765f797c5..02a3552704e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -225,27 +225,25 @@ static void node_shader_update_vector_math(bNodeTree *ntree, bNode *node) } } -static const blender::fn::MultiFunction *get_multi_function(bNode &node) +static const fn::MultiFunction *get_multi_function(bNode &node) { - using blender::float3; - NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1); - const blender::fn::MultiFunction *multi_fn = nullptr; + const fn::MultiFunction *multi_fn = nullptr; - blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl3( - operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ - info.title_case_name.c_str(), function}; - multi_fn = &fn; - }); + try_dispatch_float_math_fl3_fl3_to_fl3(operation, + [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ + info.title_case_name.c_str(), function}; + multi_fn = &fn; + }); if (multi_fn != nullptr) { return multi_fn; } - blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3( - operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ + try_dispatch_float_math_fl3_fl3_fl3_to_fl3( + operation, [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ info.title_case_name.c_str(), function}; multi_fn = &fn; }); @@ -253,9 +251,9 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) return multi_fn; } - blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3( - operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ + try_dispatch_float_math_fl3_fl3_fl_to_fl3( + operation, [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ info.title_case_name.c_str(), function}; multi_fn = &fn; }); @@ -263,40 +261,38 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) return multi_fn; } - blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl( - operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{ - info.title_case_name.c_str(), function}; - multi_fn = &fn; - }); + try_dispatch_float_math_fl3_fl3_to_fl(operation, + [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float3, float3, float> fn{ + info.title_case_name.c_str(), function}; + multi_fn = &fn; + }); if (multi_fn != nullptr) { return multi_fn; } - blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3( - operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{ - info.title_case_name.c_str(), function}; - multi_fn = &fn; - }); + try_dispatch_float_math_fl3_fl_to_fl3(operation, + [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float3, float, float3> fn{ + info.title_case_name.c_str(), function}; + multi_fn = &fn; + }); if (multi_fn != nullptr) { return multi_fn; } - blender::nodes::try_dispatch_float_math_fl3_to_fl3( - operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name.c_str(), - function}; + try_dispatch_float_math_fl3_to_fl3( + operation, [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name.c_str(), function}; multi_fn = &fn; }); if (multi_fn != nullptr) { return multi_fn; } - blender::nodes::try_dispatch_float_math_fl3_to_fl( - operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name.c_str(), - function}; + try_dispatch_float_math_fl3_to_fl( + operation, [&](auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name.c_str(), function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -306,10 +302,9 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) return nullptr; } -static void sh_node_vector_math_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_vector_math_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index b8c44b527b5..a041492fb13 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -17,9 +17,15 @@ static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) b.is_function_node(); b.add_input<decl::Vector>(N_("Vector")).min(0.0f).max(1.0f).hide_value(); b.add_input<decl::Vector>(N_("Center")); - b.add_input<decl::Vector>(N_("Axis")).min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); + b.add_input<decl::Vector>(N_("Axis")) + .min(-1.0f) + .max(1.0f) + .default_value({0.0f, 0.0f, 1.0f}) + .make_available([](bNode &node) { node.custom1 = NODE_VECTOR_ROTATE_TYPE_AXIS; }); b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE); - b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).make_available([](bNode &node) { + node.custom1 = NODE_VECTOR_ROTATE_TYPE_EULER_XYZ; + }); b.add_output<decl::Vector>(N_("Vector")); } @@ -63,8 +69,6 @@ static int gpu_shader_vector_rotate(GPUMaterial *mat, return 0; } -using blender::float3; - static float3 sh_node_vector_rotate_around_axis(const float3 vector, const float3 center, const float3 axis, @@ -92,7 +96,7 @@ static float3 sh_node_vector_rotate_euler(const float3 vector, return result + center; } -static const blender::fn::MultiFunction *get_multi_function(bNode &node) +static const fn::MultiFunction *get_multi_function(bNode &node) { bool invert = node.custom2; const int mode = node.custom1; @@ -100,13 +104,13 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) switch (mode) { case NODE_VECTOR_ROTATE_TYPE_AXIS: { if (invert) { - static blender::fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } - static blender::fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; @@ -115,13 +119,13 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) case NODE_VECTOR_ROTATE_TYPE_AXIS_X: { float3 axis = float3(1.0f, 0.0f, 0.0f); if (invert) { - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; @@ -130,13 +134,13 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: { float3 axis = float3(0.0f, 1.0f, 0.0f); if (invert) { - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; @@ -145,13 +149,13 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: { float3 axis = float3(0.0f, 0.0f, 1.0f); if (invert) { - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; @@ -159,13 +163,13 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) } case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: { if (invert) { - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, true); }}; return &fn; } - static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, false); }}; @@ -177,10 +181,9 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) } } -static void sh_node_vector_rotate_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void sh_node_vector_rotate_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index ccd483083ef..9ceff9b84b6 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1050,7 +1050,8 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args) } PyDoc_STRVAR(bpy_bmesh_from_object_doc, - ".. method:: from_object(object, depsgraph, cage=False, face_normals=True)\n" + ".. method:: from_object(object, depsgraph, cage=False, face_normals=True, " + "vertex_normals=True)\n" "\n" " Initialize this bmesh from existing object data-block (only meshes are currently " "supported).\n" @@ -1060,10 +1061,12 @@ PyDoc_STRVAR(bpy_bmesh_from_object_doc, " :arg cage: Get the mesh as a deformed cage.\n" " :type cage: boolean\n" " :arg face_normals: Calculate face normals.\n" + " :arg vertex_normals: Calculate vertex normals.\n" " :type face_normals: boolean\n"); static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw) { - static const char *kwlist[] = {"object", "depsgraph", "cage", "face_normals", NULL}; + static const char *kwlist[] = { + "object", "depsgraph", "cage", "face_normals", "vertex_normals", NULL}; PyObject *py_object; PyObject *py_depsgraph; Object *ob, *ob_eval; @@ -1073,6 +1076,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject BMesh *bm; bool use_cage = false; bool use_fnorm = true; + bool use_vert_normal = true; const CustomData_MeshMasks data_masks = CD_MASK_BMESH; BPY_BM_CHECK_OBJ(self); @@ -1086,7 +1090,9 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject PyC_ParseBool, &use_cage, PyC_ParseBool, - &use_fnorm) || + &use_fnorm, + PyC_ParseBool, + &use_vert_normal) || !(ob = PyC_RNA_AsPointer(py_object, "Object")) || !(depsgraph = PyC_RNA_AsPointer(py_depsgraph, "Depsgraph"))) { return NULL; @@ -1137,6 +1143,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject me_eval, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, + .calc_vert_normal = use_vert_normal, })); if (need_free) { @@ -1148,7 +1155,8 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject PyDoc_STRVAR( bpy_bmesh_from_mesh_doc, - ".. method:: from_mesh(mesh, face_normals=True, use_shape_key=False, shape_key_index=0)\n" + ".. method:: from_mesh(mesh, face_normals=True, vertex_normals=True, use_shape_key=False, " + "shape_key_index=0)\n" "\n" " Initialize this bmesh from existing mesh datablock.\n" "\n" @@ -1168,11 +1176,13 @@ PyDoc_STRVAR( "mesh won't be added.\n"); static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject *kw) { - static const char *kwlist[] = {"mesh", "face_normals", "use_shape_key", "shape_key_index", NULL}; + static const char *kwlist[] = { + "mesh", "face_normals", "vertex_normals", "use_shape_key", "shape_key_index", NULL}; BMesh *bm; PyObject *py_mesh; Mesh *me; bool use_fnorm = true; + bool use_vert_normal = true; bool use_shape_key = false; int shape_key_index = 0; @@ -1186,6 +1196,8 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject * PyC_ParseBool, &use_fnorm, PyC_ParseBool, + &use_vert_normal, + PyC_ParseBool, &use_shape_key, &shape_key_index) || !(me = PyC_RNA_AsPointer(py_mesh, "Mesh"))) { @@ -1198,6 +1210,7 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject * me, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, + .calc_vert_normal = use_vert_normal, .use_shapekey = use_shape_key, .active_shapekey = shape_key_index + 1, })); diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 3fbfd1655c5..9e45105d105 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -396,41 +396,41 @@ static PyObject *py_blf_shadow_offset(PyObject *UNUSED(self), PyObject *args) } PyDoc_STRVAR(py_blf_load_doc, - ".. function:: load(filename)\n" + ".. function:: load(filepath)\n" "\n" " Load a new font.\n" "\n" - " :arg filename: the filename of the font.\n" - " :type filename: string\n" + " :arg filepath: the filepath of the font.\n" + " :type filepath: string\n" " :return: the new font's fontid or -1 if there was an error.\n" " :rtype: integer\n"); static PyObject *py_blf_load(PyObject *UNUSED(self), PyObject *args) { - const char *filename; + const char *filepath; - if (!PyArg_ParseTuple(args, "s:blf.load", &filename)) { + if (!PyArg_ParseTuple(args, "s:blf.load", &filepath)) { return NULL; } - return PyLong_FromLong(BLF_load(filename)); + return PyLong_FromLong(BLF_load(filepath)); } PyDoc_STRVAR(py_blf_unload_doc, - ".. function:: unload(filename)\n" + ".. function:: unload(filepath)\n" "\n" " Unload an existing font.\n" "\n" - " :arg filename: the filename of the font.\n" - " :type filename: string\n"); + " :arg filepath: the filepath of the font.\n" + " :type filepath: string\n"); static PyObject *py_blf_unload(PyObject *UNUSED(self), PyObject *args) { - const char *filename; + const char *filepath; - if (!PyArg_ParseTuple(args, "s:blf.unload", &filename)) { + if (!PyArg_ParseTuple(args, "s:blf.unload", &filepath)) { return NULL; } - BLF_unload(filename); + BLF_unload(filepath); Py_RETURN_NONE; } diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index e3ffd3cc823..fb69bb316c4 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -347,7 +347,7 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc, ".. function:: framebuffer_active_get(enable)\n" "\n" - " Return the active framefuffer in context.\n"); + " Return the active frame-buffer in context.\n"); static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self)) { GPUFrameBuffer *fb = GPU_framebuffer_active_get(); diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index a35f03f9872..f813a006c7e 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -224,6 +224,10 @@ if(WITH_IMAGE_TIFF) add_definitions(-DWITH_TIFF) endif() +if(WITH_WEBP) + add_definitions(-DWITH_WEBP) +endif() + if(WITH_INPUT_NDOF) add_definitions(-DWITH_INPUT_NDOF) endif() diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 85a477599cd..6f278c1c771 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -36,6 +36,11 @@ #include "../generic/py_capi_rna.h" #include "../generic/py_capi_utils.h" +/* Disabled duplicating strings because the array can still be freed and + * the strings from it referenced, for now we can't support dynamically + * created strings from Python. */ +// #define USE_ENUM_COPY_STRINGS + /* -------------------------------------------------------------------- */ /** \name Shared Enums & Doc-Strings * \{ */ @@ -1855,7 +1860,7 @@ static bool py_long_as_int(PyObject *py_long, int *r_int) return false; } -#if 0 +#ifdef USE_ENUM_COPY_STRINGS /* copies orig to buf, then sets orig to buf, returns copy length */ static size_t strswapbufcpy(char *buf, const char **orig) { @@ -1897,8 +1902,10 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, PyObject *item; const Py_ssize_t seq_len = PySequence_Fast_GET_SIZE(seq_fast); PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast); - Py_ssize_t totbuf = 0; int i; +#ifdef USE_ENUM_COPY_STRINGS + Py_ssize_t totbuf = 0; +#endif short default_used = 0; const char *default_str_cmp = NULL; int default_int_cmp = 0; @@ -1988,8 +1995,10 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, items[i] = tmp; - /* calculate combine string length */ +#ifdef USE_ENUM_COPY_STRINGS + /* Calculate combine string length. */ totbuf += id_str_size + name_str_size + desc_str_size + 3; /* 3 is for '\0's */ +#endif } else if (item == Py_None) { /* Only set since the rest is cleared. */ @@ -2034,13 +2043,9 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, } } - /* disabled duplicating strings because the array can still be freed and - * the strings from it referenced, for now we can't support dynamically - * created strings from python. */ -#if 0 - /* this would all work perfectly _but_ the python strings may be freed - * immediately after use, so we need to duplicate them, ugh. - * annoying because it works most of the time without this. */ +#ifdef USE_ENUM_COPY_STRINGS + /* This would all work perfectly _but_ the python strings may be freed immediately after use, + * so we need to duplicate them, ugh. annoying because it works most of the time without this. */ { EnumPropertyItem *items_dup = MEM_mallocN((sizeof(EnumPropertyItem) * (seq_len + 1)) + (sizeof(char) * totbuf), diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h index 578df1858c9..e56e7b8d2e4 100644 --- a/source/blender/render/RE_engine.h +++ b/source/blender/render/RE_engine.h @@ -169,10 +169,10 @@ void RE_engine_free(RenderEngine *engine); * x/y offsets are only used on a partial copy when dimensions don't match. */ void RE_layer_load_from_file( - struct RenderLayer *layer, struct ReportList *reports, const char *filename, int x, int y); + struct RenderLayer *layer, struct ReportList *reports, const char *filepath, int x, int y); void RE_result_load_from_file(struct RenderResult *result, struct ReportList *reports, - const char *filename); + const char *filepath); struct RenderResult *RE_engine_begin_result( RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname); @@ -253,6 +253,7 @@ void RE_engine_render_context_disable(struct RenderEngine *engine); /* Engine Types */ void RE_engines_init(void); +void RE_engines_init_experimental(void); void RE_engines_exit(void); void RE_engines_register(RenderEngineType *render_type); diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index f532c705534..0a8668221ad 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -62,8 +62,8 @@ typedef struct RenderView { typedef struct RenderPass { struct RenderPass *next, *prev; int channels; - char name[64]; /* amount defined in openexr_multi.h */ - char chan_id[8]; /* amount defined in openexr_multi.h */ + char name[64]; /* amount defined in IMB_openexr.h */ + char chan_id[8]; /* amount defined in IMB_openexr.h */ float *rect; int rectx, recty; @@ -123,8 +123,8 @@ typedef struct RenderResult { ListBase views; /* RenderView */ /* allowing live updates: */ - volatile rcti renrect; - volatile RenderLayer *renlay; + rcti renrect; + RenderLayer *renlay; /* for render results in Image, verify validity for sequences */ int framenr; @@ -242,15 +242,23 @@ void RE_AcquiredResultGet32(struct Render *re, unsigned int *rect, int view_id); +void RE_render_result_full_channel_name(char *fullname, + const char *layname, + const char *passname, + const char *viewname, + const char *chan_id, + const int channel); + +struct ImBuf *RE_render_result_rect_to_ibuf(struct RenderResult *rr, + const struct ImageFormatData *imf, + const float dither, + const int view_id); void RE_render_result_rect_from_ibuf(struct RenderResult *rr, - struct RenderData *rd, - struct ImBuf *ibuf, - int view_id); + const struct ImBuf *ibuf, + const int view_id); struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name); -float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, - const char *name, - const char *viewname); +float *RE_RenderLayerGetPass(struct RenderLayer *rl, const char *name, const char *viewname); bool RE_HasSingleLayer(struct Render *re); @@ -307,11 +315,6 @@ void RE_GetViewPlane(struct Render *re, rctf *r_viewplane, rcti *r_disprect); */ void RE_init_threadcount(Render *re); -bool RE_WriteRenderViewsImage(struct ReportList *reports, - struct RenderResult *rr, - struct Scene *scene, - bool stamp, - char *name); bool RE_WriteRenderViewsMovie(struct ReportList *reports, struct RenderResult *rr, struct Scene *scene, @@ -336,6 +339,7 @@ void RE_RenderFrame(struct Render *re, struct ViewLayer *single_layer, struct Object *camera_override, int frame, + float subframe, bool write_still); /** * A version of #RE_RenderFrame that saves images to disk. @@ -373,16 +377,7 @@ void RE_PreviewRender(struct Render *re, struct Main *bmain, struct Scene *scene * Only the temp file! */ bool RE_ReadRenderResult(struct Scene *scene, struct Scene *scenode); -/** - * Called from the UI and render pipeline, to save multi-layer and multi-view - * images, optionally isolating a specific, view, layer or RGBA/Z pass. - */ -bool RE_WriteRenderResult(struct ReportList *reports, - RenderResult *rr, - const char *filename, - struct ImageFormatData *imf, - const char *view, - int layer); + struct RenderResult *RE_MultilayerConvert( void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); @@ -399,7 +394,7 @@ void RE_display_clear_cb(struct Render *re, void (*f)(void *handle, RenderResult *rr)); void RE_display_update_cb(struct Render *re, void *handle, - void (*f)(void *handle, RenderResult *rr, volatile struct rcti *rect)); + void (*f)(void *handle, RenderResult *rr, struct rcti *rect)); void RE_stats_draw_cb(struct Render *re, void *handle, void (*f)(void *handle, RenderStats *rs)); void RE_progress_cb(struct Render *re, void *handle, void (*f)(void *handle, float)); void RE_draw_lock_cb(struct Render *re, void *handle, void (*f)(void *handle, bool lock)); @@ -428,13 +423,13 @@ int RE_seq_render_active(struct Scene *scene, struct RenderData *rd); bool RE_layers_have_name(struct RenderResult *result); bool RE_passes_have_name(struct RenderLayer *rl); -struct RenderPass *RE_pass_find_by_name(volatile struct RenderLayer *rl, +struct RenderPass *RE_pass_find_by_name(struct RenderLayer *rl, const char *name, const char *viewname); /** * Only provided for API compatibility, don't use this in new code! */ -struct RenderPass *RE_pass_find_by_type(volatile struct RenderLayer *rl, +struct RenderPass *RE_pass_find_by_type(struct RenderLayer *rl, int passtype, const char *viewname); @@ -464,9 +459,9 @@ bool RE_allow_render_generic_object(struct Object *ob); /******* defined in render_result.c *********/ -bool RE_HasCombinedLayer(RenderResult *res); -bool RE_HasFloatPixels(RenderResult *res); -bool RE_RenderResult_is_stereo(RenderResult *res); +bool RE_HasCombinedLayer(const RenderResult *res); +bool RE_HasFloatPixels(const RenderResult *res); +bool RE_RenderResult_is_stereo(const RenderResult *res); struct RenderView *RE_RenderViewGetById(struct RenderResult *rr, int view_id); struct RenderView *RE_RenderViewGetByName(struct RenderResult *rr, const char *viewname); diff --git a/source/blender/render/RE_texture_margin.h b/source/blender/render/RE_texture_margin.h index d297ca19404..85bd06b9940 100644 --- a/source/blender/render/RE_texture_margin.h +++ b/source/blender/render/RE_texture_margin.h @@ -23,7 +23,7 @@ struct Mesh; * \param mask: pixels with a mask value of 1 are not written to. * \param margin: the size of the margin in pixels. * \param me: the mesh to use the polygons of. - * \param mloopuv: the uv data to use. + * \param uv_layer: The UV layer to use. */ void RE_generate_texturemargin_adjacentfaces( struct ImBuf *ibuf, char *mask, const int margin, struct Mesh const *me, char const *uv_layer); diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index ae5ffdfc232..3a7ac22dc1f 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -59,6 +59,11 @@ void RE_engines_init(void) DRW_engines_register(); } +void RE_engines_init_experimental() +{ + DRW_engines_register_experimental(); +} + void RE_engines_exit(void) { RenderEngineType *type, *next; diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index a5c13c26590..c573d4feed1 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -66,7 +66,7 @@ typedef struct { MLoopUV *mloopuv; const MLoopTri *mlooptri; float *pvtangent; - const float *precomputed_normals; + const float (*precomputed_normals)[3]; int w, h; int tri_index; DerivedMesh *lores_dm, *hires_dm; @@ -106,26 +106,26 @@ typedef struct BakeImBufuserData { } BakeImBufuserData; static void multiresbake_get_normal(const MResolvePixelData *data, - float norm[], const int tri_num, - const int vert_index) + const int vert_index, + float r_normal[3]) { const int poly_index = data->mlooptri[tri_num].poly; const MPoly *mp = &data->mpoly[poly_index]; const bool smoothnormal = (mp->flag & ME_SMOOTH) != 0; - if (!smoothnormal) { /* flat */ + if (smoothnormal) { + const int vi = data->mloop[data->mlooptri[tri_num].tri[vert_index]].v; + copy_v3_v3(r_normal, data->vert_normals[vi]); + } + else { if (data->precomputed_normals) { - copy_v3_v3(norm, &data->precomputed_normals[poly_index]); + copy_v3_v3(r_normal, data->precomputed_normals[poly_index]); } else { - BKE_mesh_calc_poly_normal(mp, &data->mloop[mp->loopstart], data->mvert, norm); + BKE_mesh_calc_poly_normal(mp, &data->mloop[mp->loopstart], data->mvert, r_normal); } } - else { - const int vi = data->mloop[data->mlooptri[tri_num].tri[vert_index]].v; - copy_v3_v3(norm, data->vert_normals[vi]); - } } static void init_bake_rast(MBakeRast *bake_rast, @@ -160,9 +160,9 @@ static void flush_pixel(const MResolvePixelData *data, const int x, const int y) st1 = data->mloopuv[data->mlooptri[data->tri_index].tri[1]].uv; st2 = data->mloopuv[data->mlooptri[data->tri_index].tri[2]].uv; - multiresbake_get_normal(data, no0, data->tri_index, 0); /* can optimize these 3 into one call */ - multiresbake_get_normal(data, no1, data->tri_index, 1); - multiresbake_get_normal(data, no2, data->tri_index, 2); + multiresbake_get_normal(data, data->tri_index, 0, no0); /* can optimize these 3 into one call */ + multiresbake_get_normal(data, data->tri_index, 1, no1); + multiresbake_get_normal(data, data->tri_index, 2, no2); resolve_tri_uv_v2(fUV, st, st0, st1, st2); @@ -542,7 +542,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, handle->data.mlooptri = mlooptri; handle->data.mloop = mloop; handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = (float *)poly_normals; /* don't strictly need this */ + handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ handle->data.w = ibuf->x; handle->data.h = ibuf->y; handle->data.lores_dm = dm; diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index aa006713755..cf400bdfc77 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -44,6 +44,8 @@ #include "BKE_context.h" /* XXX needed by wm_window.h */ #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" +#include "BKE_image_save.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_remap.h" @@ -166,7 +168,7 @@ static void result_nothing(void *UNUSED(arg), RenderResult *UNUSED(rr)) } static void result_rcti_nothing(void *UNUSED(arg), RenderResult *UNUSED(rr), - volatile struct rcti *UNUSED(rect)) + struct rcti *UNUSED(rect)) { } static void current_scene_nothing(void *UNUSED(arg), Scene *UNUSED(scene)) @@ -222,47 +224,12 @@ static void stats_background(void *UNUSED(arg), RenderStats *rs) fflush(stdout); } -static void render_print_save_message(ReportList *reports, const char *name, int ok, int err) -{ - if (ok) { - /* no need to report, just some helpful console info */ - printf("Saved: '%s'\n", name); - } - else { - /* report on error since users will want to know what failed */ - BKE_reportf(reports, RPT_ERROR, "Render error (%s) cannot save: '%s'", strerror(err), name); - } -} - -static int render_imbuf_write_stamp_test(ReportList *reports, - Scene *scene, - struct RenderResult *rr, - ImBuf *ibuf, - const char *name, - const ImageFormatData *imf, - bool stamp) -{ - int ok; - - if (stamp) { - /* writes the name of the individual cameras */ - ok = BKE_imbuf_write_stamp(scene, rr, ibuf, name, imf); - } - else { - ok = BKE_imbuf_write(ibuf, name, imf); - } - - render_print_save_message(reports, name, ok, errno); - - return ok; -} - void RE_FreeRenderResult(RenderResult *rr) { render_result_free(rr); } -float *RE_RenderLayerGetPass(volatile RenderLayer *rl, const char *name, const char *viewname) +float *RE_RenderLayerGetPass(RenderLayer *rl, const char *name, const char *viewname) { RenderPass *rpass = RE_pass_find_by_name(rl, name, viewname); return rpass ? rpass->rect : NULL; @@ -711,11 +678,9 @@ static void re_init_resolution(Render *re, Render *source, int winx, int winy, r re->winx = winx; re->winy = winy; if (source && (source->r.mode & R_BORDER)) { - /* eeh, doesn't seem original bordered disprect is storing anywhere - * after insertion on black happening in do_render_engine(), - * so for now simply re-calculate disprect using border from source - * renderer (sergey) - */ + /* NOTE(@sergey): doesn't seem original bordered `disprect` is storing anywhere + * after insertion on black happening in #do_render_engine(), + * so for now simply re-calculate `disprect` using border from source renderer. */ re->disprect.xmin = source->r.border.xmin * winx; re->disprect.xmax = source->r.border.xmax * winx; @@ -890,7 +855,7 @@ void RE_display_clear_cb(Render *re, void *handle, void (*f)(void *handle, Rende } void RE_display_update_cb(Render *re, void *handle, - void (*f)(void *handle, RenderResult *rr, volatile rcti *rect)) + void (*f)(void *handle, RenderResult *rr, rcti *rect)) { re->display_update = f; re->duh = handle; @@ -1247,14 +1212,8 @@ static void do_render_compositor(Render *re) RenderView *rv; for (rv = re->result->views.first; rv; rv = rv->next) { - ntreeCompositExecTree(re->pipeline_scene_eval, - ntree, - &re->r, - true, - G.background == 0, - &re->scene->view_settings, - &re->scene->display_settings, - rv->name); + ntreeCompositExecTree( + re->pipeline_scene_eval, ntree, &re->r, true, G.background == 0, rv->name); } ntree->stats_draw = NULL; @@ -1386,7 +1345,7 @@ static void do_render_sequencer(Render *re) if (ibuf_arr[view_id]) { /* copy ibuf into combined pixel rect */ - RE_render_result_rect_from_ibuf(rr, &re->r, ibuf_arr[view_id], view_id); + RE_render_result_rect_from_ibuf(rr, ibuf_arr[view_id], view_id); if (ibuf_arr[view_id]->metadata && (re->r.stamp & R_STAMP_STRIPMETA)) { /* ensure render stamp info first */ @@ -1844,7 +1803,8 @@ void RE_RenderFrame(Render *re, Scene *scene, ViewLayer *single_layer, Object *camera_override, - int frame, + const int frame, + const float subframe, const bool write_still) { render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_INIT); @@ -1854,6 +1814,7 @@ void RE_RenderFrame(Render *re, G.is_rendering = true; scene->r.cfra = frame; + scene->r.subframe = subframe; if (render_init_from_main(re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) { const RenderData rd = scene->r; @@ -1978,129 +1939,6 @@ void RE_RenderFreestyleExternal(Render *re) /** \name Read/Write Render Result (Images & Movies) * \{ */ -bool RE_WriteRenderViewsImage( - ReportList *reports, RenderResult *rr, Scene *scene, const bool stamp, char *name) -{ - bool ok = true; - RenderData *rd = &scene->r; - - if (!rr) { - return false; - } - - bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; - bool is_exr_rr = ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && - RE_HasFloatPixels(rr); - - if (rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) { - ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, NULL, -1); - render_print_save_message(reports, name, ok, errno); - } - - /* mono, legacy code */ - else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { - RenderView *rv; - int view_id; - char filepath[FILE_MAX]; - - BLI_strncpy(filepath, name, sizeof(filepath)); - - for (view_id = 0, rv = rr->views.first; rv; rv = rv->next, view_id++) { - if (!is_mono) { - BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name); - } - - if (is_exr_rr) { - ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, rv->name, -1); - render_print_save_message(reports, name, ok, errno); - - /* optional preview images for exr */ - if (ok && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { - ImageFormatData imf = rd->im_format; - imf.imtype = R_IMF_IMTYPE_JPEG90; - - if (BLI_path_extension_check(name, ".exr")) { - name[strlen(name) - 4] = 0; - } - BKE_image_path_ensure_ext_from_imformat(name, &imf); - - ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); - ibuf->planes = 24; - IMB_colormanagement_imbuf_for_write( - ibuf, true, false, &scene->view_settings, &scene->display_settings, &imf); - - ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &imf, stamp); - - IMB_freeImBuf(ibuf); - } - } - else { - ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); - - IMB_colormanagement_imbuf_for_write( - ibuf, true, false, &scene->view_settings, &scene->display_settings, &rd->im_format); - - ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &rd->im_format, stamp); - - /* imbuf knows which rects are not part of ibuf */ - IMB_freeImBuf(ibuf); - } - } - } - else { /* R_IMF_VIEWS_STEREO_3D */ - BLI_assert(scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D); - - if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - printf("Stereo 3D not supported for MultiLayer image: %s\n", name); - } - else { - ImBuf *ibuf_arr[3] = {NULL}; - const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - int i; - - for (i = 0; i < 2; i++) { - int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); - ibuf_arr[i] = render_result_rect_to_ibuf(rr, rd, view_id); - IMB_colormanagement_imbuf_for_write(ibuf_arr[i], - true, - false, - &scene->view_settings, - &scene->display_settings, - &scene->r.im_format); - IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); - } - - ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); - - ok = render_imbuf_write_stamp_test( - reports, scene, rr, ibuf_arr[2], name, &rd->im_format, stamp); - - /* optional preview images for exr */ - if (ok && is_exr_rr && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { - ImageFormatData imf = rd->im_format; - imf.imtype = R_IMF_IMTYPE_JPEG90; - - if (BLI_path_extension_check(name, ".exr")) { - name[strlen(name) - 4] = 0; - } - - BKE_image_path_ensure_ext_from_imformat(name, &imf); - ibuf_arr[2]->planes = 24; - - ok = render_imbuf_write_stamp_test( - reports, scene, rr, ibuf_arr[2], name, &rd->im_format, stamp); - } - - /* imbuf knows which rects are not part of ibuf */ - for (i = 0; i < 3; i++) { - IMB_freeImBuf(ibuf_arr[i]); - } - } - } - - return ok; -} - bool RE_WriteRenderViewsMovie(ReportList *reports, RenderResult *rr, Scene *scene, @@ -2110,23 +1948,25 @@ bool RE_WriteRenderViewsMovie(ReportList *reports, const int totvideos, bool preview) { - bool is_mono; bool ok = true; if (!rr) { return false; } - is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; + ImageFormatData image_format; + BKE_image_format_init_for_write(&image_format, scene, NULL); + + const bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; + const float dither = scene->r.dither_intensity; - if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { + if (is_mono || (image_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { int view_id; for (view_id = 0; view_id < totvideos; view_id++) { const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); - ImBuf *ibuf = render_result_rect_to_ibuf(rr, &scene->r, view_id); + ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &rd->im_format, dither, view_id); - IMB_colormanagement_imbuf_for_write( - ibuf, true, false, &scene->view_settings, &scene->display_settings, &scene->r.im_format); + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &image_format); ok &= mh->append_movie(movie_ctx_arr[view_id], rd, @@ -2148,21 +1988,16 @@ bool RE_WriteRenderViewsMovie(ReportList *reports, ImBuf *ibuf_arr[3] = {NULL}; int i; - BLI_assert((totvideos == 1) && (scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D)); + BLI_assert((totvideos == 1) && (image_format.views_format == R_IMF_VIEWS_STEREO_3D)); for (i = 0; i < 2; i++) { int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); - ibuf_arr[i] = render_result_rect_to_ibuf(rr, &scene->r, view_id); - - IMB_colormanagement_imbuf_for_write(ibuf_arr[i], - true, - false, - &scene->view_settings, - &scene->display_settings, - &scene->r.im_format); + ibuf_arr[i] = RE_render_result_rect_to_ibuf(rr, &rd->im_format, dither, view_id); + + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &image_format); } - ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); + ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]); ok = mh->append_movie(movie_ctx_arr[0], rd, @@ -2180,6 +2015,8 @@ bool RE_WriteRenderViewsMovie(ReportList *reports, } } + BKE_image_format_free(&image_format); + return ok; } @@ -2224,7 +2061,7 @@ static int do_write_image_or_movie(Render *re, } /* write images as individual images or stereo */ - ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, true, name); + ok = BKE_image_render_write(re->reports, &rres, scene, true, name); } RE_ReleaseResultImageViews(re, &rres); @@ -2305,7 +2142,8 @@ void RE_RenderAnim(Render *re, const RenderData rd = scene->r; bMovieHandle *mh = NULL; - const int cfrao = rd.cfra; + const int cfra_old = rd.cfra; + const float subframe_old = rd.subframe; int nfra, totrendered = 0, totskipped = 0; const int totvideos = BKE_scene_multiview_num_videos_get(&rd); const bool is_movie = BKE_imtype_is_movie(rd.im_format.imtype); @@ -2373,6 +2211,7 @@ void RE_RenderAnim(Render *re, re->flag |= R_ANIMATION; { + scene->r.subframe = 0.0f; for (nfra = sfra, scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) { char name[FILE_MAX]; @@ -2481,6 +2320,7 @@ void RE_RenderAnim(Render *re, } re->r.cfra = scene->r.cfra; /* weak.... */ + re->r.subframe = scene->r.subframe; /* run callbacks before rendering, before the scene is updated */ render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_PRE); @@ -2549,7 +2389,8 @@ void RE_RenderAnim(Render *re, BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite"); } - scene->r.cfra = cfrao; + scene->r.cfra = cfra_old; + scene->r.subframe = subframe_old; re->flag &= ~R_ANIMATION; @@ -2639,10 +2480,10 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode) } void RE_layer_load_from_file( - RenderLayer *layer, ReportList *reports, const char *filename, int x, int y) + RenderLayer *layer, ReportList *reports, const char *filepath, int x, int y) { /* OCIO_TODO: assume layer was saved in default color space */ - ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL); + ImBuf *ibuf = IMB_loadiffname(filepath, IB_rect, NULL); RenderPass *rpass = NULL; /* multiview: since the API takes no 'view', we use the first combined pass found */ @@ -2657,7 +2498,7 @@ void RE_layer_load_from_file( RPT_ERROR, "%s: no Combined pass found in the render layer '%s'", __func__, - filename); + filepath); } if (ibuf && (ibuf->rect || ibuf->rect_float)) { @@ -2686,7 +2527,7 @@ void RE_layer_load_from_file( } else { BKE_reportf( - reports, RPT_ERROR, "%s: failed to allocate clip buffer '%s'", __func__, filename); + reports, RPT_ERROR, "%s: failed to allocate clip buffer '%s'", __func__, filepath); } } else { @@ -2694,21 +2535,21 @@ void RE_layer_load_from_file( RPT_ERROR, "%s: incorrect dimensions for partial copy '%s'", __func__, - filename); + filepath); } } IMB_freeImBuf(ibuf); } else { - BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); + BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filepath); } } -void RE_result_load_from_file(RenderResult *result, ReportList *reports, const char *filename) +void RE_result_load_from_file(RenderResult *result, ReportList *reports, const char *filepath) { - if (!render_result_exr_file_read_path(result, NULL, filename)) { - BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); + if (!render_result_exr_file_read_path(result, NULL, filepath)) { + BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filepath); return; } } @@ -2737,7 +2578,7 @@ bool RE_passes_have_name(struct RenderLayer *rl) return false; } -RenderPass *RE_pass_find_by_name(volatile RenderLayer *rl, const char *name, const char *viewname) +RenderPass *RE_pass_find_by_name(RenderLayer *rl, const char *name, const char *viewname) { RenderPass *rp = NULL; @@ -2754,7 +2595,7 @@ RenderPass *RE_pass_find_by_name(volatile RenderLayer *rl, const char *name, con return rp; } -RenderPass *RE_pass_find_by_type(volatile RenderLayer *rl, int passtype, const char *viewname) +RenderPass *RE_pass_find_by_type(RenderLayer *rl, int passtype, const char *viewname) { #define CHECK_PASS(NAME) \ if (passtype == SCE_PASS_##NAME) { \ diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index a3ba8e2f02b..ea7fa961f0d 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -26,14 +26,15 @@ #include "BKE_camera.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_format.h" +#include "BKE_image_save.h" #include "BKE_report.h" #include "BKE_scene.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" - -#include "intern/openexr/openexr_multi.h" +#include "IMB_openexr.h" #include "RE_engine.h" @@ -161,37 +162,6 @@ void render_result_views_shallowdelete(RenderResult *rr) } } -static char *set_pass_name(char *outname, const char *name, int channel, const char *chan_id) -{ - const char *strings[2]; - int strings_len = 0; - strings[strings_len++] = name; - char token[2]; - if (channel >= 0) { - ARRAY_SET_ITEMS(token, chan_id[channel], '\0'); - strings[strings_len++] = token; - } - BLI_string_join_array_by_sep_char(outname, EXR_PASS_MAXNAME, '.', strings, strings_len); - return outname; -} - -static void set_pass_full_name( - char *fullname, const char *name, int channel, const char *view, const char *chan_id) -{ - const char *strings[3]; - int strings_len = 0; - strings[strings_len++] = name; - if (view && view[0]) { - strings[strings_len++] = view; - } - char token[2]; - if (channel >= 0) { - ARRAY_SET_ITEMS(token, chan_id[channel], '\0'); - strings[strings_len++] = token; - } - BLI_string_join_array_by_sep_char(fullname, EXR_PASS_MAXNAME, '.', strings, strings_len); -} - /********************************** New **************************************/ static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) @@ -237,20 +207,15 @@ RenderPass *render_layer_add_pass(RenderResult *rr, BLI_strncpy(rpass->name, name, sizeof(rpass->name)); BLI_strncpy(rpass->chan_id, chan_id, sizeof(rpass->chan_id)); BLI_strncpy(rpass->view, viewname, sizeof(rpass->view)); - set_pass_full_name(rpass->fullname, rpass->name, -1, rpass->view, rpass->chan_id); + RE_render_result_full_channel_name( + rpass->fullname, NULL, rpass->name, rpass->view, rpass->chan_id, -1); if (rl->exrhandle) { int a; for (a = 0; a < channels; a++) { char passname[EXR_PASS_MAXNAME]; - IMB_exr_add_channel(rl->exrhandle, - rl->name, - set_pass_name(passname, rpass->name, a, rpass->chan_id), - viewname, - 0, - 0, - NULL, - false); + RE_render_result_full_channel_name(passname, NULL, rpass->name, NULL, rpass->chan_id, a); + IMB_exr_add_channel(rl->exrhandle, rl->name, passname, viewname, 0, 0, NULL, false); } } @@ -449,6 +414,11 @@ RenderResult *render_result_new(Render *re, rr->xof = re->disprect.xmin + BLI_rcti_cent_x(&re->disprect) - (re->winx / 2); rr->yof = re->disprect.ymin + BLI_rcti_cent_y(&re->disprect) - (re->winy / 2); + /* Preview does not support deferred render result allocation. */ + if (re->r.scemode & R_BUTS_PREVIEW) { + render_result_passes_allocated_ensure(rr); + } + return rr; } @@ -543,6 +513,36 @@ void RE_create_render_pass(RenderResult *rr, } } +void RE_render_result_full_channel_name(char *fullname, + const char *layname, + const char *passname, + const char *viewname, + const char *chan_id, + const int channel) +{ + /* OpenEXR compatible full channel name. */ + const char *strings[4]; + int strings_len = 0; + + if (layname && layname[0]) { + strings[strings_len++] = layname; + } + if (passname && passname[0]) { + strings[strings_len++] = passname; + } + if (viewname && viewname[0]) { + strings[strings_len++] = viewname; + } + + char token[2]; + if (channel >= 0) { + ARRAY_SET_ITEMS(token, chan_id[channel], '\0'); + strings[strings_len++] = token; + } + + BLI_string_join_array_by_sep_char(fullname, EXR_PASS_MAXNAME, '.', strings, strings_len); +} + static int passtype_from_name(const char *name) { const char delim[] = {'.', '\0'}; @@ -619,7 +619,7 @@ static void ml_addpass_cb(void *base, rpass->rect = rect; BLI_strncpy(rpass->name, name, EXR_PASS_MAXNAME); BLI_strncpy(rpass->view, view, sizeof(rpass->view)); - set_pass_full_name(rpass->fullname, name, -1, view, rpass->chan_id); + RE_render_result_full_channel_name(rpass->fullname, NULL, name, view, rpass->chan_id, -1); if (view[0] != '\0') { rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name)); @@ -663,7 +663,7 @@ static void *ml_addview_cb(void *base, const char *str) static int order_render_passes(const void *a, const void *b) { - // 1 if a is after b + /* 1 if `a` is after `b`. */ RenderPass *rpa = (RenderPass *)a; RenderPass *rpb = (RenderPass *)b; unsigned int passtype_a = passtype_from_name(rpa->name); @@ -785,12 +785,6 @@ void render_result_views_new(RenderResult *rr, const RenderData *rd) } } -bool render_result_has_views(const RenderResult *rr) -{ - const RenderView *rv = rr->views.first; - return (rv && (rv->next || rv->name[0])); -} - /*********************************** Merge ***********************************/ static void do_merge_tile( @@ -845,162 +839,6 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) } } -bool RE_WriteRenderResult(ReportList *reports, - RenderResult *rr, - const char *filename, - ImageFormatData *imf, - const char *view, - int layer) -{ - void *exrhandle = IMB_exr_get_handle(); - const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16); - const bool multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR); - const bool write_z = !multi_layer && (imf && (imf->flag & R_IMF_FLAG_ZBUF)); - - /* Write first layer if not multilayer and no layer was specified. */ - if (!multi_layer && layer == -1) { - layer = 0; - } - - /* First add views since IMB_exr_add_channel checks number of views. */ - if (render_result_has_views(rr)) { - LISTBASE_FOREACH (RenderView *, rview, &rr->views) { - if (!view || STREQ(view, rview->name)) { - IMB_exr_add_view(exrhandle, rview->name); - } - } - } - - /* Compositing result. */ - if (rr->have_combined) { - LISTBASE_FOREACH (RenderView *, rview, &rr->views) { - if (!rview->rectf) { - continue; - } - - const char *viewname = rview->name; - if (view) { - if (!STREQ(view, viewname)) { - continue; - } - - viewname = ""; - } - - /* Skip compositing if only a single other layer is requested. */ - if (!multi_layer && layer != 0) { - continue; - } - - for (int a = 0; a < 4; a++) { - char passname[EXR_PASS_MAXNAME]; - char layname[EXR_PASS_MAXNAME]; - const char *chan_id = "RGBA"; - - if (multi_layer) { - set_pass_name(passname, "Combined", a, chan_id); - BLI_strncpy(layname, "Composite", sizeof(layname)); - } - else { - passname[0] = chan_id[a]; - passname[1] = '\0'; - layname[0] = '\0'; - } - - IMB_exr_add_channel(exrhandle, - layname, - passname, - viewname, - 4, - 4 * rr->rectx, - rview->rectf + a, - half_float); - } - - if (write_z && rview->rectz) { - const char *layname = (multi_layer) ? "Composite" : ""; - IMB_exr_add_channel(exrhandle, layname, "Z", viewname, 1, rr->rectx, rview->rectz, false); - } - } - } - - /* Other render layers. */ - int nr = (rr->have_combined) ? 1 : 0; - for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next, nr++) { - /* Skip other render layers if requested. */ - if (!multi_layer && nr != layer) { - continue; - } - - LISTBASE_FOREACH (RenderPass *, rp, &rl->passes) { - /* Skip non-RGBA and Z passes if not using multi layer. */ - if (!multi_layer && !(STREQ(rp->name, RE_PASSNAME_COMBINED) || STREQ(rp->name, "") || - (STREQ(rp->name, RE_PASSNAME_Z) && write_z))) { - continue; - } - - /* Skip pass if it does not match the requested view(s). */ - const char *viewname = rp->view; - if (view) { - if (!STREQ(view, viewname)) { - continue; - } - - viewname = ""; - } - - /* We only store RGBA passes as half float, for - * others precision loss can be problematic. */ - bool pass_half_float = half_float && - (STR_ELEM(rp->chan_id, "RGB", "RGBA", "R", "G", "B", "A")); - - for (int a = 0; a < rp->channels; a++) { - /* Save Combined as RGBA if single layer save. */ - char passname[EXR_PASS_MAXNAME]; - char layname[EXR_PASS_MAXNAME]; - - if (multi_layer) { - set_pass_name(passname, rp->name, a, rp->chan_id); - BLI_strncpy(layname, rl->name, sizeof(layname)); - } - else { - passname[0] = rp->chan_id[a]; - passname[1] = '\0'; - layname[0] = '\0'; - } - - IMB_exr_add_channel(exrhandle, - layname, - passname, - viewname, - rp->channels, - rp->channels * rr->rectx, - rp->rect + a, - pass_half_float); - } - } - } - - errno = 0; - - BLI_make_existing_file(filename); - - int compress = (imf ? imf->exr_codec : 0); - bool success = IMB_exr_begin_write( - exrhandle, filename, rr->rectx, rr->recty, compress, rr->stamp_data); - if (success) { - IMB_exr_write_channels(exrhandle); - } - else { - /* TODO: get the error from openexr's exception. */ - BKE_reportf( - reports, RPT_ERROR, "Error writing render result, %s (see console)", strerror(errno)); - } - - IMB_exr_close(exrhandle); - return success; -} - /**************************** Single Layer Rendering *************************/ void render_result_single_layer_begin(Render *re) @@ -1096,12 +934,14 @@ int render_result_exr_file_read_path(RenderResult *rr, char fullname[EXR_PASS_MAXNAME]; for (a = 0; a < xstride; a++) { - set_pass_full_name(fullname, rpass->name, a, rpass->view, rpass->chan_id); + RE_render_result_full_channel_name( + fullname, NULL, rpass->name, rpass->view, rpass->chan_id, a); IMB_exr_set_channel( exrhandle, rl->name, fullname, xstride, xstride * rectx, rpass->rect + a); } - set_pass_full_name(rpass->fullname, rpass->name, -1, rpass->view, rpass->chan_id); + RE_render_result_full_channel_name( + rpass->fullname, NULL, rpass->name, rpass->view, rpass->chan_id, -1); } } @@ -1153,7 +993,7 @@ void render_result_exr_file_cache_write(Render *re) render_result_exr_file_cache_path(re->scene, root, str); printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str); - RE_WriteRenderResult(NULL, rr, str, NULL, NULL, -1); + BKE_image_render_write_exr(NULL, rr, str, NULL, true, NULL, -1); } bool render_result_exr_file_cache_read(Render *re) @@ -1189,9 +1029,12 @@ bool render_result_exr_file_cache_read(Render *re) /*************************** Combined Pixel Rect *****************************/ -ImBuf *render_result_rect_to_ibuf(RenderResult *rr, const RenderData *rd, const int view_id) +ImBuf *RE_render_result_rect_to_ibuf(RenderResult *rr, + const ImageFormatData *imf, + const float dither, + const int view_id) { - ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, rd->im_format.planes, 0); + ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, imf->planes, 0); RenderView *rv = RE_RenderViewGetById(rr, view_id); /* if not exists, BKE_imbuf_write makes one */ @@ -1200,15 +1043,15 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, const RenderData *rd, const ibuf->zbuf_float = rv->rectz; /* float factor for random dither, imbuf takes care of it */ - ibuf->dither = rd->dither_intensity; + ibuf->dither = dither; /* prepare to gamma correct to sRGB color space * note that sequence editor can generate 8bpc render buffers */ if (ibuf->rect) { - if (BKE_imtype_valid_depths(rd->im_format.imtype) & + if (BKE_imtype_valid_depths(imf->imtype) & (R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_24 | R_IMF_CHAN_DEPTH_32)) { - if (rd->im_format.depth == R_IMF_CHAN_DEPTH_8) { + if (imf->depth == R_IMF_CHAN_DEPTH_8) { /* Higher depth bits are supported but not needed for current file output. */ ibuf->rect_float = NULL; } @@ -1224,7 +1067,7 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, const RenderData *rd, const /* Color -> gray-scale. */ /* editing directly would alter the render view */ - if (rd->im_format.planes == R_IMF_PLANES_BW) { + if (imf->planes == R_IMF_PLANES_BW) { ImBuf *ibuf_bw = IMB_dupImBuf(ibuf); IMB_color_to_bw(ibuf_bw); IMB_freeImBuf(ibuf); @@ -1234,10 +1077,7 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, const RenderData *rd, const return ibuf; } -void RE_render_result_rect_from_ibuf(RenderResult *rr, - RenderData *UNUSED(rd), - ImBuf *ibuf, - const int view_id) +void RE_render_result_rect_from_ibuf(RenderResult *rr, const ImBuf *ibuf, const int view_id) { RenderView *rv = RE_RenderViewGetById(rr, view_id); @@ -1314,15 +1154,13 @@ void render_result_rect_get_pixels(RenderResult *rr, /*************************** multiview functions *****************************/ -bool RE_HasCombinedLayer(RenderResult *rr) +bool RE_HasCombinedLayer(const RenderResult *rr) { - RenderView *rv; - if (rr == NULL) { return false; } - rv = rr->views.first; + const RenderView *rv = rr->views.first; if (rv == NULL) { return false; } @@ -1330,11 +1168,9 @@ bool RE_HasCombinedLayer(RenderResult *rr) return (rv->rect32 || rv->rectf); } -bool RE_HasFloatPixels(RenderResult *rr) +bool RE_HasFloatPixels(const RenderResult *rr) { - RenderView *rview; - - for (rview = rr->views.first; rview; rview = rview->next) { + for (const RenderView *rview = rr->views.first; rview; rview = rview->next) { if (rview->rect32 && !rview->rectf) { return false; } @@ -1343,7 +1179,7 @@ bool RE_HasFloatPixels(RenderResult *rr) return true; } -bool RE_RenderResult_is_stereo(RenderResult *rr) +bool RE_RenderResult_is_stereo(const RenderResult *rr) { if (!BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) { return false; diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h index 07929f4351f..30f49775562 100644 --- a/source/blender/render/intern/render_result.h +++ b/source/blender/render/intern/render_result.h @@ -131,7 +131,6 @@ void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResu * Free the views created temporarily. */ void render_result_views_shallowdelete(struct RenderResult *rr); -bool render_result_has_views(const struct RenderResult *rr); #define FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re_, iter_) \ { \ diff --git a/source/blender/render/intern/render_types.h b/source/blender/render/intern/render_types.h index b3c80f7dded..27b014ac289 100644 --- a/source/blender/render/intern/render_types.h +++ b/source/blender/render/intern/render_types.h @@ -99,7 +99,7 @@ struct Render { void *dih; void (*display_clear)(void *handle, RenderResult *rr); void *dch; - void (*display_update)(void *handle, RenderResult *rr, volatile rcti *rect); + void (*display_update)(void *handle, RenderResult *rr, rcti *rect); void *duh; void (*current_scene_update)(void *handle, struct Scene *scene); void *suh; diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index adc11cd925e..d01c0dbea71 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -331,7 +331,7 @@ class TextureMarginMap { float destx, desty; int foundpoly; - float mindist = -1.f; + float mindist = -1.0f; /* Loop over all adjacent polygons and determine which edge is closest. * This could be optimized by only inspecting neighbors which are on the edge of an island. @@ -356,7 +356,7 @@ class TextureMarginMap { } } - return mindist >= 0.f; + return mindist >= 0.0f; } /** diff --git a/source/blender/sequencer/intern/disk_cache.c b/source/blender/sequencer/intern/disk_cache.c index 9216383d274..0fdaef61b65 100644 --- a/source/blender/sequencer/intern/disk_cache.c +++ b/source/blender/sequencer/intern/disk_cache.c @@ -160,10 +160,11 @@ static DiskCacheFile *seq_disk_cache_add_file_to_list(SeqDiskCache *disk_cache, static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path) { struct direntry *filelist, *fl; - uint nbr, i; + uint i; disk_cache->size_total = 0; - i = nbr = BLI_filelist_dir_contents(path, &filelist); + const int filelist_num = BLI_filelist_dir_contents(path, &filelist); + i = filelist_num; fl = filelist; while (i--) { /* Don't follow links. */ @@ -194,7 +195,7 @@ static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path) } fl++; } - BLI_filelist_free(filelist, nbr); + BLI_filelist_free(filelist, filelist_num); } static DiskCacheFile *seq_disk_cache_get_oldest_file(SeqDiskCache *disk_cache) diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index a77b34ae66d..8f7088b0c4b 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -3737,9 +3737,9 @@ int SEQ_effect_get_num_inputs(int seq_type) { struct SeqEffectHandle rval = get_sequence_effect_impl(seq_type); - int cnt = rval.num_inputs(); + int count = rval.num_inputs(); if (rval.execute || (rval.execute_slice && rval.init_execution)) { - return cnt; + return count; } return 0; } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index e53ef34603d..3f4d1e875f3 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1501,7 +1501,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, } RE_RenderFrame( - re, context->bmain, scene, have_comp ? NULL : view_layer, camera, frame, false); + re, context->bmain, scene, have_comp ? NULL : view_layer, camera, frame, 0.0f, false); /* restore previous state after it was toggled on & off by RE_RenderFrame */ G.is_rendering = is_rendering; diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 8849b029d46..ddf75f3d664 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -298,7 +298,7 @@ static int shuffle_seq_time_offset_test(SeqCollection *strips_to_shuffle, } if (UNLIKELY(SEQ_collection_has_strip(seq_other, strips_to_shuffle))) { CLOG_WARN(&LOG, - "Strip overlaps with itself or another strip, that is to be shuffled." + "Strip overlaps with itself or another strip, that is to be shuffled. " "This should never happen."); continue; } diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 9edbafafdd3..6df65f2a2a3 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -1167,25 +1167,28 @@ typedef struct wmDropBox { struct wmDropBox *next, *prev; /** Test if the dropbox is active. */ - bool (*poll)(struct bContext *, struct wmDrag *, const wmEvent *); + bool (*poll)(struct bContext *C, struct wmDrag *drag, const wmEvent *event); /** Before exec, this copies drag info to #wmDrop properties. */ - void (*copy)(struct wmDrag *, struct wmDropBox *); + void (*copy)(struct wmDrag *drag, struct wmDropBox *drop); /** - * If the operator is cancelled (returns `OPERATOR_CANCELLED`), this can be used for cleanup of + * If the operator is canceled (returns `OPERATOR_CANCELLED`), this can be used for cleanup of * `copy()` resources. */ - void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *); + void (*cancel)(struct Main *bmain, struct wmDrag *drag, struct wmDropBox *drop); - /** Override the default drawing function. */ - void (*draw)(struct bContext *, struct wmWindow *, struct wmDrag *, const int *); + /** + * Override the default drawing function. + * \param xy: Cursor location in window coordinates (#wmEvent.xy compatible). + */ + void (*draw)(struct bContext *C, struct wmWindow *win, struct wmDrag *drag, const int xy[2]); /** Called when pool returns true the first time. */ - void (*draw_activate)(struct wmDropBox *, struct wmDrag *drag); + void (*draw_activate)(struct wmDropBox *drop, struct wmDrag *drag); /** Called when pool returns false the first time or when the drag event ends. */ - void (*draw_deactivate)(struct wmDropBox *, struct wmDrag *drag); + void (*draw_deactivate)(struct wmDropBox *drop, struct wmDrag *drag); /** Custom data for drawing. */ void *draw_data; diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 46f7b67c2ba..3c8474b1b6c 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -1037,6 +1037,10 @@ void wm_draw_update(bContext *C) wmWindowManager *wm = CTX_wm_manager(C); GPU_context_main_lock(); + + GPU_render_begin(); + GPU_render_step(); + BKE_image_free_unused_gpu_textures(); LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { @@ -1075,6 +1079,7 @@ void wm_draw_update(bContext *C) /* Draw non-windows (surfaces) */ wm_surfaces_iter(C, wm_draw_surface); + GPU_render_end(); GPU_context_main_unlock(); } diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 9a7d31f8bb8..503dae53122 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -38,6 +38,30 @@ /** \name Event Printing * \{ */ +struct FlagIdentifierPair { + const char *id; + uint flag; +}; + +static void event_ids_from_flag(char *str, + const int str_maxlen, + const struct FlagIdentifierPair *flag_data, + const int flag_data_len, + const uint flag) +{ + int ofs = 0; + ofs += BLI_strncpy_rlen(str + ofs, "{", str_maxlen - ofs); + for (int i = 0; i < flag_data_len; i++) { + if (flag & flag_data[i].flag) { + if (ofs != 1) { + ofs += BLI_strncpy_rlen(str + ofs, "|", str_maxlen - ofs); + } + ofs += BLI_strncpy_rlen(str + ofs, flag_data[i].id, str_maxlen - ofs); + } + } + ofs += BLI_strncpy_rlen(str + ofs, "}", str_maxlen - ofs); +} + static void event_ids_from_type_and_value(const short type, const short val, const char **r_type_id, @@ -62,11 +86,33 @@ void WM_event_print(const wmEvent *event) event_ids_from_type_and_value(event->type, event->val, &type_id, &val_id); event_ids_from_type_and_value(event->prev_type, event->prev_val, &prev_type_id, &prev_val_id); + char modifier_id[128]; + { + struct FlagIdentifierPair flag_data[] = { + {"SHIFT", KM_SHIFT}, + {"CTRL", KM_CTRL}, + {"ALT", KM_ALT}, + {"OS", KM_OSKEY}, + }; + event_ids_from_flag( + modifier_id, sizeof(modifier_id), flag_data, ARRAY_SIZE(flag_data), event->modifier); + } + + char flag_id[128]; + { + struct FlagIdentifierPair flag_data[] = { + {"SCROLL_INVERT", WM_EVENT_SCROLL_INVERT}, + {"IS_REPEAT", WM_EVENT_IS_REPEAT}, + {"FORCE_DRAG_THRESHOLD", WM_EVENT_FORCE_DRAG_THRESHOLD}, + }; + event_ids_from_flag(flag_id, sizeof(flag_id), flag_data, ARRAY_SIZE(flag_data), event->flag); + } + printf( - "wmEvent type:%d / %s, val:%d / %s,\n" - " prev_type:%d / %s, prev_val:%d / %s,\n" - " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d, is_repeat:%d,\n" - " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p\n", + "wmEvent type:%d/%s, val:%d/%s, " + "prev_type:%d/%s, prev_val:%d/%s, " + "modifier=%s, keymodifier:%d, flag:%s, " + "mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p", event->type, type_id, event->val, @@ -75,12 +121,9 @@ void WM_event_print(const wmEvent *event) prev_type_id, event->prev_val, prev_val_id, - (event->modifier & KM_SHIFT) != 0, - (event->modifier & KM_CTRL) != 0, - (event->modifier & KM_ALT) != 0, - (event->modifier & KM_OSKEY) != 0, + modifier_id, event->keymodifier, - (event->flag & WM_EVENT_IS_REPEAT) != 0, + flag_id, event->xy[0], event->xy[1], event->ascii, @@ -92,7 +135,7 @@ void WM_event_print(const wmEvent *event) if (ISNDOF(event->type)) { const wmNDOFMotionData *ndof = event->customdata; if (event->type == NDOF_MOTION) { - printf(" ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u\n", + printf(", ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u", UNPACK3(ndof->rvec), UNPACK3(ndof->tvec), ndof->dt, @@ -106,12 +149,13 @@ void WM_event_print(const wmEvent *event) if (event->tablet.active != EVT_TABLET_NONE) { const wmTabletData *wmtab = &event->tablet; - printf(" tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)\n", + printf(", tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)", wmtab->active, wmtab->pressure, wmtab->x_tilt, wmtab->y_tilt); } + printf("\n"); } else { printf("wmEvent - NULL\n"); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 669d8a9b4f0..60ae4eccbbe 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -194,6 +194,10 @@ void wm_event_free(wmEvent *event) printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__); WM_event_print(event); } + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) && (event->val != KM_NOTHING)) { + printf("%s: 'val != NOTHING' for a cursor motion event, this should not happen.\n", __func__); + WM_event_print(event); + } #endif wm_event_custom_free(event); @@ -3879,6 +3883,7 @@ void wm_event_do_handlers(bContext *C) wmEvent tevent = *(win->eventstate); // printf("adding MOUSEMOVE %d %d\n", tevent.xy[0], tevent.xy[1]); tevent.type = MOUSEMOVE; + tevent.val = KM_NOTHING; tevent.prev_xy[0] = tevent.xy[0]; tevent.prev_xy[1] = tevent.xy[1]; tevent.flag = 0; @@ -4423,6 +4428,9 @@ void WM_event_add_mousemove(wmWindow *win) /** \name Ghost Event Conversion * \{ */ +/** + * \return The WM enum for key or #EVENT_NONE (which should be ignored). + */ static int convert_key(GHOST_TKey key) { if (key >= GHOST_kKeyA && key <= GHOST_kKeyZ) { @@ -4446,7 +4454,7 @@ static int convert_key(GHOST_TKey key) case GHOST_kKeyLinefeed: return EVT_LINEFEEDKEY; case GHOST_kKeyClear: - return 0; + return EVENT_NONE; case GHOST_kKeyEnter: return EVT_RETKEY; @@ -4501,9 +4509,9 @@ static int convert_key(GHOST_TKey key) case GHOST_kKeyCapsLock: return EVT_CAPSLOCKKEY; case GHOST_kKeyNumLock: - return 0; + return EVENT_NONE; case GHOST_kKeyScrollLock: - return 0; + return EVENT_NONE; case GHOST_kKeyLeftArrow: return EVT_LEFTARROWKEY; @@ -4515,7 +4523,7 @@ static int convert_key(GHOST_TKey key) return EVT_DOWNARROWKEY; case GHOST_kKeyPrintScreen: - return 0; + return EVENT_NONE; case GHOST_kKeyPause: return EVT_PAUSEKEY; @@ -4557,9 +4565,28 @@ static int convert_key(GHOST_TKey key) case GHOST_kKeyMediaLast: return EVT_MEDIALAST; - default: - return EVT_UNKNOWNKEY; /* #GHOST_kKeyUnknown (this could be asserted). */ + case GHOST_kKeyUnknown: + return EVT_UNKNOWNKEY; + +#if defined(__GNUC__) || defined(__clang__) + /* Ensure all members of this enum are handled, otherwise generate a compiler warning. + * Note that these members have been handled, these ranges are to satisfy the compiler. */ + case GHOST_kKeyF1 ... GHOST_kKeyF24: + case GHOST_kKeyA ... GHOST_kKeyZ: + case GHOST_kKeyNumpad0 ... GHOST_kKeyNumpad9: + case GHOST_kKey0 ... GHOST_kKey9: { + BLI_assert_unreachable(); + break; + } +#else + default: { + break; + } +#endif } + + CLOG_WARN(WM_LOG_EVENTS, "unknown event type %d from ghost", (int)key); + return EVENT_NONE; } static void wm_eventemulation(wmEvent *event, bool test_only) @@ -4822,6 +4849,7 @@ static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win) } tevent.type = MOUSEMOVE; + tevent.val = KM_NOTHING; copy_v2_v2_int(tevent.prev_xy, tevent.xy); wmEvent *event_new = wm_event_add(win, &tevent); @@ -4956,6 +4984,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void wm_tablet_data_from_ghost(&cd->tablet, &event.tablet); event.type = MOUSEMOVE; + event.val = KM_NOTHING; { wmEvent *event_new = wm_event_add_mousemove(win, &event); copy_v2_v2_int(event_state->xy, event_new->xy); @@ -4974,6 +5003,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void copy_v2_v2_int(event_other.xy, event.xy); event_other.type = MOUSEMOVE; + event_other.val = KM_NOTHING; { wmEvent *event_new = wm_event_add_mousemove(win_other, &event_other); copy_v2_v2_int(win_other->eventstate->xy, event_new->xy); @@ -5079,6 +5109,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kEventKeyUp: { GHOST_TEventKeyData *kd = customdata; event.type = convert_key(kd->key); + if (UNLIKELY(event.type == EVENT_NONE)) { + break; + } + event.ascii = kd->ascii; /* Might be not NULL terminated. */ memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf)); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index edce3914666..1369ee99cd2 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -2005,20 +2005,20 @@ void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *UNUSED(wt)) void wm_autosave_delete(void) { - char filename[FILE_MAX]; + char filepath[FILE_MAX]; - wm_autosave_location(filename); + wm_autosave_location(filepath); - if (BLI_exists(filename)) { + if (BLI_exists(filepath)) { char str[FILE_MAX]; BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), BLENDER_QUIT_FILE); /* if global undo; remove tempsave, otherwise rename */ if (U.uiflag & USER_GLOBALUNDO) { - BLI_delete(filename, false, false); + BLI_delete(filepath, false, false); } else { - BLI_rename(filename, str); + BLI_rename(filepath, str); } } } @@ -2965,10 +2965,10 @@ static int wm_recover_auto_save_exec(bContext *C, wmOperator *op) static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - char filename[FILE_MAX]; + char filepath[FILE_MAX]; - wm_autosave_location(filename); - RNA_string_set(op->ptr, "filepath", filename); + wm_autosave_location(filepath); + RNA_string_set(op->ptr, "filepath", filepath); wm_open_init_use_scripts(op, true); WM_event_add_fileselect(C, op); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 6a9776c6933..8d6741dcfb6 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -440,19 +440,19 @@ void WM_exit_ex(bContext *C, const bool do_python) if (undo_memfile != NULL) { /* save the undo state as quit.blend */ Main *bmain = CTX_data_main(C); - char filename[FILE_MAX]; + char filepath[FILE_MAX]; bool has_edited; const int fileflags = G.fileflags & ~G_FILE_COMPRESS; - BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_base(), BLENDER_QUIT_FILE); + BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE); has_edited = ED_editors_flush_edits(bmain); if ((has_edited && BLO_write_file( - bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL)) || - (BLO_memfile_write_file(undo_memfile, filename))) { - printf("Saved session recovery to '%s'\n", filename); + bmain, filepath, fileflags, &(const struct BlendFileWriteParams){0}, NULL)) || + (BLO_memfile_write_file(undo_memfile, filepath))) { + printf("Saved session recovery to '%s'\n", filepath); } } } diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 1c7e63749f8..dacc17c2c1e 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -518,6 +518,14 @@ void WM_operator_properties_mouse_select(wmOperatorType *ot) "Deselect On Nothing", "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + /* TODO: currently only used for the 3D viewport. */ + prop = RNA_def_boolean(ot->srna, + "select_passthrough", + false, + "Only Select Unselected", + "Ignore the select action when the element is already selected"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } void WM_operator_properties_checker_interval(wmOperatorType *ot, bool nth_can_disable) diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c index 5a817075cd5..bde072bf000 100644 --- a/source/blender/windowmanager/intern/wm_operator_utils.c +++ b/source/blender/windowmanager/intern/wm_operator_utils.c @@ -32,9 +32,15 @@ int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent *event) { - if ((event->val != KM_PRESS) && - ((retval & OPERATOR_PASS_THROUGH) && (retval & OPERATOR_FINISHED))) { - retval &= ~OPERATOR_PASS_THROUGH; + if (event->val != KM_PRESS) { + if (retval & OPERATOR_PASS_THROUGH) { + /* Operators that use this function should either finish or cancel, + * otherwise non-press events will be passed through to other key-map items. */ + BLI_assert((retval & ~OPERATOR_PASS_THROUGH) != 0); + if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) { + retval &= ~OPERATOR_PASS_THROUGH; + } + } } return retval; } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 896139796ee..b45c638d7b9 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -51,6 +51,7 @@ #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_image.h" +#include "BKE_image_format.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -1990,7 +1991,7 @@ static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) { ot->name = "Toggle Window Fullscreen"; ot->idname = "WM_OT_window_fullscreen_toggle"; - ot->description = "Toggle the current window fullscreen"; + ot->description = "Toggle the current window full-screen"; ot->exec = wm_window_fullscreen_toggle_exec; ot->poll = WM_operator_winactive; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index cbccc525f0f..ea4f4987190 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1215,6 +1215,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wmEvent event; wm_event_init_from_window(win, &event); event.type = MOUSEMOVE; + event.val = KM_NOTHING; copy_v2_v2_int(event.prev_xy, event.xy); event.flag = 0; @@ -1346,6 +1347,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* activate region */ event.type = MOUSEMOVE; + event.val = KM_NOTHING; copy_v2_v2_int(event.prev_xy, event.xy); event.flag = 0; diff --git a/source/blender/windowmanager/message_bus/wm_message_bus.h b/source/blender/windowmanager/message_bus/wm_message_bus.h index ccb9b92349a..1bc983f20ad 100644 --- a/source/blender/windowmanager/message_bus/wm_message_bus.h +++ b/source/blender/windowmanager/message_bus/wm_message_bus.h @@ -195,16 +195,6 @@ typedef struct wmMsgSubscribeKey_RNA { wmMsg_RNA msg; } wmMsgSubscribeKey_RNA; -#ifdef __GNUC__ -# define _WM_MESSAGE_EXTERN_BEGIN \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wredundant-decls\""); -# define _WM_MESSAGE_EXTERN_END _Pragma("GCC diagnostic pop"); -#else -# define _WM_MESSAGE_EXTERN_BEGIN -# define _WM_MESSAGE_EXTERN_END -#endif - void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msgtype_info); wmMsgSubscribeKey_RNA *WM_msg_lookup_rna(struct wmMsgBus *mbus, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 4321e0f673e..7c6caaa876e 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1352,7 +1352,7 @@ static void wm_xr_session_surface_draw(bContext *C) GHOST_XrSessionDrawViews(wm->xr.runtime->context, &draw_data); - /* There's no active framebuffer if the session was cancelled (exception while drawing views). */ + /* There's no active frame-buffer if the session was canceled (exception while drawing views). */ if (GPU_framebuffer_active_get()) { GPU_framebuffer_restore(); } diff --git a/source/creator/creator.c b/source/creator/creator.c index ec85786c7a4..6c95ee3e490 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -488,6 +488,9 @@ int main(int argc, WM_init(C, argc, (const char **)argv); + /* Need to be after WM init so that userpref are loaded. */ + RE_engines_init_experimental(); + #ifndef WITH_PYTHON printf( "\n* WARNING * - Blender compiled without Python!\n" diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 6651aa77725..e1f5bb6377d 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -35,7 +35,7 @@ # include "BKE_context.h" # include "BKE_global.h" -# include "BKE_image.h" +# include "BKE_image_format.h" # include "BKE_lib_id.h" # include "BKE_main.h" # include "BKE_report.h" @@ -828,7 +828,7 @@ static int arg_handle_log_show_timestamp_set(int UNUSED(argc), } static const char arg_handle_log_file_set_doc[] = - "<filename>\n" + "<filepath>\n" "\tSet a file to output the log to."; static int arg_handle_log_file_set(int argc, const char **argv, void *UNUSED(data)) { @@ -1432,7 +1432,7 @@ static const char arg_handle_image_type_set_doc[] = "\t'TGA' 'RAWTGA' 'JPEG' 'IRIS' 'IRIZ' 'AVIRAW' 'AVIJPEG' 'PNG' 'BMP'\n" "\n" "\tFormats that can be compiled into Blender, not available on all systems:\n" - "\t'HDR' 'TIFF' 'OPEN_EXR' 'OPEN_EXR_MULTILAYER' 'MPEG' 'CINEON' 'DPX' 'DDS' 'JP2'"; + "\t'HDR' 'TIFF' 'OPEN_EXR' 'OPEN_EXR_MULTILAYER' 'MPEG' 'CINEON' 'DPX' 'DDS' 'JP2' 'WEBP'"; static int arg_handle_image_type_set(int argc, const char **argv, void *data) { bContext *C = data; @@ -1752,7 +1752,7 @@ static int arg_handle_frame_skip_set(int argc, const char **argv, void *data) } static const char arg_handle_python_file_run_doc[] = - "<filename>\n" + "<filepath>\n" "\tRun the given Python script file."; static int arg_handle_python_file_run(int argc, const char **argv, void *data) { @@ -1762,12 +1762,12 @@ static int arg_handle_python_file_run(int argc, const char **argv, void *data) /* workaround for scripts not getting a bpy.context.scene, causes internal errors elsewhere */ if (argc > 1) { /* Make the path absolute because its needed for relative linked blends to be found */ - char filename[FILE_MAX]; - BLI_strncpy(filename, argv[1], sizeof(filename)); - BLI_path_abs_from_cwd(filename, sizeof(filename)); + char filepath[FILE_MAX]; + BLI_strncpy(filepath, argv[1], sizeof(filepath)); + BLI_path_abs_from_cwd(filepath, sizeof(filepath)); bool ok; - BPY_CTX_SETUP(ok = BPY_run_filepath(C, filename, NULL)); + BPY_CTX_SETUP(ok = BPY_run_filepath(C, filepath, NULL)); if (!ok && app_state.exit_code_on_error.python) { printf("\nError: script failed, file: '%s', exiting.\n", argv[1]); BPY_python_end(); @@ -1952,22 +1952,22 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data) bool success; /* Make the path absolute because its needed for relative linked blends to be found */ - char filename[FILE_MAX]; + char filepath[FILE_MAX]; /* NOTE: we could skip these, but so far we always tried to load these files. */ if (argv[0][0] == '-') { fprintf(stderr, "unknown argument, loading as file: %s\n", argv[0]); } - BLI_strncpy(filename, argv[0], sizeof(filename)); - BLI_path_slash_native(filename); - BLI_path_abs_from_cwd(filename, sizeof(filename)); - BLI_path_normalize(NULL, filename); + BLI_strncpy(filepath, argv[0], sizeof(filepath)); + BLI_path_slash_native(filepath); + BLI_path_abs_from_cwd(filepath, sizeof(filepath)); + BLI_path_normalize(NULL, filepath); /* load the file */ BKE_reports_init(&reports, RPT_PRINT); - WM_file_autoexec_init(filename); - success = WM_file_read(C, filename, &reports); + WM_file_autoexec_init(filepath); + success = WM_file_read(C, filepath, &reports); BKE_reports_clear(&reports); if (success) { @@ -1988,16 +1988,16 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data) return -1; } - if (BLO_has_bfile_extension(filename)) { + if (BLO_has_bfile_extension(filepath)) { /* Just pretend a file was loaded, so the user can press Save and it'll - * save at the filename from the CLI. */ - STRNCPY(G_MAIN->filepath, filename); - printf("... opened default scene instead; saving will write to: %s\n", filename); + * save at the filepath from the CLI. */ + STRNCPY(G_MAIN->filepath, filepath); + printf("... opened default scene instead; saving will write to: %s\n", filepath); } else { printf( "Error: argument has no '.blend' file extension, not using as new file, exiting! %s\n", - filename); + filepath); G.is_break = true; WM_exit(C); } |