diff options
author | Monique <mdewanchand@atmind.nl> | 2022-09-13 21:08:48 +0300 |
---|---|---|
committer | Monique <mdewanchand@atmind.nl> | 2022-09-13 21:08:48 +0300 |
commit | 3f5fd912a7bab78b4b85438f630f392ad8234bc8 (patch) | |
tree | c60796389d0a88ee78281baf1792351150a426b4 | |
parent | b31a8e30cba735e5f5880b9ed59e5576ecc2d9eb (diff) | |
parent | e449a9bb5e11c07b315f9eb2dcb7de0237f37c02 (diff) |
Merge branch 'temp-T73411-add-scene-parameters' into temp-T73411-view-layer-lazy-cache
651 files changed, 17694 insertions, 10833 deletions
diff --git a/.clang-format b/.clang-format index 7e88e6d1cb1..72add4594a4 100644 --- a/.clang-format +++ b/.clang-format @@ -273,5 +273,5 @@ StatementMacros: - PyObject_VAR_HEAD - ccl_gpu_kernel_postfix -MacroBlockBegin: "^BSDF_CLOSURE_CLASS_BEGIN$" -MacroBlockEnd: "^BSDF_CLOSURE_CLASS_END$" +MacroBlockBegin: "^OSL_CLOSURE_STRUCT_BEGIN$" +MacroBlockEnd: "^OSL_CLOSURE_STRUCT_END$" diff --git a/CMakeLists.txt b/CMakeLists.txt index 9688c711bc7..85e2a1450d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2006 Blender Foundation. All rights reserved. -#----------------------------------------------------------------------------- -# We don't allow in-source builds. This causes no end of troubles because +# ----------------------------------------------------------------------------- +# Early Initialization + +# NOTE: We don't allow in-source builds. This causes no end of troubles because # all out-of-source builds will use the CMakeCache.txt file there and even # build the libs and objects in it. - if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) if(NOT DEFINED WITH_IN_SOURCE_BUILD) message(FATAL_ERROR @@ -35,7 +36,7 @@ endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/build_files/cmake/Modules") list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/build_files/cmake/platform") -# avoid having empty buildtype +# Avoid having an empty `CMAKE_BUILD_TYPE`. if(NOT DEFINED CMAKE_BUILD_TYPE_INIT) set(CMAKE_BUILD_TYPE_INIT "Release") # Internal logic caches this variable, avoid showing it by default @@ -59,7 +60,8 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:RelWithDebInfo>:NDEBUG> ) -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Set policy # see "cmake --help-policy CMP0003" @@ -89,13 +91,16 @@ endif() if(POLICY CMP0087) cmake_policy(SET CMP0087 NEW) endif() -#----------------------------------------------------------------------------- -# Load some macros. + + +# ----------------------------------------------------------------------------- +# Load Blender's Local Macros + include(build_files/cmake/macros.cmake) -#----------------------------------------------------------------------------- -# Initialize project. +# ----------------------------------------------------------------------------- +# Initialize Project blender_project_hack_pre() @@ -105,14 +110,15 @@ blender_project_hack_post() enable_testing() -#----------------------------------------------------------------------------- -# Test compiler/library features. + +# ----------------------------------------------------------------------------- +# Test Compiler/Library Features include(build_files/cmake/have_features.cmake) -#----------------------------------------------------------------------------- -# Redirect output files +# ----------------------------------------------------------------------------- +# Redirect Output Files set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin CACHE INTERNAL "" FORCE) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib CACHE INTERNAL "" FORCE) @@ -124,14 +130,15 @@ else() set(TESTS_OUTPUT_DIR ${EXECUTABLE_OUTPUT_PATH}/tests/ CACHE INTERNAL "" FORCE) endif() -#----------------------------------------------------------------------------- -# Set default config options + +# ----------------------------------------------------------------------------- +# Set Default Configuration Options get_blender_version() -#----------------------------------------------------------------------------- -# Options +# ----------------------------------------------------------------------------- +# Declare Options # Blender internal features option(WITH_BLENDER "Build blender (disable to build only the blender player)" ON) @@ -157,9 +164,6 @@ mark_as_advanced(WITH_PYTHON_SECURITY) # some distributions see this as a secur option(WITH_PYTHON_SAFETY "Enable internal API error checking to track invalid data to prevent crash on access (at the expense of some efficiency, only enable for development)." OFF) mark_as_advanced(WITH_PYTHON_SAFETY) option(WITH_PYTHON_MODULE "Enable building as a python module which runs without a user interface, like running regular blender in background mode (experimental, only enable for development), installs to PYTHON_SITE_PACKAGES (or CMAKE_INSTALL_PREFIX if WITH_INSTALL_PORTABLE is enabled)." OFF) -if(APPLE) - option(WITH_PYTHON_FRAMEWORK "Enable building using the Python available in the framework (OSX only)" OFF) -endif() option(WITH_BUILDINFO "Include extra build details (only disable for development & faster builds)" ON) set(BUILDINFO_OVERRIDE_DATE "" CACHE STRING "Use instead of the current date for reproducible builds (empty string disables this option)") @@ -753,8 +757,8 @@ if(APPLE) endif() -#----------------------------------------------------------------------------- -# Check for conflicting/unsupported configurations +# ----------------------------------------------------------------------------- +# Check for Conflicting/Unsupported Configurations if(NOT WITH_BLENDER AND NOT WITH_CYCLES_STANDALONE AND NOT WITH_CYCLES_HYDRA_RENDER_DELEGATE) message(FATAL_ERROR @@ -888,7 +892,11 @@ endif() if(WITH_CYCLES AND WITH_CYCLES_DEVICE_CUDA AND NOT WITH_CUDA_DYNLOAD) find_package(CUDA) if(NOT CUDA_FOUND) - message(STATUS "CUDA toolkit not found, using dynamic runtime loading of libraries (WITH_CUDA_DYNLOAD) instead") + message( + STATUS + "CUDA toolkit not found, " + "using dynamic runtime loading of libraries (WITH_CUDA_DYNLOAD) instead" + ) set(WITH_CUDA_DYNLOAD ON) endif() endif() @@ -898,14 +906,16 @@ if(WITH_CYCLES_DEVICE_HIP) set(WITH_HIP_DYNLOAD ON) endif() -#----------------------------------------------------------------------------- -# Check if submodules are cloned. + +# ----------------------------------------------------------------------------- +# Check if Sub-modules are Cloned if(WITH_INTERNATIONAL) file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/datafiles/locale") list(LENGTH RESULT DIR_LEN) if(DIR_LEN EQUAL 0) - message(WARNING + message( + WARNING "Translation path '${CMAKE_SOURCE_DIR}/release/datafiles/locale' is missing, " "This is a 'git submodule', which are known not to work with bridges to other version " "control systems, disabling 'WITH_INTERNATIONAL'." @@ -923,13 +933,17 @@ if(WITH_PYTHON) # because UNIX will search for the old Python paths which may not exist. # giving errors about missing paths before this case is met. if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.10") - message(FATAL_ERROR "At least Python 3.10 is required to build, but found Python ${PYTHON_VERSION}") + message( + FATAL_ERROR + "At least Python 3.10 is required to build, but found Python ${PYTHON_VERSION}" + ) endif() file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/scripts/addons") list(LENGTH RESULT DIR_LEN) if(DIR_LEN EQUAL 0) - message(WARNING + message( + WARNING "Addons path '${CMAKE_SOURCE_DIR}/release/scripts/addons' is missing, " "This is a 'git submodule', which are known not to work with bridges to other version " "control systems: * CONTINUING WITHOUT ADDONS *" @@ -937,8 +951,9 @@ if(WITH_PYTHON) endif() endif() -#----------------------------------------------------------------------------- -# Initialize un-cached vars, avoid unused warning + +# ----------------------------------------------------------------------------- +# InitialIze Un-cached Vars, Avoid Unused Warning # linux only, not cached set(WITH_BINRELOC OFF) @@ -1013,6 +1028,7 @@ if(WITH_CPU_SIMD) endif() endif() + # ---------------------------------------------------------------------------- # Main Platform Checks # @@ -1028,8 +1044,9 @@ elseif(APPLE) include(platform_apple) endif() -#----------------------------------------------------------------------------- -# Common. + +# ----------------------------------------------------------------------------- +# Common Checks for Compatible Options if(NOT WITH_FFTW3 AND WITH_MOD_OCEANSIM) message(FATAL_ERROR "WITH_MOD_OCEANSIM requires WITH_FFTW3 to be ON") @@ -1037,13 +1054,15 @@ endif() if(WITH_CYCLES) if(NOT WITH_OPENIMAGEIO) - message(FATAL_ERROR + message( + FATAL_ERROR "Cycles requires WITH_OPENIMAGEIO, the library may not have been found. " "Configure OIIO or disable WITH_CYCLES" ) endif() if(NOT WITH_BOOST) - message(FATAL_ERROR + message( + FATAL_ERROR "Cycles requires WITH_BOOST, the library may not have been found. " "Configure BOOST or disable WITH_CYCLES" ) @@ -1051,7 +1070,8 @@ if(WITH_CYCLES) if(WITH_CYCLES_OSL) if(NOT WITH_LLVM) - message(FATAL_ERROR + message( + FATAL_ERROR "Cycles OSL requires WITH_LLVM, the library may not have been found. " "Configure LLVM or disable WITH_CYCLES_OSL" ) @@ -1061,7 +1081,8 @@ endif() if(WITH_INTERNATIONAL) if(NOT WITH_BOOST) - message(FATAL_ERROR + message( + FATAL_ERROR "Internationalization requires WITH_BOOST, the library may not have been found. " "Configure BOOST or disable WITH_INTERNATIONAL" ) @@ -1175,15 +1196,18 @@ if(WITH_OPENVDB) list(APPEND OPENVDB_LIBRARIES ${BOOST_LIBRARIES} ${TBB_LIBRARIES}) endif() -#----------------------------------------------------------------------------- -# Configure OpenGL. + +# ----------------------------------------------------------------------------- +# Configure OpenGL if(WITH_OPENGL) add_definitions(-DWITH_OPENGL) endif() -#----------------------------------------------------------------------------- -# Configure Metal. + +# ----------------------------------------------------------------------------- +# Configure Metal + if(WITH_METAL_BACKEND) add_definitions(-DWITH_METAL_BACKEND) @@ -1192,8 +1216,10 @@ if(WITH_METAL_BACKEND) # build_files/cmake/platform/platform_apple.cmake endif() -#----------------------------------------------------------------------------- -# Configure OpenMP. + +# ----------------------------------------------------------------------------- +# Configure OpenMP + if(WITH_OPENMP) if(NOT OPENMP_CUSTOM) find_package(OpenMP) @@ -1225,7 +1251,8 @@ if(WITH_OPENMP) ) endif() -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Configure Bullet if(WITH_BULLET AND WITH_SYSTEM_BULLET) @@ -1239,15 +1266,17 @@ else() # set(BULLET_LIBRARIES "") endif() -#----------------------------------------------------------------------------- -# Configure Python. + +# ----------------------------------------------------------------------------- +# Configure Python if(WITH_PYTHON_MODULE) add_definitions(-DPy_ENABLE_SHARED) endif() -#----------------------------------------------------------------------------- -# Configure GLog/GFlags + +# ----------------------------------------------------------------------------- +# Configure `GLog/GFlags` if(WITH_LIBMV OR WITH_GTESTS OR (WITH_CYCLES AND WITH_CYCLES_LOGGING)) if(WITH_SYSTEM_GFLAGS) @@ -1255,7 +1284,7 @@ if(WITH_LIBMV OR WITH_GTESTS OR (WITH_CYCLES AND WITH_CYCLES_LOGGING)) if(NOT GFLAGS_FOUND) message(FATAL_ERROR "System wide Gflags is requested but was not found") endif() - # FindGflags does not define this, and we are not even sure what to use here. + # `FindGflags` does not define this, and we are not even sure what to use here. set(GFLAGS_DEFINES) else() set(GFLAGS_DEFINES @@ -1273,7 +1302,7 @@ if(WITH_LIBMV OR WITH_GTESTS OR (WITH_CYCLES AND WITH_CYCLES_LOGGING)) if(NOT GLOG_FOUND) message(FATAL_ERROR "System wide Glog is requested but was not found") endif() - # FindGlog does not define this, and we are not even sure what to use here. + # `FindGlog` does not define this, and we are not even sure what to use here. set(GLOG_DEFINES) else() set(GLOG_DEFINES @@ -1288,9 +1317,13 @@ if(WITH_LIBMV OR WITH_GTESTS OR (WITH_CYCLES AND WITH_CYCLES_LOGGING)) endif() endif() -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# Ninja Job Limiting + # Extra limits to number of jobs running in parallel for some kind os tasks. # Only supported by Ninja build system currently. + if("${CMAKE_GENERATOR}" MATCHES "Ninja" AND WITH_NINJA_POOL_JOBS) if(NOT NINJA_MAX_NUM_PARALLEL_COMPILE_JOBS AND NOT NINJA_MAX_NUM_PARALLEL_COMPILE_HEAVY_JOBS AND @@ -1302,7 +1335,8 @@ if("${CMAKE_GENERATOR}" MATCHES "Ninja" AND WITH_NINJA_POOL_JOBS) # Note: this gives mem in MB. cmake_host_system_information(RESULT _TOT_MEM QUERY TOTAL_PHYSICAL_MEMORY) - # Heuristics... the more cores we have, the more free mem we have to keep for the non-heavy tasks too. + # Heuristics: the more cores we have, the more free memory we have to keep + # for the non-heavy tasks too. if(${_TOT_MEM} LESS 8000 AND ${_NUM_CORES} GREATER 2) set(_compile_heavy_jobs "1") elseif(${_TOT_MEM} LESS 16000 AND ${_NUM_CORES} GREATER 4) @@ -1322,7 +1356,8 @@ if("${CMAKE_GENERATOR}" MATCHES "Ninja" AND WITH_NINJA_POOL_JOBS) mark_as_advanced(NINJA_MAX_NUM_PARALLEL_COMPILE_HEAVY_JOBS) set(_compile_heavy_jobs) - # Only set regular compile jobs if we set heavy jobs, otherwise default (using all cores) if fine. + # Only set regular compile jobs if we set heavy jobs, + # otherwise default (using all cores) if fine. if(NINJA_MAX_NUM_PARALLEL_COMPILE_HEAVY_JOBS) math(EXPR _compile_jobs "${_NUM_CORES} - 1") else() @@ -1333,8 +1368,8 @@ if("${CMAKE_GENERATOR}" MATCHES "Ninja" AND WITH_NINJA_POOL_JOBS) mark_as_advanced(NINJA_MAX_NUM_PARALLEL_COMPILE_JOBS) set(_compile_jobs) - # In practice, even when there is RAM available, this proves to be quicker than running in parallel - # (due to slow disks accesses). + # In practice, even when there is RAM available, + # this proves to be quicker than running in parallel (due to slow disks accesses). set(NINJA_MAX_NUM_PARALLEL_LINK_JOBS "1" CACHE STRING "Define the maximum number of concurrent link jobs, for ninja build system." FORCE) mark_as_advanced(NINJA_MAX_NUM_PARALLEL_LINK_JOBS) @@ -1358,8 +1393,9 @@ if("${CMAKE_GENERATOR}" MATCHES "Ninja" AND WITH_NINJA_POOL_JOBS) endif() endif() -#----------------------------------------------------------------------------- -# Extra compile flags + +# ----------------------------------------------------------------------------- +# Extra Compile Flags if(CMAKE_COMPILER_IS_GNUCC) @@ -1449,7 +1485,7 @@ if(CMAKE_COMPILER_IS_GNUCC) endif() - #---------------------- + # --------------------- # Suppress Strict Flags # # Exclude the following warnings from this list: @@ -1515,7 +1551,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") # ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_MACROS -Wunused-macros) # ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_UNUSED_MACROS -Wunused-macros) - #---------------------- + # --------------------- # Suppress Strict Flags # flags to undo strict flags @@ -1606,7 +1642,8 @@ endif() # be most problematic. if(WITH_PYTHON) if(NOT EXISTS "${PYTHON_INCLUDE_DIR}/Python.h") - message(FATAL_ERROR + message( + FATAL_ERROR "Missing: \"${PYTHON_INCLUDE_DIR}/Python.h\",\n" "Set the cache entry 'PYTHON_INCLUDE_DIR' to point " "to a valid python include path. Containing " @@ -1614,8 +1651,8 @@ if(WITH_PYTHON) ) endif() - if(WIN32 OR APPLE) - # Windows and macOS have this bundled with Python libraries. + if(WIN32) + # Always use numpy bundled in precompiled libs. elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR WITH_PYTHON_NUMPY) if(("${PYTHON_NUMPY_PATH}" STREQUAL "") OR (${PYTHON_NUMPY_PATH} MATCHES NOTFOUND)) find_python_package(numpy "core/include") @@ -1623,13 +1660,13 @@ if(WITH_PYTHON) endif() if(WIN32 OR APPLE) - # pass, we have this in lib/python/site-packages + # Always copy from precompiled libs. elseif(WITH_PYTHON_INSTALL_REQUESTS) find_python_package(requests "") endif() if(WIN32 OR APPLE) - # pass, we have this in lib/python/site-packages + # Always copy from precompiled libs. elseif(WITH_PYTHON_INSTALL_ZSTANDARD) find_python_package(zstandard "") endif() @@ -1675,9 +1712,11 @@ if(WITH_COMPILER_SHORT_FILE_MACRO) if(XCODE AND ${XCODE_VERSION} VERSION_LESS 12.0) # Developers may have say LLVM Clang-10.0.1 toolchain (which supports the flag) # with Xcode-11 (the Clang of which doesn't support the flag). - message(WARNING + message( + WARNING "-fmacro-prefix-map flag is NOT supported by Clang shipped with Xcode-${XCODE_VERSION}." - " Some Xcode functionality in Product menu may not work. Disabling WITH_COMPILER_SHORT_FILE_MACRO." + " Some Xcode functionality in Product menu may not work. " + "Disabling WITH_COMPILER_SHORT_FILE_MACRO." ) set(WITH_COMPILER_SHORT_FILE_MACRO OFF) endif() @@ -1693,7 +1732,8 @@ if(WITH_COMPILER_SHORT_FILE_MACRO) unset(_bin_dir) endif() else() - message(WARNING + message( + WARNING "-fmacro-prefix-map flag is NOT supported by C/C++ compiler." " Disabling WITH_COMPILER_SHORT_FILE_MACRO." ) @@ -1723,7 +1763,8 @@ mark_as_advanced( LLVM_VERSION ) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Global Defines # better not set includes here but this debugging option is off by default. @@ -1739,8 +1780,9 @@ endif() # message(STATUS "Using CFLAGS: ${CMAKE_C_FLAGS}") # message(STATUS "Using CXXFLAGS: ${CMAKE_CXX_FLAGS}") -#----------------------------------------------------------------------------- -# Libraries + +# ----------------------------------------------------------------------------- +# Add Sub-Directories if(WITH_BLENDER) add_subdirectory(intern) @@ -1769,33 +1811,41 @@ elseif(WITH_CYCLES_STANDALONE OR WITH_CYCLES_HYDRA_RENDER_DELEGATE) endif() endif() -#----------------------------------------------------------------------------- -# Testing + +# ----------------------------------------------------------------------------- +# Add Testing Directory + add_subdirectory(tests) -#----------------------------------------------------------------------------- -# Blender Application + +# ----------------------------------------------------------------------------- +# Add Blender Application + if(WITH_BLENDER) add_subdirectory(source/creator) endif() -#----------------------------------------------------------------------------- -# Define 'heavy' submodules (for Ninja builder when using pools). +# ----------------------------------------------------------------------------- +# Define 'heavy' sub-modules (for Ninja builder when using pools) setup_heavy_lib_pool() -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # CPack for generating packages + include(build_files/cmake/packaging.cmake) -#----------------------------------------------------------------------------- -# Use dynamic loading for OpenMP + +# ----------------------------------------------------------------------------- +# Use Dynamic Loading for OpenMP + if(WITH_BLENDER) openmp_delayload(blender) endif() -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Print Final Configuration if(FIRST_RUN) @@ -1891,9 +1941,6 @@ if(FIRST_RUN) info_cfg_option(WITH_LZO) info_cfg_text("Python:") - if(APPLE) - info_cfg_option(WITH_PYTHON_FRAMEWORK) - endif() info_cfg_option(WITH_PYTHON_INSTALL) info_cfg_option(WITH_PYTHON_INSTALL_NUMPY) info_cfg_option(WITH_PYTHON_INSTALL_ZSTANDARD) diff --git a/GNUmakefile b/GNUmakefile index a82d1bedace..884d2232d71 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -162,6 +162,7 @@ CPU:=$(shell uname -m) # Source and Build DIR's BLENDER_DIR:=$(shell pwd -P) BUILD_TYPE:=Release +BLENDER_IS_PYTHON_MODULE:= # CMake arguments, assigned to local variable to make it mutable. CMAKE_CONFIG_ARGS := $(BUILD_CMAKE_ARGS) @@ -229,9 +230,18 @@ endif # ----------------------------------------------------------------------------- -# additional targets for the build configuration +# Additional targets for the build configuration -# support 'make debug' +# NOTE: These targets can be combined and are applied in reverse order listed here. +# So it's important that `bpy` comes before `release` (for example) +# `make bpy release` first loads `release` configuration, then `bpy`. +# This is important as `bpy` will turn off some settings enabled by release. + +ifneq "$(findstring bpy, $(MAKECMDGOALS))" "" + BUILD_DIR:=$(BUILD_DIR)_bpy + CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/bpy_module.cmake" $(CMAKE_CONFIG_ARGS) + BLENDER_IS_PYTHON_MODULE:=1 +endif ifneq "$(findstring debug, $(MAKECMDGOALS))" "" BUILD_DIR:=$(BUILD_DIR)_debug BUILD_TYPE:=Debug @@ -256,10 +266,6 @@ ifneq "$(findstring headless, $(MAKECMDGOALS))" "" BUILD_DIR:=$(BUILD_DIR)_headless CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/blender_headless.cmake" $(CMAKE_CONFIG_ARGS) endif -ifneq "$(findstring bpy, $(MAKECMDGOALS))" "" - BUILD_DIR:=$(BUILD_DIR)_bpy - CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/bpy_module.cmake" $(CMAKE_CONFIG_ARGS) -endif ifneq "$(findstring developer, $(MAKECMDGOALS))" "" CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/blender_developer.cmake" $(CMAKE_CONFIG_ARGS) @@ -297,8 +303,10 @@ endif # use the default build path can still use utility helpers. ifeq ($(OS), Darwin) BLENDER_BIN?="$(BUILD_DIR)/bin/Blender.app/Contents/MacOS/Blender" + BLENDER_BIN_DIR?="$(BUILD_DIR)/bin/Blender.app/Contents/MacOS/Blender" else BLENDER_BIN?="$(BUILD_DIR)/bin/blender" + BLENDER_BIN_DIR?="$(BUILD_DIR)/bin" endif @@ -355,8 +363,12 @@ all: .FORCE @echo Building Blender ... $(BUILD_COMMAND) -C "$(BUILD_DIR)" -j $(NPROCS) install @echo - @echo edit build configuration with: "$(BUILD_DIR)/CMakeCache.txt" run make again to rebuild. - @echo Blender successfully built, run from: $(BLENDER_BIN) + @echo Edit build configuration with: \"$(BUILD_DIR)/CMakeCache.txt\" run make again to rebuild. + @if test "$(BLENDER_IS_PYTHON_MODULE)" == ""; then \ + echo Blender successfully built, run from: $(BLENDER_BIN); \ + else \ + echo Blender successfully built as a Python module, \"bpy\" can be imported from: $(BLENDER_BIN_DIR); \ + fi @echo debug: all diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 814834ccf34..287a7a0c962 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -136,7 +136,7 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: Build and install the OpenImageDenoise libraries. --with-nanovdb - Build and install the NanoVDB branch of OpenVDB (instead of official release of OpenVDB). + Build and install NanoVDB together with OpenVDB. --with-jack Install the jack libraries. @@ -385,7 +385,7 @@ CLANG_FORMAT_VERSION="10.0" CLANG_FORMAT_VERSION_MIN="6.0" CLANG_FORMAT_VERSION_MEX="14.0" -PYTHON_VERSION="3.10.2" +PYTHON_VERSION="3.10.6" PYTHON_VERSION_SHORT="3.10" PYTHON_VERSION_MIN="3.10" PYTHON_VERSION_MEX="3.12" @@ -425,7 +425,7 @@ PYTHON_ZSTANDARD_VERSION_MIN="0.15.2" PYTHON_ZSTANDARD_VERSION_MEX="0.20.0" PYTHON_ZSTANDARD_NAME="zstandard" -PYTHON_NUMPY_VERSION="1.22.0" +PYTHON_NUMPY_VERSION="1.23.2" PYTHON_NUMPY_VERSION_MIN="1.14" PYTHON_NUMPY_VERSION_MEX="2.0" PYTHON_NUMPY_NAME="numpy" @@ -453,8 +453,8 @@ PYTHON_MODULES_PIP=( ) -BOOST_VERSION="1.78.0" -BOOST_VERSION_SHORT="1.78" +BOOST_VERSION="1.80.0" +BOOST_VERSION_SHORT="1.80" BOOST_VERSION_MIN="1.49" BOOST_VERSION_MEX="2.0" BOOST_FORCE_BUILD=false @@ -496,7 +496,7 @@ OPENEXR_FORCE_REBUILD=false OPENEXR_SKIP=false _with_built_openexr=false -OIIO_VERSION="2.3.13.0" +OIIO_VERSION="2.3.18.0" OIIO_VERSION_SHORT="2.3" OIIO_VERSION_MIN="2.1.12" OIIO_VERSION_MEX="2.4.0" @@ -534,10 +534,10 @@ OSD_SKIP=false # OpenVDB needs to be compiled for now OPENVDB_BLOSC_VERSION="1.21.1" -OPENVDB_VERSION="9.0.0" -OPENVDB_VERSION_SHORT="9.0" +OPENVDB_VERSION="9.1.0" +OPENVDB_VERSION_SHORT="9.1" OPENVDB_VERSION_MIN="9.0" -OPENVDB_VERSION_MEX="9.1" +OPENVDB_VERSION_MEX="9.2" OPENVDB_FORCE_BUILD=false OPENVDB_FORCE_REBUILD=false OPENVDB_SKIP=false @@ -2919,6 +2919,10 @@ compile_OPENVDB() { cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst" cmake_d="$cmake_d -D USE_STATIC_DEPENDENCIES=OFF" cmake_d="$cmake_d -D OPENVDB_BUILD_BINARIES=OFF" + # Unfortunately OpenVDB currently forces using recent oneTBB over older versions when it finds it, + # even when TBB_ROOT is specified. So have to prevent any check for system library - + # in the hope it will not break in some other cases. + cmake_d="$cmake_d -D DISABLE_CMAKE_SEARCH_PATHS=ON" if [ "$WITH_NANOVDB" = true ]; then cmake_d="$cmake_d -D USE_NANOVDB=ON" @@ -2931,7 +2935,6 @@ compile_OPENVDB() { cmake_d="$cmake_d -D Boost_USE_MULTITHREADED=ON" cmake_d="$cmake_d -D Boost_NO_SYSTEM_PATHS=ON" cmake_d="$cmake_d -D Boost_NO_BOOST_CMAKE=ON" - cmake_d="$cmake_d -D Boost_NO_BOOST_CMAKE=ON" fi if [ -d $INST/tbb ]; then cmake_d="$cmake_d -D TBB_ROOT=$INST/tbb" @@ -3195,7 +3198,7 @@ _init_opencollada() { _inst_shortcut=$INST/opencollada } -_update_deps_collada() { +_update_deps_opencollada() { : } @@ -6215,7 +6218,7 @@ print_info() { fi if [ -d $INST/nanovdb ]; then _1="-D WITH_NANOVDB=ON" - _2="-D NANOVDB_ROOT_DIR=$INST/nanovdb" + _2="-D NANOVDB_ROOT_DIR=$INST/openvdb" PRINT " $_1" PRINT " $_2" _buildargs="$_buildargs $_1 $_2" diff --git a/build_files/cmake/Modules/FindPythonLibsUnix.cmake b/build_files/cmake/Modules/FindPythonLibsUnix.cmake index 1e88621303f..0afe1299330 100644 --- a/build_files/cmake/Modules/FindPythonLibsUnix.cmake +++ b/build_files/cmake/Modules/FindPythonLibsUnix.cmake @@ -34,11 +34,17 @@ SET(PYTHON_VERSION 3.10 CACHE STRING "Python Version (major and minor only)") MARK_AS_ADVANCED(PYTHON_VERSION) -# See: http://docs.python.org/extending/embedding.html#linking-requirements -# for why this is needed -SET(PYTHON_LINKFLAGS "-Xlinker -export-dynamic" CACHE STRING "Linker flags for python") -MARK_AS_ADVANCED(PYTHON_LINKFLAGS) - +if(APPLE) + if(WITH_PYTHON_MODULE) + set(PYTHON_LINKFLAGS "-undefined dynamic_lookup") + else() + set(PYTHON_LINKFLAGS) + endif() +else() + # See: http://docs.python.org/extending/embedding.html#linking-requirements + SET(PYTHON_LINKFLAGS "-Xlinker -export-dynamic" CACHE STRING "Linker flags for python") + MARK_AS_ADVANCED(PYTHON_LINKFLAGS) +endif() # if the user passes these defines as args, we don't want to overwrite SET(_IS_INC_DEF OFF) diff --git a/build_files/cmake/Modules/GTest.cmake b/build_files/cmake/Modules/GTest.cmake index a38550958fd..5c7fabdcff7 100644 --- a/build_files/cmake/Modules/GTest.cmake +++ b/build_files/cmake/Modules/GTest.cmake @@ -268,7 +268,8 @@ same as the Google Test name (i.e. ``suite.testcase``); see also cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST -#------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------- + function(gtest_add_tests) if(ARGC LESS 1) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 060fcc0638b..38997e2139b 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -7,8 +7,6 @@ # cmake -C../blender/build_files/cmake/config/blender_lite.cmake ../blender # -set(WITH_INSTALL_PORTABLE ON CACHE BOOL "" FORCE) - set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE) set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE) set(WITH_BLENDER_THUMBNAILER OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/bpy_module.cmake b/build_files/cmake/config/bpy_module.cmake index c4be5c2e61f..7bd5c3fc1a1 100644 --- a/build_files/cmake/config/bpy_module.cmake +++ b/build_files/cmake/config/bpy_module.cmake @@ -8,41 +8,81 @@ set(WITH_PYTHON_MODULE ON CACHE BOOL "" FORCE) -# install into the systems python dir -set(WITH_INSTALL_PORTABLE OFF CACHE BOOL "" FORCE) -# no point int copying python into python +# ----------------------------------------------------------------------------- +# Installation Configuration. +# +# NOTE: `WITH_INSTALL_PORTABLE` always defaults to ON when building as a Python module and +# isn't set here as it makes changing the setting impractical. +# Python-developers could prefer either ON/OFF depending on their usage: +# +# - When using the system's Python, disabling will install into their `site-packages`, +# allowing them to run Python from any directory and `import bpy`. +# - When using Blender's bundled Python in `./../lib/` it will install there +# which isn't especially useful as it requires running Python from this directory too. +# +# So default `WITH_INSTALL_PORTABLE` to ON, and developers who don't use Python from `./../lib/` +# can disable it if they wish to install into their systems Python. + +# There is no point in copying python into Python. set(WITH_PYTHON_INSTALL OFF CACHE BOOL "" FORCE) -# disable audio, its possible some devs may want this but for now disable -# so the python module doesn't hold the audio device and loads quickly. +# Depends on Python install, do this to quiet warning. +set(WITH_DRACO OFF CACHE BOOL "" FORCE) + +if(WIN32) + set(WITH_WINDOWS_BUNDLE_CRT OFF CACHE BOOL "" FORCE) +endif() + + +# ----------------------------------------------------------------------------- +# Library Compatibility. + +# JEMALLOC does not work with `dlopen()` of Python modules: +# https://github.com/jemalloc/jemalloc/issues/1237 +set(WITH_MEM_JEMALLOC OFF CACHE BOOL "" FORCE) + + +# ----------------------------------------------------------------------------- +# Application Support. + +# Not useful to include with the Python module. +# Although a way to extract this from Python could be handle, +# this would be better exposed directly via the Python API. +set(WITH_BLENDER_THUMBNAILER OFF CACHE BOOL "" FORCE) + + +# ----------------------------------------------------------------------------- +# Audio Support. + +# Disable audio, its possible some developers may want this but for now disable +# so the Python module doesn't hold the audio device and loads quickly. set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE) -set(WITH_CODEC_FFMPEG OFF CACHE BOOL "" FORCE) -set(WITH_CODEC_SNDFILE OFF CACHE BOOL "" FORCE) -set(WITH_COREAUDIO OFF CACHE BOOL "" FORCE) set(WITH_JACK OFF CACHE BOOL "" FORCE) set(WITH_OPENAL OFF CACHE BOOL "" FORCE) -set(WITH_PULSEAUDIO OFF CACHE BOOL "" FORCE) set(WITH_SDL OFF CACHE BOOL "" FORCE) -set(WITH_WASAPI OFF CACHE BOOL "" FORCE) +if(UNIX AND NOT APPLE) + set(WITH_PULSEAUDIO OFF CACHE BOOL "" FORCE) +endif() +if(WIN32) + set(WITH_WASAPI OFF CACHE BOOL "" FORCE) +endif() +if(APPLE) + set(WITH_COREAUDIO OFF CACHE BOOL "" FORCE) +endif() + -# other features which are not especially useful as a python module -set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE) -set(WITH_BULLET OFF CACHE BOOL "" FORCE) +# ----------------------------------------------------------------------------- +# Input Device Support. + +# Other features which are not especially useful as a python module. set(WITH_INPUT_NDOF OFF CACHE BOOL "" FORCE) -set(WITH_INTERNATIONAL OFF CACHE BOOL "" FORCE) -set(WITH_NANOVDB OFF CACHE BOOL "" FORCE) -set(WITH_OPENCOLLADA OFF CACHE BOOL "" FORCE) -set(WITH_OPENVDB OFF CACHE BOOL "" FORCE) -set(WITH_X11_XINPUT OFF CACHE BOOL "" FORCE) +if(WIN32 OR APPLE) + set(WITH_INPUT_IME OFF CACHE BOOL "" FORCE) +endif() -# Depends on Python install, do this to quiet warning. -set(WITH_DRACO OFF CACHE BOOL "" FORCE) -# Jemalloc does not work with dlopen() of Python modules: -# https://github.com/jemalloc/jemalloc/issues/1237 -set(WITH_MEM_JEMALLOC OFF CACHE BOOL "" FORCE) +# ----------------------------------------------------------------------------- +# Language Support. -if(WIN32) - set(WITH_WINDOWS_BUNDLE_CRT OFF CACHE BOOL "" FORCE) -endif() +set(WITH_INTERNATIONAL OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 5508e8f2104..d271d8f216f 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1208,16 +1208,8 @@ endmacro() macro(without_system_libs_begin) set(CMAKE_IGNORE_PATH "${CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES};${CMAKE_SYSTEM_INCLUDE_PATH};${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES};${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}") - if(APPLE) - # Avoid searching for headers in frameworks (like Mono), and libraries in LIBDIR. - set(CMAKE_FIND_FRAMEWORK NEVER) - endif() endmacro() macro(without_system_libs_end) unset(CMAKE_IGNORE_PATH) - if(APPLE) - # FIRST is the default. - set(CMAKE_FIND_FRAMEWORK FIRST) - endif() endmacro() diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index bc5baf43530..f2a8bd42a3e 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -36,6 +36,10 @@ endmacro() # ------------------------------------------------------------------------ # Find system provided libraries. +# Avoid searching for headers since this would otherwise override our lib +# directory as well as PYTHON_ROOT_DIR. +set(CMAKE_FIND_FRAMEWORK NEVER) + # Find system ZLIB, not the pre-compiled one supplied with OpenCollada. set(ZLIB_ROOT /usr) find_package(ZLIB REQUIRED) @@ -75,6 +79,11 @@ if(NOT EXISTS "${LIBDIR}/") message(FATAL_ERROR "Mac OSX requires pre-compiled libs at: '${LIBDIR}'") endif() +# Optionally use system Python if PYTHON_ROOT_DIR is specified. +if(WITH_PYTHON AND (WITH_PYTHON_MODULE AND PYTHON_ROOT_DIR)) + find_package(PythonLibsUnix REQUIRED) +endif() + # Prefer lib directory paths file(GLOB LIB_SUBDIRS ${LIBDIR}/*) set(CMAKE_PREFIX_PATH ${LIB_SUBDIRS}) @@ -123,34 +132,8 @@ if(WITH_CODEC_SNDFILE) unset(_sndfile_VORBISENC_LIBRARY) endif() -if(WITH_PYTHON) - # Use precompiled libraries by default. - set(PYTHON_VERSION 3.10) - if(NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK) - # Normally cached but not since we include them with blender. - set(PYTHON_INCLUDE_DIR "${LIBDIR}/python/include/python${PYTHON_VERSION}") - set(PYTHON_EXECUTABLE "${LIBDIR}/python/bin/python${PYTHON_VERSION}") - set(PYTHON_LIBRARY ${LIBDIR}/python/lib/libpython${PYTHON_VERSION}.a) - set(PYTHON_LIBPATH "${LIBDIR}/python/lib/python${PYTHON_VERSION}") - else() - # Module must be compiled against Python framework. - set(_py_framework "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}") - set(PYTHON_INCLUDE_DIR "${_py_framework}/include/python${PYTHON_VERSION}") - set(PYTHON_EXECUTABLE "${_py_framework}/bin/python${PYTHON_VERSION}") - set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}") - unset(_py_framework) - endif() - - # uncached vars - set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") - set(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") - - # needed for Audaspace, numpy is installed into python site-packages - set(PYTHON_NUMPY_INCLUDE_DIRS "${PYTHON_LIBPATH}/site-packages/numpy/core/include") - - if(NOT EXISTS "${PYTHON_EXECUTABLE}") - message(FATAL_ERROR "Python executable missing: ${PYTHON_EXECUTABLE}") - endif() +if(WITH_PYTHON AND NOT (WITH_PYTHON_MODULE AND PYTHON_ROOT_DIR)) + find_package(PythonLibsUnix REQUIRED) endif() if(WITH_FFTW3) @@ -213,11 +196,6 @@ if(WITH_JACK) string(APPEND PLATFORM_LINKFLAGS " -F/Library/Frameworks -weak_framework jackmp") endif() -if(WITH_PYTHON_MODULE OR WITH_PYTHON_FRAMEWORK) - # force cmake to link right framework - string(APPEND PLATFORM_LINKFLAGS " /Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}/Python") -endif() - if(WITH_OPENCOLLADA) find_package(OpenCOLLADA) find_library(PCRE_LIBRARIES NAMES pcre HINTS ${LIBDIR}/opencollada/lib) @@ -462,6 +440,9 @@ if(EXISTS ${LIBDIR}) without_system_libs_end() endif() +# Restore to default. +set(CMAKE_FIND_FRAMEWORK FIRST) + # --------------------------------------------------------------------- # Set compiler and linker flags. diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index c321da80649..f65fda83504 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -16,9 +16,16 @@ if(NOT DEFINED LIBDIR) # Choose the best suitable libraries. if(EXISTS ${LIBDIR_NATIVE_ABI}) set(LIBDIR ${LIBDIR_NATIVE_ABI}) + set(WITH_LIBC_MALLOC_HOOK_WORKAROUND True) elseif(EXISTS ${LIBDIR_CENTOS7_ABI}) set(LIBDIR ${LIBDIR_CENTOS7_ABI}) set(WITH_CXX11_ABI OFF) + if(WITH_MEM_JEMALLOC) + # jemalloc provides malloc hooks. + set(WITH_LIBC_MALLOC_HOOK_WORKAROUND False) + else() + set(WITH_LIBC_MALLOC_HOOK_WORKAROUND True) + endif() if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.3) diff --git a/build_files/cmake/project_source_info.py b/build_files/cmake/project_source_info.py index a544f5733f0..f29d068044c 100644 --- a/build_files/cmake/project_source_info.py +++ b/build_files/cmake/project_source_info.py @@ -30,6 +30,8 @@ from typing import ( cast, ) +import shlex + SOURCE_DIR = join(dirname(__file__), "..", "..") SOURCE_DIR = normpath(SOURCE_DIR) @@ -160,7 +162,7 @@ def build_info( for c in compilers: args = args.replace(c, fake_compiler) - args = args.split() + args = shlex.split(args) # end # remove compiler diff --git a/intern/cycles/blender/session.cpp b/intern/cycles/blender/session.cpp index 321771b67a5..1b7aa38efb4 100644 --- a/intern/cycles/blender/session.cpp +++ b/intern/cycles/blender/session.cpp @@ -704,7 +704,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, buffer_params.window_width = bake_width; buffer_params.window_height = bake_height; /* Unique layer name for multi-image baking. */ - buffer_params.layer = string_printf("bake_%d\n", (int)full_buffer_files_.size()); + buffer_params.layer = string_printf("bake_%d\n", bake_id++); /* Update session. */ session->reset(session_params, buffer_params); diff --git a/intern/cycles/blender/session.h b/intern/cycles/blender/session.h index f9a5b6faf7e..ceca86016b8 100644 --- a/intern/cycles/blender/session.h +++ b/intern/cycles/blender/session.h @@ -146,6 +146,8 @@ class BlenderSession { BlenderDisplayDriver *display_driver_ = nullptr; vector<string> full_buffer_files_; + + int bake_id = 0; }; CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index 1e4b9baa0c0..a2b8d1cbbfa 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -28,7 +28,6 @@ #include "kernel/device/cpu/kernel.h" #include "kernel/types.h" -#include "kernel/osl/shader.h" #include "kernel/osl/globals.h" // clang-format on diff --git a/intern/cycles/device/cpu/device_impl.h b/intern/cycles/device/cpu/device_impl.h index 5c1f3cc6ce5..e7e77f18194 100644 --- a/intern/cycles/device/cpu/device_impl.h +++ b/intern/cycles/device/cpu/device_impl.h @@ -23,7 +23,6 @@ #include "kernel/device/cpu/kernel.h" #include "kernel/device/cpu/globals.h" -#include "kernel/osl/shader.h" #include "kernel/osl/globals.h" // clang-format on diff --git a/intern/cycles/device/cpu/kernel_thread_globals.cpp b/intern/cycles/device/cpu/kernel_thread_globals.cpp index 89545399602..99af1525d92 100644 --- a/intern/cycles/device/cpu/kernel_thread_globals.cpp +++ b/intern/cycles/device/cpu/kernel_thread_globals.cpp @@ -3,10 +3,7 @@ #include "device/cpu/kernel_thread_globals.h" -// clang-format off -#include "kernel/osl/shader.h" #include "kernel/osl/globals.h" -// clang-format on #include "util/profiling.h" @@ -20,7 +17,7 @@ CPUKernelThreadGlobals::CPUKernelThreadGlobals(const KernelGlobalsCPU &kernel_gl reset_runtime_memory(); #ifdef WITH_OSL - OSLShader::thread_init(this, reinterpret_cast<OSLGlobals *>(osl_globals_memory)); + OSLGlobals::thread_init(this, static_cast<OSLGlobals *>(osl_globals_memory)); #else (void)osl_globals_memory; #endif @@ -35,7 +32,7 @@ CPUKernelThreadGlobals::CPUKernelThreadGlobals(CPUKernelThreadGlobals &&other) n CPUKernelThreadGlobals::~CPUKernelThreadGlobals() { #ifdef WITH_OSL - OSLShader::thread_free(this); + OSLGlobals::thread_free(this); #endif } diff --git a/intern/cycles/device/oneapi/device.cpp b/intern/cycles/device/oneapi/device.cpp index 8056c204188..4aa307e9300 100644 --- a/intern/cycles/device/oneapi/device.cpp +++ b/intern/cycles/device/oneapi/device.cpp @@ -25,10 +25,12 @@ static OneAPIDLLInterface oneapi_dll; #ifdef _WIN32 # define LOAD_ONEAPI_SHARED_LIBRARY(path) (void *)(LoadLibrary(path)) +# define LOAD_ONEAPI_SHARED_LIBRARY_ERROR() GetLastError() # define FREE_SHARED_LIBRARY(handle) FreeLibrary((HMODULE)handle) # define GET_SHARED_LIBRARY_SYMBOL(handle, name) GetProcAddress((HMODULE)handle, name) #elif __linux__ # define LOAD_ONEAPI_SHARED_LIBRARY(path) dlopen(path, RTLD_NOW) +# define LOAD_ONEAPI_SHARED_LIBRARY_ERROR() dlerror() # define FREE_SHARED_LIBRARY(handle) dlclose(handle) # define GET_SHARED_LIBRARY_SYMBOL(handle, name) dlsym(handle, name) #endif @@ -49,8 +51,8 @@ bool device_oneapi_init() /* This shouldn't happen, but it still makes sense to have a branch for this. */ if (lib_handle == NULL) { - LOG(ERROR) << "oneAPI kernel shared library cannot be loaded for some reason. This should not " - "happen, however, it occurs hence oneAPI rendering will be disabled"; + LOG(ERROR) << "oneAPI kernel shared library cannot be loaded: " + << LOAD_ONEAPI_SHARED_LIBRARY_ERROR(); return false; } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 6d84357b699..a89c5679b27 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -544,8 +544,6 @@ if(WITH_CYCLES_CUDA_BINARIES) cycles_set_solution_folder(cycles_kernel_cuda) endif() -####################################################### START - # HIP module if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) @@ -620,7 +618,6 @@ if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) cycles_set_solution_folder(cycles_kernel_hip) endif() -####################################################### END # OptiX PTX modules if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) @@ -712,6 +709,8 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) cycles_set_solution_folder(cycles_kernel_optix) endif() +# oneAPI module + if(WITH_CYCLES_DEVICE_ONEAPI) if(WIN32) set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/cycles_kernel_oneapi.dll) @@ -793,7 +792,7 @@ if(WITH_CYCLES_DEVICE_ONEAPI) if(UNIX AND NOT APPLE) if(NOT WITH_CXX11_ABI) check_library_exists(sycl - _ZN2cl4sycl7handler22verifyUsedKernelBundleERKSs ${sycl_compiler_root}/../lib SYCL_NO_CXX11_ABI) + _ZN4sycl3_V17handler22verifyUsedKernelBundleERKSs ${sycl_compiler_root}/../lib SYCL_NO_CXX11_ABI) if(SYCL_NO_CXX11_ABI) list(APPEND sycl_compiler_flags -D_GLIBCXX_USE_CXX11_ABI=0) endif() diff --git a/intern/cycles/kernel/closure/alloc.h b/intern/cycles/kernel/closure/alloc.h index 9847898ee89..1cf06614f3b 100644 --- a/intern/cycles/kernel/closure/alloc.h +++ b/intern/cycles/kernel/closure/alloc.h @@ -59,39 +59,10 @@ ccl_device_inline ccl_private ShaderClosure *bsdf_alloc(ccl_private ShaderData * * we will not allocate new closure. */ if (sample_weight >= CLOSURE_WEIGHT_CUTOFF) { ccl_private ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight); - if (sc == NULL) { - return NULL; - } - - sc->sample_weight = sample_weight; - - return sc; - } - - return NULL; -} - -#ifdef __OSL__ -ccl_device_inline ShaderClosure *bsdf_alloc_osl(ShaderData *sd, - int size, - Spectrum weight, - void *data) -{ - kernel_assert(isfinite_safe(weight)); - - const float sample_weight = fabsf(average(weight)); - - /* Use comparison this way to help dealing with non-finite weight: if the average is not finite - * we will not allocate new closure. */ - if (sample_weight >= CLOSURE_WEIGHT_CUTOFF) { - ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight); if (!sc) { return NULL; } - memcpy((void *)sc, data, size); - - sc->weight = weight; sc->sample_weight = sample_weight; return sc; @@ -99,6 +70,5 @@ ccl_device_inline ShaderClosure *bsdf_alloc_osl(ShaderData *sd, return NULL; } -#endif CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 02cf8bfe3e2..f0b28ff77c4 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -116,7 +116,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg, case CLOSURE_BSDF_DIFFUSE_ID: label = bsdf_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; -#ifdef __SVM__ +#if defined(__SVM__) || defined(__OSL__) case CLOSURE_BSDF_OREN_NAYAR_ID: label = bsdf_oren_nayar_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; @@ -246,7 +246,7 @@ ccl_device_inline case CLOSURE_BSDF_DIFFUSE_ID: eval = bsdf_diffuse_eval_reflect(sc, sd->I, omega_in, pdf); break; -#ifdef __SVM__ +#if defined(__SVM__) || defined(__OSL__) case CLOSURE_BSDF_OREN_NAYAR_ID: eval = bsdf_oren_nayar_eval_reflect(sc, sd->I, omega_in, pdf); break; @@ -337,7 +337,7 @@ ccl_device_inline case CLOSURE_BSDF_DIFFUSE_ID: eval = bsdf_diffuse_eval_transmit(sc, sd->I, omega_in, pdf); break; -#ifdef __SVM__ +#if defined(__SVM__) || defined(__OSL__) case CLOSURE_BSDF_OREN_NAYAR_ID: eval = bsdf_oren_nayar_eval_transmit(sc, sd->I, omega_in, pdf); break; @@ -419,7 +419,7 @@ ccl_device_inline ccl_device void bsdf_blur(KernelGlobals kg, ccl_private ShaderClosure *sc, float roughness) { /* TODO: do we want to blur volume closures? */ -#ifdef __SVM__ +#if defined(__SVM__) || defined(__OSL__) switch (sc->type) { case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index ac37a648a2c..9402ce11f7a 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -371,7 +371,7 @@ ccl_device void bsdf_microfacet_multi_ggx_blur(ccl_private ShaderClosure *sc, fl /* === Closure implementations === */ -/* Multiscattering GGX Glossy closure */ +/* Multi-scattering GGX Glossy closure */ ccl_device int bsdf_microfacet_multi_ggx_common_setup(ccl_private MicrofacetBsdf *bsdf) { @@ -546,7 +546,7 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg, return LABEL_REFLECT | LABEL_GLOSSY; } -/* Multiscattering GGX Glass closure */ +/* Multi-scattering GGX Glass closure */ ccl_device int bsdf_microfacet_multi_ggx_glass_setup(ccl_private MicrofacetBsdf *bsdf) { diff --git a/intern/cycles/kernel/film/data_passes.h b/intern/cycles/kernel/film/data_passes.h index d14b3cea989..efdf616749f 100644 --- a/intern/cycles/kernel/film/data_passes.h +++ b/intern/cycles/kernel/film/data_passes.h @@ -5,6 +5,8 @@ #include "kernel/geom/geom.h" +#include "kernel/camera/camera.h" + #include "kernel/film/cryptomatte_passes.h" #include "kernel/film/write.h" diff --git a/intern/cycles/kernel/geom/attribute.h b/intern/cycles/kernel/geom/attribute.h index 31a9e39d528..3a0ee1b09d1 100644 --- a/intern/cycles/kernel/geom/attribute.h +++ b/intern/cycles/kernel/geom/attribute.h @@ -16,14 +16,14 @@ CCL_NAMESPACE_BEGIN /* Patch index for triangle, -1 if not subdivision triangle */ -ccl_device_inline uint subd_triangle_patch(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device_inline uint subd_triangle_patch(KernelGlobals kg, int prim) { - return (sd->prim != PRIM_NONE) ? kernel_data_fetch(tri_patch, sd->prim) : ~0; + return (prim != PRIM_NONE) ? kernel_data_fetch(tri_patch, prim) : ~0; } -ccl_device_inline uint attribute_primitive_type(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device_inline uint attribute_primitive_type(KernelGlobals kg, int prim, int type) { - if ((sd->type & PRIMITIVE_TRIANGLE) && subd_triangle_patch(kg, sd) != ~0) { + if ((type & PRIMITIVE_TRIANGLE) && subd_triangle_patch(kg, prim) != ~0) { return ATTR_PRIM_SUBD; } else { @@ -45,17 +45,16 @@ ccl_device_inline uint object_attribute_map_offset(KernelGlobals kg, int object) return kernel_data_fetch(objects, object).attribute_map_offset; } -ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg, - ccl_private const ShaderData *sd, - uint id) +ccl_device_inline AttributeDescriptor +find_attribute(KernelGlobals kg, int object, int prim, int type, uint64_t id) { - if (sd->object == OBJECT_NONE) { + if (object == OBJECT_NONE) { return attribute_not_found(); } /* for SVM, find attribute by unique id */ - uint attr_offset = object_attribute_map_offset(kg, sd->object); - attr_offset += attribute_primitive_type(kg, sd); + uint attr_offset = object_attribute_map_offset(kg, object); + attr_offset += attribute_primitive_type(kg, prim, type); AttributeMap attr_map = kernel_data_fetch(attributes_map, attr_offset); while (attr_map.id != id) { @@ -77,7 +76,7 @@ ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg, AttributeDescriptor desc; desc.element = (AttributeElement)attr_map.element; - if (sd->prim == PRIM_NONE && desc.element != ATTR_ELEMENT_MESH && + if (prim == PRIM_NONE && desc.element != ATTR_ELEMENT_MESH && desc.element != ATTR_ELEMENT_VOXEL && desc.element != ATTR_ELEMENT_OBJECT) { return attribute_not_found(); } @@ -91,11 +90,16 @@ ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg, return desc; } +ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg, + ccl_private const ShaderData *sd, + uint64_t id) +{ + return find_attribute(kg, sd->object, sd->prim, sd->type, id); +} + /* Transform matrix attribute on meshes */ -ccl_device Transform primitive_attribute_matrix(KernelGlobals kg, - ccl_private const ShaderData *sd, - const AttributeDescriptor desc) +ccl_device Transform primitive_attribute_matrix(KernelGlobals kg, const AttributeDescriptor desc) { Transform tfm; diff --git a/intern/cycles/kernel/geom/primitive.h b/intern/cycles/kernel/geom/primitive.h index 0f1a3fc11bc..04b04ff5985 100644 --- a/intern/cycles/kernel/geom/primitive.h +++ b/intern/cycles/kernel/geom/primitive.h @@ -25,7 +25,7 @@ ccl_device_forceinline float primitive_surface_attribute_float(KernelGlobals kg, ccl_private float *dy) { if (sd->type & PRIMITIVE_TRIANGLE) { - if (subd_triangle_patch(kg, sd) == ~0) + if (subd_triangle_patch(kg, sd->prim) == ~0) return triangle_attribute_float(kg, sd, desc, dx, dy); else return subd_triangle_attribute_float(kg, sd, desc, dx, dy); @@ -56,7 +56,7 @@ ccl_device_forceinline float2 primitive_surface_attribute_float2(KernelGlobals k ccl_private float2 *dy) { if (sd->type & PRIMITIVE_TRIANGLE) { - if (subd_triangle_patch(kg, sd) == ~0) + if (subd_triangle_patch(kg, sd->prim) == ~0) return triangle_attribute_float2(kg, sd, desc, dx, dy); else return subd_triangle_attribute_float2(kg, sd, desc, dx, dy); @@ -87,7 +87,7 @@ ccl_device_forceinline float3 primitive_surface_attribute_float3(KernelGlobals k ccl_private float3 *dy) { if (sd->type & PRIMITIVE_TRIANGLE) { - if (subd_triangle_patch(kg, sd) == ~0) + if (subd_triangle_patch(kg, sd->prim) == ~0) return triangle_attribute_float3(kg, sd, desc, dx, dy); else return subd_triangle_attribute_float3(kg, sd, desc, dx, dy); @@ -118,7 +118,7 @@ ccl_device_forceinline float4 primitive_surface_attribute_float4(KernelGlobals k ccl_private float4 *dy) { if (sd->type & PRIMITIVE_TRIANGLE) { - if (subd_triangle_patch(kg, sd) == ~0) + if (subd_triangle_patch(kg, sd->prim) == ~0) return triangle_attribute_float4(kg, sd, desc, dx, dy); else return subd_triangle_attribute_float4(kg, sd, desc, dx, dy); @@ -320,7 +320,7 @@ ccl_device_forceinline float4 primitive_motion_vector(KernelGlobals kg, #endif if (sd->type & PRIMITIVE_TRIANGLE) { /* Triangle */ - if (subd_triangle_patch(kg, sd) == ~0) { + if (subd_triangle_patch(kg, sd->prim) == ~0) { motion_pre = triangle_attribute_float3(kg, sd, desc, NULL, NULL); desc.offset += numverts; motion_post = triangle_attribute_float3(kg, sd, desc, NULL, NULL); diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h index 028c03ace1d..b67d19365a3 100644 --- a/intern/cycles/kernel/geom/shader_data.h +++ b/intern/cycles/kernel/geom/shader_data.h @@ -7,6 +7,8 @@ #pragma once +#include "kernel/util/differential.h" + CCL_NAMESPACE_BEGIN /* ShaderData setup from incoming ray */ diff --git a/intern/cycles/kernel/geom/subd_triangle.h b/intern/cycles/kernel/geom/subd_triangle.h index c6f883461bd..784ba377318 100644 --- a/intern/cycles/kernel/geom/subd_triangle.h +++ b/intern/cycles/kernel/geom/subd_triangle.h @@ -87,7 +87,7 @@ ccl_device_noinline float subd_triangle_attribute_float(KernelGlobals kg, ccl_private float *dx, ccl_private float *dy) { - int patch = subd_triangle_patch(kg, sd); + int patch = subd_triangle_patch(kg, sd->prim); #ifdef __PATCH_EVAL__ if (desc.flags & ATTR_SUBDIVIDED) { @@ -226,7 +226,7 @@ ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals kg, ccl_private float2 *dx, ccl_private float2 *dy) { - int patch = subd_triangle_patch(kg, sd); + int patch = subd_triangle_patch(kg, sd->prim); #ifdef __PATCH_EVAL__ if (desc.flags & ATTR_SUBDIVIDED) { @@ -368,7 +368,7 @@ ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals kg, ccl_private float3 *dx, ccl_private float3 *dy) { - int patch = subd_triangle_patch(kg, sd); + int patch = subd_triangle_patch(kg, sd->prim); #ifdef __PATCH_EVAL__ if (desc.flags & ATTR_SUBDIVIDED) { @@ -509,7 +509,7 @@ ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals kg, ccl_private float4 *dx, ccl_private float4 *dy) { - int patch = subd_triangle_patch(kg, sd); + int patch = subd_triangle_patch(kg, sd->prim); #ifdef __PATCH_EVAL__ if (desc.flags & ATTR_SUBDIVIDED) { diff --git a/intern/cycles/kernel/geom/volume.h b/intern/cycles/kernel/geom/volume.h index 3510a905def..885a420c97f 100644 --- a/intern/cycles/kernel/geom/volume.h +++ b/intern/cycles/kernel/geom/volume.h @@ -29,7 +29,7 @@ ccl_device_inline float3 volume_normalized_position(KernelGlobals kg, object_inverse_position_transform(kg, sd, &P); if (desc.offset != ATTR_STD_NOT_FOUND) { - Transform tfm = primitive_attribute_matrix(kg, sd, desc); + Transform tfm = primitive_attribute_matrix(kg, desc); P = transform_point(&tfm, P); } diff --git a/intern/cycles/kernel/integrator/displacement_shader.h b/intern/cycles/kernel/integrator/displacement_shader.h index 71a0f56fb3e..839dfe244ac 100644 --- a/intern/cycles/kernel/integrator/displacement_shader.h +++ b/intern/cycles/kernel/integrator/displacement_shader.h @@ -5,10 +5,11 @@ #pragma once -#include "kernel/svm/svm.h" - +#ifdef __SVM__ +# include "kernel/svm/svm.h" +#endif #ifdef __OSL__ -# include "kernel/osl/shader.h" +# include "kernel/osl/osl.h" #endif CCL_NAMESPACE_BEGIN @@ -22,17 +23,18 @@ ccl_device void displacement_shader_eval(KernelGlobals kg, sd->num_closure_left = 0; /* this will modify sd->P */ -#ifdef __SVM__ -# ifdef __OSL__ - if (kg->osl) +#ifdef __OSL__ + if (kg->osl) { OSLShader::eval_displacement(kg, state, sd); + } else -# endif +#endif { +#ifdef __SVM__ svm_eval_nodes<KERNEL_FEATURE_NODE_MASK_DISPLACEMENT, SHADER_TYPE_DISPLACEMENT>( kg, state, sd, NULL, 0); - } #endif + } } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index 4ecff56a3fd..c7c3d74fa21 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -12,8 +12,6 @@ #include "kernel/light/light.h" -#include "kernel/util/differential.h" - #include "kernel/geom/geom.h" #include "kernel/bvh/bvh.h" diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index a4246f99bbf..f2d65eddfbb 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -62,8 +62,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg, /* multiple importance sampling, get regular light pdf, * and compute weight with respect to BSDF pdf */ const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - const float mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf); - light_eval *= mis_weight; + mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf); } /* Write to render buffer. */ diff --git a/intern/cycles/kernel/integrator/surface_shader.h b/intern/cycles/kernel/integrator/surface_shader.h index f40ff3c33ee..64b5556f7e9 100644 --- a/intern/cycles/kernel/integrator/surface_shader.h +++ b/intern/cycles/kernel/integrator/surface_shader.h @@ -10,10 +10,11 @@ #include "kernel/closure/bsdf_util.h" #include "kernel/closure/emissive.h" -#include "kernel/svm/svm.h" - +#ifdef __SVM__ +# include "kernel/svm/svm.h" +#endif #ifdef __OSL__ -# include "kernel/osl/shader.h" +# include "kernel/osl/osl.h" #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/kernel/integrator/volume_shader.h b/intern/cycles/kernel/integrator/volume_shader.h index a1d191e2d32..31039bfdcf5 100644 --- a/intern/cycles/kernel/integrator/volume_shader.h +++ b/intern/cycles/kernel/integrator/volume_shader.h @@ -10,10 +10,11 @@ #include "kernel/closure/bsdf_util.h" #include "kernel/closure/emissive.h" -#include "kernel/svm/svm.h" - +#ifdef __SVM__ +# include "kernel/svm/svm.h" +#endif #ifdef __OSL__ -# include "kernel/osl/shader.h" +# include "kernel/osl/osl.h" #endif CCL_NAMESPACE_BEGIN @@ -326,18 +327,18 @@ ccl_device_inline void volume_shader_eval(KernelGlobals kg, } /* evaluate shader */ -# ifdef __SVM__ -# ifdef __OSL__ +# ifdef __OSL__ if (kg->osl) { OSLShader::eval_volume(kg, state, sd, path_flag); } else -# endif +# endif { +# ifdef __SVM__ svm_eval_nodes<KERNEL_FEATURE_NODE_MASK_VOLUME, SHADER_TYPE_VOLUME>( kg, state, sd, NULL, path_flag); - } # endif + } /* Merge closures to avoid exceeding number of closures limit. */ if (!shadow) { diff --git a/intern/cycles/kernel/osl/CMakeLists.txt b/intern/cycles/kernel/osl/CMakeLists.txt index 7570490be7c..5075e4e1528 100644 --- a/intern/cycles/kernel/osl/CMakeLists.txt +++ b/intern/cycles/kernel/osl/CMakeLists.txt @@ -10,21 +10,18 @@ set(INC_SYS ) set(SRC - background.cpp - bsdf_diffuse_ramp.cpp - bsdf_phong_ramp.cpp - emissive.cpp - bssrdf.cpp closures.cpp + globals.cpp services.cpp - shader.cpp ) set(HEADER_SRC - closures.h + closures_setup.h + closures_template.h globals.h + osl.h services.h - shader.h + types.h ) set(LIB diff --git a/intern/cycles/kernel/osl/background.cpp b/intern/cycles/kernel/osl/background.cpp deleted file mode 100644 index 4b5a2686117..00000000000 --- a/intern/cycles/kernel/osl/background.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Adapted from Open Shading Language - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2011-2022 Blender Foundation. */ - -#include <OpenImageIO/fmath.h> - -#include <OSL/genclosure.h> - -#include "kernel/osl/closures.h" - -// clang-format off -#include "kernel/device/cpu/compat.h" -#include "kernel/device/cpu/globals.h" - -#include "kernel/closure/alloc.h" -#include "kernel/closure/emissive.h" - -#include "kernel/util/color.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -using namespace OSL; - -/// Generic background closure -/// -/// We only have a background closure for the shaders -/// to return a color in background shaders. No methods, -/// only the weight is taking into account -/// -class GenericBackgroundClosure : public CClosurePrimitive { - public: - void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) - { - background_setup(sd, rgb_to_spectrum(weight)); - } -}; - -/// Holdout closure -/// -/// This will be used by the shader to mark the -/// amount of holdout for the current shading -/// point. No parameters, only the weight will be -/// used -/// -class HoldoutClosure : CClosurePrimitive { - public: - void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) - { - closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, rgb_to_spectrum(weight)); - sd->flag |= SD_HOLDOUT; - } -}; - -ClosureParam *closure_background_params() -{ - static ClosureParam params[] = { - CLOSURE_STRING_KEYPARAM(GenericBackgroundClosure, label, "label"), - CLOSURE_FINISH_PARAM(GenericBackgroundClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_background_prepare, GenericBackgroundClosure) - -ClosureParam *closure_holdout_params() -{ - static ClosureParam params[] = {CLOSURE_FINISH_PARAM(HoldoutClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_holdout_prepare, HoldoutClosure) - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp deleted file mode 100644 index 667207ec6bf..00000000000 --- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Adapted from Open Shading Language - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2011-2022 Blender Foundation. */ - -#include <OpenImageIO/fmath.h> - -#include <OSL/genclosure.h> - -#include "kernel/device/cpu/compat.h" -#include "kernel/osl/closures.h" - -// clang-format off -#include "kernel/device/cpu/compat.h" -#include "kernel/device/cpu/globals.h" - -#include "kernel/types.h" -#include "kernel/closure/alloc.h" -#include "kernel/closure/bsdf_diffuse_ramp.h" -#include "kernel/closure/bsdf_util.h" - -#include "kernel/util/color.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -using namespace OSL; - -class DiffuseRampClosure : public CBSDFClosure { - public: - DiffuseRampBsdf params; - Color3 colors[8]; - - void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - DiffuseRampBsdf *bsdf = (DiffuseRampBsdf *)bsdf_alloc_osl( - sd, sizeof(DiffuseRampBsdf), rgb_to_spectrum(weight), ¶ms); - - if (bsdf) { - bsdf->colors = (float3 *)closure_alloc_extra(sd, sizeof(float3) * 8); - - if (bsdf->colors) { - for (int i = 0; i < 8; i++) - bsdf->colors[i] = TO_FLOAT3(colors[i]); - - sd->flag |= bsdf_diffuse_ramp_setup(bsdf); - } - } - } -}; - -ClosureParam *closure_bsdf_diffuse_ramp_params() -{ - static ClosureParam params[] = {CLOSURE_FLOAT3_PARAM(DiffuseRampClosure, params.N), - CLOSURE_COLOR_ARRAY_PARAM(DiffuseRampClosure, colors, 8), - CLOSURE_STRING_KEYPARAM(DiffuseRampClosure, label, "label"), - CLOSURE_FINISH_PARAM(DiffuseRampClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_bsdf_diffuse_ramp_prepare, DiffuseRampClosure) - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp deleted file mode 100644 index 6f54a96e542..00000000000 --- a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Adapted from Open Shading Language - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2011-2022 Blender Foundation. */ - -#include <OpenImageIO/fmath.h> - -#include <OSL/genclosure.h> - -#include "kernel/device/cpu/compat.h" -#include "kernel/osl/closures.h" - -// clang-format off -#include "kernel/device/cpu/compat.h" -#include "kernel/device/cpu/globals.h" - -#include "kernel/types.h" -#include "kernel/closure/alloc.h" -#include "kernel/closure/bsdf_phong_ramp.h" -#include "kernel/closure/bsdf_util.h" - -#include "kernel/util/color.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -using namespace OSL; - -class PhongRampClosure : public CBSDFClosure { - public: - PhongRampBsdf params; - Color3 colors[8]; - - void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - PhongRampBsdf *bsdf = (PhongRampBsdf *)bsdf_alloc_osl( - sd, sizeof(PhongRampBsdf), rgb_to_spectrum(weight), ¶ms); - - if (bsdf) { - bsdf->colors = (float3 *)closure_alloc_extra(sd, sizeof(float3) * 8); - - if (bsdf->colors) { - for (int i = 0; i < 8; i++) - bsdf->colors[i] = TO_FLOAT3(colors[i]); - - sd->flag |= bsdf_phong_ramp_setup(bsdf); - } - } - } -}; - -ClosureParam *closure_bsdf_phong_ramp_params() -{ - static ClosureParam params[] = {CLOSURE_FLOAT3_PARAM(PhongRampClosure, params.N), - CLOSURE_FLOAT_PARAM(PhongRampClosure, params.exponent), - CLOSURE_COLOR_ARRAY_PARAM(PhongRampClosure, colors, 8), - CLOSURE_STRING_KEYPARAM(PhongRampClosure, label, "label"), - CLOSURE_FINISH_PARAM(PhongRampClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_bsdf_phong_ramp_prepare, PhongRampClosure) - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/bssrdf.cpp b/intern/cycles/kernel/osl/bssrdf.cpp deleted file mode 100644 index 3054946ba5a..00000000000 --- a/intern/cycles/kernel/osl/bssrdf.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Adapted from Open Shading Language - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2011-2022 Blender Foundation. */ - -#include <OSL/genclosure.h> - -#include "kernel/device/cpu/compat.h" -#include "kernel/osl/closures.h" - -// clang-format off -#include "kernel/device/cpu/compat.h" -#include "kernel/device/cpu/globals.h" - -#include "kernel/types.h" - -#include "kernel/closure/alloc.h" -#include "kernel/closure/bsdf_util.h" -#include "kernel/closure/bsdf_diffuse.h" -#include "kernel/closure/bsdf_principled_diffuse.h" -#include "kernel/closure/bssrdf.h" - -#include "kernel/util/color.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -using namespace OSL; - -static ustring u_burley("burley"); -static ustring u_random_walk_fixed_radius("random_walk_fixed_radius"); -static ustring u_random_walk("random_walk"); - -class CBSSRDFClosure : public CClosurePrimitive { - public: - Bssrdf params; - float ior; - ustring method; - - CBSSRDFClosure() - { - params.roughness = FLT_MAX; - params.anisotropy = 1.0f; - ior = 1.4f; - } - - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - if (method == u_burley) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_BURLEY_ID); - } - else if (method == u_random_walk_fixed_radius) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); - } - else if (method == u_random_walk) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_ID); - } - } - - void alloc(ShaderData *sd, uint32_t path_flag, float3 weight, ClosureType type) - { - Bssrdf *bssrdf = bssrdf_alloc(sd, rgb_to_spectrum(weight)); - - if (bssrdf) { - /* disable in case of diffuse ancestor, can't see it well then and - * adds considerably noise due to probabilities of continuing path - * getting lower and lower */ - if (path_flag & PATH_RAY_DIFFUSE_ANCESTOR) { - params.radius = zero_spectrum(); - } - - /* create one closure per color channel */ - bssrdf->radius = params.radius; - bssrdf->albedo = params.albedo; - bssrdf->N = params.N; - bssrdf->roughness = params.roughness; - bssrdf->anisotropy = clamp(params.anisotropy, 0.0f, 0.9f); - sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type, clamp(ior, 1.01f, 3.8f)); - } - } -}; - -ClosureParam *closure_bssrdf_params() -{ - static ClosureParam params[] = { - CLOSURE_STRING_PARAM(CBSSRDFClosure, method), - CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.N), - CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.radius), - CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.albedo), - CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.roughness, "roughness"), - CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, ior, "ior"), - CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.anisotropy, "anisotropy"), - CLOSURE_STRING_KEYPARAM(CBSSRDFClosure, label, "label"), - CLOSURE_FINISH_PARAM(CBSSRDFClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_bssrdf_prepare, CBSSRDFClosure) - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/closures.cpp b/intern/cycles/kernel/osl/closures.cpp index 8766fb73dbb..d56e0551a91 100644 --- a/intern/cycles/kernel/osl/closures.cpp +++ b/intern/cycles/kernel/osl/closures.cpp @@ -9,999 +9,304 @@ #include <OSL/genclosure.h> #include <OSL/oslclosure.h> -#include "kernel/osl/closures.h" -#include "kernel/osl/shader.h" +#include "kernel/types.h" + +#include "kernel/osl/globals.h" +#include "kernel/osl/services.h" #include "util/math.h" #include "util/param.h" -// clang-format off #include "kernel/device/cpu/compat.h" #include "kernel/device/cpu/globals.h" -#include "kernel/types.h" - -#include "kernel/closure/alloc.h" -#include "kernel/closure/bsdf_util.h" -#include "kernel/closure/bsdf_ashikhmin_velvet.h" -#include "kernel/closure/bsdf_diffuse.h" -#include "kernel/closure/bsdf_microfacet.h" -#include "kernel/closure/bsdf_microfacet_multi.h" -#include "kernel/closure/bsdf_oren_nayar.h" -#include "kernel/closure/bsdf_reflection.h" -#include "kernel/closure/bsdf_refraction.h" -#include "kernel/closure/bsdf_transparent.h" -#include "kernel/closure/bsdf_ashikhmin_shirley.h" -#include "kernel/closure/bsdf_toon.h" -#include "kernel/closure/bsdf_hair.h" -#include "kernel/closure/bsdf_hair_principled.h" -#include "kernel/closure/bsdf_principled_diffuse.h" -#include "kernel/closure/bsdf_principled_sheen.h" -#include "kernel/closure/volume.h" - -#include "kernel/util/color.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -using namespace OSL; - -/* BSDF class definitions */ - -BSDF_CLOSURE_CLASS_BEGIN(Diffuse, diffuse, DiffuseBsdf, LABEL_DIFFUSE) - BSDF_CLOSURE_FLOAT3_PARAM(DiffuseClosure, params.N) -BSDF_CLOSURE_CLASS_END(Diffuse, diffuse) - -BSDF_CLOSURE_CLASS_BEGIN(Translucent, translucent, DiffuseBsdf, LABEL_DIFFUSE) - BSDF_CLOSURE_FLOAT3_PARAM(TranslucentClosure, params.N) -BSDF_CLOSURE_CLASS_END(Translucent, translucent) - -BSDF_CLOSURE_CLASS_BEGIN(OrenNayar, oren_nayar, OrenNayarBsdf, LABEL_DIFFUSE) - BSDF_CLOSURE_FLOAT3_PARAM(OrenNayarClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(OrenNayarClosure, params.roughness) -BSDF_CLOSURE_CLASS_END(OrenNayar, oren_nayar) - -BSDF_CLOSURE_CLASS_BEGIN(Reflection, reflection, MicrofacetBsdf, LABEL_SINGULAR) - BSDF_CLOSURE_FLOAT3_PARAM(ReflectionClosure, params.N) -BSDF_CLOSURE_CLASS_END(Reflection, reflection) - -BSDF_CLOSURE_CLASS_BEGIN(Refraction, refraction, MicrofacetBsdf, LABEL_SINGULAR) - BSDF_CLOSURE_FLOAT3_PARAM(RefractionClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(RefractionClosure, params.ior) -BSDF_CLOSURE_CLASS_END(Refraction, refraction) - -BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, VelvetBsdf, LABEL_DIFFUSE) - BSDF_CLOSURE_FLOAT3_PARAM(AshikhminVelvetClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, params.sigma) -BSDF_CLOSURE_CLASS_END(AshikhminVelvet, ashikhmin_velvet) - -BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, - ashikhmin_shirley, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - BSDF_CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.N) - BSDF_CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.T) - BSDF_CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_x) - BSDF_CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_y) -BSDF_CLOSURE_CLASS_END(AshikhminShirley, ashikhmin_shirley) - -BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, ToonBsdf, LABEL_DIFFUSE) - BSDF_CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.size) - BSDF_CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.smooth) -BSDF_CLOSURE_CLASS_END(DiffuseToon, diffuse_toon) - -BSDF_CLOSURE_CLASS_BEGIN(GlossyToon, glossy_toon, ToonBsdf, LABEL_GLOSSY) - BSDF_CLOSURE_FLOAT3_PARAM(GlossyToonClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.size) - BSDF_CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.smooth) -BSDF_CLOSURE_CLASS_END(GlossyToon, glossy_toon) - -BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXIsotropic, - microfacet_ggx_isotropic, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXIsotropicClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXIsotropicClosure, params.alpha_x) -BSDF_CLOSURE_CLASS_END(MicrofacetGGXIsotropic, microfacet_ggx_isotropic) - -BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, - microfacet_ggx, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.N) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.T) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_x) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_y) -BSDF_CLOSURE_CLASS_END(MicrofacetGGX, microfacet_ggx) - -BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannIsotropic, - microfacet_beckmann_isotropic, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannIsotropicClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannIsotropicClosure, params.alpha_x) -BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannIsotropic, microfacet_beckmann_isotropic) - -BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, - microfacet_beckmann, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.N) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.T) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_x) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_y) -BSDF_CLOSURE_CLASS_END(MicrofacetBeckmann, microfacet_beckmann) - -BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, - microfacet_ggx_refraction, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_TRANSMIT) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.alpha_x) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.ior) -BSDF_CLOSURE_CLASS_END(MicrofacetGGXRefraction, microfacet_ggx_refraction) - -BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannRefraction, - microfacet_beckmann_refraction, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_TRANSMIT) - BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannRefractionClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.alpha_x) - BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.ior) -BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction) - -BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, HairBsdf, LABEL_GLOSSY) - BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness1) - BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness2) - BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T) - BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset) -BSDF_CLOSURE_CLASS_END(HairReflection, hair_reflection) - -BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, HairBsdf, LABEL_GLOSSY) - BSDF_CLOSURE_FLOAT3_PARAM(HairTransmissionClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness1) - BSDF_CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness2) - BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T) - BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset) -BSDF_CLOSURE_CLASS_END(HairTransmission, hair_transmission) - -BSDF_CLOSURE_CLASS_BEGIN(PrincipledDiffuse, - principled_diffuse, - PrincipledDiffuseBsdf, - LABEL_DIFFUSE) - BSDF_CLOSURE_FLOAT3_PARAM(PrincipledDiffuseClosure, params.N) - BSDF_CLOSURE_FLOAT_PARAM(PrincipledDiffuseClosure, params.roughness) -BSDF_CLOSURE_CLASS_END(PrincipledDiffuse, principled_diffuse) - -class PrincipledSheenClosure : public CBSDFClosure { - public: - PrincipledSheenBsdf params; - - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - if (!skip(sd, path_flag, LABEL_DIFFUSE)) { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)bsdf_alloc_osl( - sd, sizeof(PrincipledSheenBsdf), rgb_to_spectrum(weight), ¶ms); - sd->flag |= (bsdf) ? bsdf_principled_sheen_setup(sd, bsdf) : 0; - } - } -}; - -static ClosureParam *bsdf_principled_sheen_params() -{ - static ClosureParam params[] = {CLOSURE_FLOAT3_PARAM(PrincipledSheenClosure, params.N), - CLOSURE_STRING_KEYPARAM(PrincipledSheenClosure, label, "label"), - CLOSURE_FINISH_PARAM(PrincipledSheenClosure)}; - return params; -} - -CCLOSURE_PREPARE_STATIC(closure_bsdf_principled_sheen_prepare, PrincipledSheenClosure) - -/* PRINCIPLED HAIR BSDF */ -class PrincipledHairClosure : public CBSDFClosure { - public: - PrincipledHairBSDF params; - - PrincipledHairBSDF *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) - { - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)bsdf_alloc_osl( - sd, sizeof(PrincipledHairBSDF), rgb_to_spectrum(weight), ¶ms); - if (!bsdf) { - return NULL; - } - - PrincipledHairExtra *extra = (PrincipledHairExtra *)closure_alloc_extra( - sd, sizeof(PrincipledHairExtra)); - if (!extra) { - return NULL; - } - - bsdf->extra = extra; - return bsdf; - } +#include "kernel/geom/object.h" +#include "kernel/util/differential.h" - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - if (!skip(sd, path_flag, LABEL_GLOSSY)) { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); +#include "kernel/osl/osl.h" - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - - sd->flag |= (bsdf) ? bsdf_principled_hair_setup(sd, bsdf) : 0; - } - } -}; - -static ClosureParam *closure_bsdf_principled_hair_params() -{ - static ClosureParam params[] = {CLOSURE_FLOAT3_PARAM(PrincipledHairClosure, params.N), - CLOSURE_FLOAT3_PARAM(PrincipledHairClosure, params.sigma), - CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.v), - CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.s), - CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.m0_roughness), - CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.alpha), - CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.eta), - CLOSURE_STRING_KEYPARAM(PrincipledHairClosure, label, "label"), - CLOSURE_FINISH_PARAM(PrincipledHairClosure)}; - - return params; -} +#include "kernel/osl/closures_setup.h" -CCLOSURE_PREPARE(closure_bsdf_principled_hair_prepare, PrincipledHairClosure) +#define TO_VEC3(v) OSL::Vec3(v.x, v.y, v.z) +#define TO_FLOAT3(v) make_float3(v[0], v[1], v[2]) -/* DISNEY PRINCIPLED CLEARCOAT */ -class PrincipledClearcoatClosure : public CBSDFClosure { - public: - MicrofacetBsdf params; - float clearcoat, clearcoat_roughness; - - MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) - { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); - if (!bsdf) { - return NULL; - } - - MicrofacetExtra *extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); - if (!extra) { - return NULL; - } - - bsdf->T = zero_float3(); - bsdf->extra = extra; - bsdf->ior = 1.5f; - bsdf->alpha_x = clearcoat_roughness; - bsdf->alpha_y = clearcoat_roughness; - bsdf->extra->color = zero_spectrum(); - bsdf->extra->cspec0 = make_spectrum(0.04f); - bsdf->extra->clearcoat = clearcoat; - return bsdf; - } +CCL_NAMESPACE_BEGIN - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } +/* Registration */ - sd->flag |= bsdf_microfacet_ggx_clearcoat_setup(bsdf, sd); +#define OSL_CLOSURE_STRUCT_BEGIN(Upper, lower) \ + static OSL::ClosureParam *osl_closure_##lower##_params() \ + { \ + static OSL::ClosureParam params[] = { +#define OSL_CLOSURE_STRUCT_END(Upper, lower) \ + CLOSURE_STRING_KEYPARAM(Upper##Closure, label, "label"), CLOSURE_FINISH_PARAM(Upper##Closure) \ + } \ + ; \ + return params; \ } -}; +#define OSL_CLOSURE_STRUCT_MEMBER(Upper, TYPE, type, name, key) \ + CLOSURE_##TYPE##_KEYPARAM(Upper##Closure, name, key), +#define OSL_CLOSURE_STRUCT_ARRAY_MEMBER(Upper, TYPE, type, name, key, size) \ + CLOSURE_##TYPE##_ARRAY_PARAM(Upper##Closure, name, size), -ClosureParam *closure_bsdf_principled_clearcoat_params() -{ - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(PrincipledClearcoatClosure, params.N), - CLOSURE_FLOAT_PARAM(PrincipledClearcoatClosure, clearcoat), - CLOSURE_FLOAT_PARAM(PrincipledClearcoatClosure, clearcoat_roughness), - CLOSURE_STRING_KEYPARAM(PrincipledClearcoatClosure, label, "label"), - CLOSURE_FINISH_PARAM(PrincipledClearcoatClosure)}; - return params; -} -CCLOSURE_PREPARE(closure_bsdf_principled_clearcoat_prepare, PrincipledClearcoatClosure) - -/* Registration */ +#include "closures_template.h" -static void register_closure(OSL::ShadingSystem *ss, - const char *name, - int id, - OSL::ClosureParam *params, - OSL::PrepareClosureFunc prepare) +void OSLRenderServices::register_closures(OSL::ShadingSystem *ss) { - /* optimization: it's possible to not use a prepare function at all and - * only initialize the actual class when accessing the closure component - * data, but then we need to map the id to the class somehow */ -#if OSL_LIBRARY_VERSION_CODE >= 10900 - ss->register_closure(name, id, params, prepare, NULL); -#else - ss->register_closure(name, id, params, prepare, NULL, 16); -#endif -} +#define OSL_CLOSURE_STRUCT_BEGIN(Upper, lower) \ + ss->register_closure( \ + #lower, OSL_CLOSURE_##Upper##_ID, osl_closure_##lower##_params(), nullptr, nullptr); -void OSLShader::register_closures(OSLShadingSystem *ss_) -{ - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)ss_; - int id = 0; - - register_closure(ss, "diffuse", id++, bsdf_diffuse_params(), bsdf_diffuse_prepare); - register_closure(ss, "oren_nayar", id++, bsdf_oren_nayar_params(), bsdf_oren_nayar_prepare); - register_closure(ss, "translucent", id++, bsdf_translucent_params(), bsdf_translucent_prepare); - register_closure(ss, "reflection", id++, bsdf_reflection_params(), bsdf_reflection_prepare); - register_closure(ss, "refraction", id++, bsdf_refraction_params(), bsdf_refraction_prepare); - register_closure(ss, - "transparent", - id++, - closure_bsdf_transparent_params(), - closure_bsdf_transparent_prepare); - - register_closure( - ss, "microfacet", id++, closure_bsdf_microfacet_params(), closure_bsdf_microfacet_prepare); - register_closure(ss, - "microfacet_ggx", - id++, - bsdf_microfacet_ggx_isotropic_params(), - bsdf_microfacet_ggx_isotropic_prepare); - register_closure( - ss, "microfacet_ggx_aniso", id++, bsdf_microfacet_ggx_params(), bsdf_microfacet_ggx_prepare); - register_closure(ss, - "microfacet_ggx_refraction", - id++, - bsdf_microfacet_ggx_refraction_params(), - bsdf_microfacet_ggx_refraction_prepare); - register_closure(ss, - "microfacet_multi_ggx", - id++, - closure_bsdf_microfacet_multi_ggx_params(), - closure_bsdf_microfacet_multi_ggx_prepare); - register_closure(ss, - "microfacet_multi_ggx_glass", - id++, - closure_bsdf_microfacet_multi_ggx_glass_params(), - closure_bsdf_microfacet_multi_ggx_glass_prepare); - register_closure(ss, - "microfacet_multi_ggx_aniso", - id++, - closure_bsdf_microfacet_multi_ggx_aniso_params(), - closure_bsdf_microfacet_multi_ggx_aniso_prepare); - register_closure(ss, - "microfacet_ggx_fresnel", - id++, - closure_bsdf_microfacet_ggx_fresnel_params(), - closure_bsdf_microfacet_ggx_fresnel_prepare); - register_closure(ss, - "microfacet_ggx_aniso_fresnel", - id++, - closure_bsdf_microfacet_ggx_aniso_fresnel_params(), - closure_bsdf_microfacet_ggx_aniso_fresnel_prepare); - register_closure(ss, - "microfacet_multi_ggx_fresnel", - id++, - closure_bsdf_microfacet_multi_ggx_fresnel_params(), - closure_bsdf_microfacet_multi_ggx_fresnel_prepare); - register_closure(ss, - "microfacet_multi_ggx_glass_fresnel", - id++, - closure_bsdf_microfacet_multi_ggx_glass_fresnel_params(), - closure_bsdf_microfacet_multi_ggx_glass_fresnel_prepare); - register_closure(ss, - "microfacet_multi_ggx_aniso_fresnel", - id++, - closure_bsdf_microfacet_multi_ggx_aniso_fresnel_params(), - closure_bsdf_microfacet_multi_ggx_aniso_fresnel_prepare); - register_closure(ss, - "microfacet_beckmann", - id++, - bsdf_microfacet_beckmann_isotropic_params(), - bsdf_microfacet_beckmann_isotropic_prepare); - register_closure(ss, - "microfacet_beckmann_aniso", - id++, - bsdf_microfacet_beckmann_params(), - bsdf_microfacet_beckmann_prepare); - register_closure(ss, - "microfacet_beckmann_refraction", - id++, - bsdf_microfacet_beckmann_refraction_params(), - bsdf_microfacet_beckmann_refraction_prepare); - register_closure(ss, - "ashikhmin_shirley", - id++, - bsdf_ashikhmin_shirley_params(), - bsdf_ashikhmin_shirley_prepare); - register_closure( - ss, "ashikhmin_velvet", id++, bsdf_ashikhmin_velvet_params(), bsdf_ashikhmin_velvet_prepare); - register_closure( - ss, "diffuse_toon", id++, bsdf_diffuse_toon_params(), bsdf_diffuse_toon_prepare); - register_closure(ss, "glossy_toon", id++, bsdf_glossy_toon_params(), bsdf_glossy_toon_prepare); - register_closure(ss, - "principled_diffuse", - id++, - bsdf_principled_diffuse_params(), - bsdf_principled_diffuse_prepare); - register_closure(ss, - "principled_sheen", - id++, - bsdf_principled_sheen_params(), - closure_bsdf_principled_sheen_prepare); - register_closure(ss, - "principled_clearcoat", - id++, - closure_bsdf_principled_clearcoat_params(), - closure_bsdf_principled_clearcoat_prepare); - - register_closure(ss, "emission", id++, closure_emission_params(), closure_emission_prepare); - register_closure( - ss, "background", id++, closure_background_params(), closure_background_prepare); - register_closure(ss, "holdout", id++, closure_holdout_params(), closure_holdout_prepare); - register_closure(ss, - "diffuse_ramp", - id++, - closure_bsdf_diffuse_ramp_params(), - closure_bsdf_diffuse_ramp_prepare); - register_closure( - ss, "phong_ramp", id++, closure_bsdf_phong_ramp_params(), closure_bsdf_phong_ramp_prepare); - register_closure(ss, "bssrdf", id++, closure_bssrdf_params(), closure_bssrdf_prepare); - - register_closure( - ss, "hair_reflection", id++, bsdf_hair_reflection_params(), bsdf_hair_reflection_prepare); - register_closure(ss, - "hair_transmission", - id++, - bsdf_hair_transmission_params(), - bsdf_hair_transmission_prepare); - - register_closure(ss, - "principled_hair", - id++, - closure_bsdf_principled_hair_params(), - closure_bsdf_principled_hair_prepare); - - register_closure(ss, - "henyey_greenstein", - id++, - closure_henyey_greenstein_params(), - closure_henyey_greenstein_prepare); - register_closure( - ss, "absorption", id++, closure_absorption_params(), closure_absorption_prepare); +#include "closures_template.h" } -/* BSDF Closure */ +/* Globals */ -bool CBSDFClosure::skip(const ShaderData *sd, uint32_t path_flag, int scattering) +static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, + ShaderData *sd, + const void *state, + uint32_t path_flag, + OSLThreadData *tdata) { - /* caustic options */ - if ((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) { - const KernelGlobalsCPU *kg = sd->osl_globals; - - if ((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) || - (!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT))) { - return true; - } + OSL::ShaderGlobals *globals = &tdata->globals; + + const differential3 dP = differential_from_compact(sd->Ng, sd->dP); + const differential3 dI = differential_from_compact(sd->I, sd->dI); + + /* copy from shader data to shader globals */ + globals->P = TO_VEC3(sd->P); + globals->dPdx = TO_VEC3(dP.dx); + globals->dPdy = TO_VEC3(dP.dy); + globals->I = TO_VEC3(sd->I); + globals->dIdx = TO_VEC3(dI.dx); + globals->dIdy = TO_VEC3(dI.dy); + globals->N = TO_VEC3(sd->N); + globals->Ng = TO_VEC3(sd->Ng); + globals->u = sd->u; + globals->dudx = sd->du.dx; + globals->dudy = sd->du.dy; + globals->v = sd->v; + globals->dvdx = sd->dv.dx; + globals->dvdy = sd->dv.dy; + globals->dPdu = TO_VEC3(sd->dPdu); + globals->dPdv = TO_VEC3(sd->dPdv); + globals->surfacearea = 1.0f; + globals->time = sd->time; + + /* booleans */ + globals->raytype = path_flag; + globals->flipHandedness = 0; + globals->backfacing = (sd->flag & SD_BACKFACING); + + /* shader data to be used in services callbacks */ + globals->renderstate = sd; + + /* hacky, we leave it to services to fetch actual object matrix */ + globals->shader2common = sd; + globals->object2common = sd; + + /* must be set to NULL before execute */ + globals->Ci = NULL; + + /* clear trace data */ + tdata->tracedata.init = false; + + /* Used by render-services. */ + sd->osl_globals = kg; + if (path_flag & PATH_RAY_SHADOW) { + sd->osl_path_state = nullptr; + sd->osl_shadow_path_state = (const IntegratorShadowStateCPU *)state; } - - return false; -} - -/* Standard Microfacet Closure */ - -class MicrofacetClosure : public CBSDFClosure { - public: - MicrofacetBsdf params; - ustring distribution; - int refract; - - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - static ustring u_ggx("ggx"); - static ustring u_default("default"); - - const int label = (refract) ? LABEL_TRANSMIT : LABEL_REFLECT; - if (skip(sd, path_flag, LABEL_GLOSSY | label)) { - return; - } - - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); - - if (!bsdf) { - return; - } - - /* GGX */ - if (distribution == u_ggx || distribution == u_default) { - if (!refract) { - if (params.alpha_x == params.alpha_y) { - /* Isotropic */ - sd->flag |= bsdf_microfacet_ggx_isotropic_setup(bsdf); - } - else { - /* Anisotropic */ - sd->flag |= bsdf_microfacet_ggx_setup(bsdf); - } - } - else { - sd->flag |= bsdf_microfacet_ggx_refraction_setup(bsdf); - } - } - /* Beckmann */ - else { - if (!refract) { - if (params.alpha_x == params.alpha_y) { - /* Isotropic */ - sd->flag |= bsdf_microfacet_beckmann_isotropic_setup(bsdf); - } - else { - /* Anisotropic */ - sd->flag |= bsdf_microfacet_beckmann_setup(bsdf); - } - } - else { - sd->flag |= bsdf_microfacet_beckmann_refraction_setup(bsdf); - } - } + else { + sd->osl_path_state = (const IntegratorStateCPU *)state; + sd->osl_shadow_path_state = nullptr; } -}; - -ClosureParam *closure_bsdf_microfacet_params() -{ - static ClosureParam params[] = {CLOSURE_STRING_PARAM(MicrofacetClosure, distribution), - CLOSURE_FLOAT3_PARAM(MicrofacetClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.alpha_y), - CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.ior), - CLOSURE_INT_PARAM(MicrofacetClosure, refract), - CLOSURE_STRING_KEYPARAM(MicrofacetClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetClosure)}; - - return params; } -CCLOSURE_PREPARE(closure_bsdf_microfacet_prepare, MicrofacetClosure) - -/* GGX closures with Fresnel */ - -class MicrofacetFresnelClosure : public CBSDFClosure { - public: - MicrofacetBsdf params; - float3 color; - float3 cspec0; - - MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) - { - /* Technically, the MultiGGX Glass closure may also transmit. However, - * since this is set statically and only used for caustic flags, this - * is probably as good as it gets. */ - if (skip(sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { - return NULL; - } - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); - if (!bsdf) { - return NULL; +static void flatten_closure_tree(const KernelGlobalsCPU *kg, + ShaderData *sd, + uint32_t path_flag, + const OSL::ClosureColor *closure, + float3 weight = make_float3(1.0f, 1.0f, 1.0f)) +{ + /* OSL gives us a closure tree, we flatten it into arrays per + * closure type, for evaluation, sampling, etc later on. */ + + switch (closure->id) { + case OSL::ClosureColor::MUL: { + OSL::ClosureMul *mul = (OSL::ClosureMul *)closure; + flatten_closure_tree(kg, sd, path_flag, mul->closure, TO_FLOAT3(mul->weight) * weight); + break; } - - MicrofacetExtra *extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); - if (!extra) { - return NULL; + case OSL::ClosureColor::ADD: { + OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure; + flatten_closure_tree(kg, sd, path_flag, add->closureA, weight); + flatten_closure_tree(kg, sd, path_flag, add->closureB, weight); + break; } - - bsdf->extra = extra; - bsdf->extra->color = rgb_to_spectrum(color); - bsdf->extra->cspec0 = rgb_to_spectrum(cspec0); - bsdf->extra->clearcoat = 0.0f; - return bsdf; +#define OSL_CLOSURE_STRUCT_BEGIN(Upper, lower) \ + case OSL_CLOSURE_##Upper##_ID: { \ + const OSL::ClosureComponent *comp = reinterpret_cast<const OSL::ClosureComponent *>(closure); \ + weight *= TO_FLOAT3(comp->w); \ + osl_closure_##lower##_setup( \ + kg, sd, path_flag, weight, reinterpret_cast<const Upper##Closure *>(comp + 1)); \ + break; \ } -}; - -class MicrofacetGGXFresnelClosure : public MicrofacetFresnelClosure { - public: - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - - bsdf->T = zero_float3(); - bsdf->alpha_y = bsdf->alpha_x; - sd->flag |= bsdf_microfacet_ggx_fresnel_setup(bsdf, sd); +#include "closures_template.h" + default: + break; } -}; - -ClosureParam *closure_bsdf_microfacet_ggx_fresnel_params() -{ - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.ior), - CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, color), - CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, cspec0), - CLOSURE_STRING_KEYPARAM(MicrofacetGGXFresnelClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetGGXFresnelClosure)}; - return params; } -CCLOSURE_PREPARE(closure_bsdf_microfacet_ggx_fresnel_prepare, MicrofacetGGXFresnelClosure); -class MicrofacetGGXAnisoFresnelClosure : public MicrofacetFresnelClosure { - public: - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); +/* Surface */ - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - - sd->flag |= bsdf_microfacet_ggx_fresnel_setup(bsdf, sd); - } -}; - -ClosureParam *closure_bsdf_microfacet_ggx_aniso_fresnel_params() +void OSLShader::eval_surface(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag) { - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.alpha_y), - CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.ior), - CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, color), - CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, cspec0), - CLOSURE_STRING_KEYPARAM(MicrofacetGGXFresnelClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetGGXFresnelClosure)}; - return params; -} -CCLOSURE_PREPARE(closure_bsdf_microfacet_ggx_aniso_fresnel_prepare, - MicrofacetGGXAnisoFresnelClosure); - -/* Multiscattering GGX closures */ - -class MicrofacetMultiClosure : public CBSDFClosure { - public: - MicrofacetBsdf params; - float3 color; - - MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) - { - /* Technically, the MultiGGX closure may also transmit. However, - * since this is set statically and only used for caustic flags, this - * is probably as good as it gets. */ - if (skip(sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { - return NULL; + /* setup shader globals from shader data */ + OSLThreadData *tdata = kg->osl_tdata; + shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata); + + /* execute shader for this point */ + OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; + OSL::ShaderGlobals *globals = &tdata->globals; + OSL::ShadingContext *octx = tdata->context; + int shader = sd->shader & SHADER_MASK; + + /* automatic bump shader */ + if (kg->osl->bump_state[shader]) { + /* save state */ + const float3 P = sd->P; + const float dP = sd->dP; + const OSL::Vec3 dPdx = globals->dPdx; + const OSL::Vec3 dPdy = globals->dPdy; + + /* set state as if undisplaced */ + if (sd->flag & SD_HAS_DISPLACEMENT) { + float data[9]; + bool found = kg->osl->services->get_attribute(sd, + true, + OSLRenderServices::u_empty, + TypeDesc::TypeVector, + OSLRenderServices::u_geom_undisplaced, + data); + (void)found; + assert(found); + + differential3 tmp_dP; + memcpy(&sd->P, data, sizeof(float) * 3); + memcpy(&tmp_dP.dx, data + 3, sizeof(float) * 3); + memcpy(&tmp_dP.dy, data + 6, sizeof(float) * 3); + + object_position_transform(kg, sd, &sd->P); + object_dir_transform(kg, sd, &tmp_dP.dx); + object_dir_transform(kg, sd, &tmp_dP.dy); + + sd->dP = differential_make_compact(tmp_dP); + + globals->P = TO_VEC3(sd->P); + globals->dPdx = TO_VEC3(tmp_dP.dx); + globals->dPdy = TO_VEC3(tmp_dP.dy); } - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); - if (!bsdf) { - return NULL; - } + /* execute bump shader */ + ss->execute(octx, *(kg->osl->bump_state[shader]), *globals); - MicrofacetExtra *extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); - if (!extra) { - return NULL; - } + /* reset state */ + sd->P = P; + sd->dP = dP; - bsdf->extra = extra; - bsdf->extra->color = rgb_to_spectrum(color); - bsdf->extra->cspec0 = zero_spectrum(); - bsdf->extra->clearcoat = 0.0f; - return bsdf; + globals->P = TO_VEC3(P); + globals->dPdx = TO_VEC3(dPdx); + globals->dPdy = TO_VEC3(dPdy); } -}; -class MicrofacetMultiGGXClosure : public MicrofacetMultiClosure { - public: - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - - bsdf->ior = 0.0f; - bsdf->T = zero_float3(); - bsdf->alpha_y = bsdf->alpha_x; - sd->flag |= bsdf_microfacet_multi_ggx_setup(bsdf); + /* surface shader */ + if (kg->osl->surface_state[shader]) { + ss->execute(octx, *(kg->osl->surface_state[shader]), *globals); } -}; - -ClosureParam *closure_bsdf_microfacet_multi_ggx_params() -{ - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_x), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, color), - CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetMultiGGXClosure)}; - return params; -} -CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_prepare, MicrofacetMultiGGXClosure); -class MicrofacetMultiGGXAnisoClosure : public MicrofacetMultiClosure { - public: - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - - bsdf->ior = 0.0f; - sd->flag |= bsdf_microfacet_multi_ggx_setup(bsdf); + /* flatten closure tree */ + if (globals->Ci) { + flatten_closure_tree(kg, sd, path_flag, globals->Ci); } -}; - -ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_params() -{ - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_y), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, color), - CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetMultiGGXClosure)}; - return params; } -CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_aniso_prepare, MicrofacetMultiGGXAnisoClosure); - -class MicrofacetMultiGGXGlassClosure : public MicrofacetMultiClosure { - public: - MicrofacetMultiGGXGlassClosure() : MicrofacetMultiClosure() - { - } - - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - bsdf->T = zero_float3(); - bsdf->alpha_y = bsdf->alpha_x; - sd->flag |= bsdf_microfacet_multi_ggx_glass_setup(bsdf); - } -}; +/* Background */ -ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_params() +void OSLShader::eval_background(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag) { - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.ior), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, color), - CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetMultiGGXClosure)}; - return params; -} -CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_glass_prepare, MicrofacetMultiGGXGlassClosure); - -/* Multiscattering GGX closures with Fresnel */ - -class MicrofacetMultiFresnelClosure : public CBSDFClosure { - public: - MicrofacetBsdf params; - float3 color; - float3 cspec0; - - MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) - { - /* Technically, the MultiGGX closure may also transmit. However, - * since this is set statically and only used for caustic flags, this - * is probably as good as it gets. */ - if (skip(sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { - return NULL; - } - - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); - if (!bsdf) { - return NULL; - } + /* setup shader globals from shader data */ + OSLThreadData *tdata = kg->osl_tdata; + shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata); - MicrofacetExtra *extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); - if (!extra) { - return NULL; - } + /* execute shader for this point */ + OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; + OSL::ShaderGlobals *globals = &tdata->globals; + OSL::ShadingContext *octx = tdata->context; - bsdf->extra = extra; - bsdf->extra->color = rgb_to_spectrum(color); - bsdf->extra->cspec0 = rgb_to_spectrum(cspec0); - bsdf->extra->clearcoat = 0.0f; - return bsdf; + if (kg->osl->background_state) { + ss->execute(octx, *(kg->osl->background_state), *globals); } -}; - -class MicrofacetMultiGGXFresnelClosure : public MicrofacetMultiFresnelClosure { - public: - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - bsdf->T = zero_float3(); - bsdf->alpha_y = bsdf->alpha_x; - sd->flag |= bsdf_microfacet_multi_ggx_fresnel_setup(bsdf, sd); + /* return background color immediately */ + if (globals->Ci) { + flatten_closure_tree(kg, sd, path_flag, globals->Ci); } -}; - -ClosureParam *closure_bsdf_microfacet_multi_ggx_fresnel_params() -{ - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.ior), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, color), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, cspec0), - CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXFresnelClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetMultiGGXFresnelClosure)}; - return params; } -CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_fresnel_prepare, - MicrofacetMultiGGXFresnelClosure); - -class MicrofacetMultiGGXAnisoFresnelClosure : public MicrofacetMultiFresnelClosure { - public: - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - sd->flag |= bsdf_microfacet_multi_ggx_fresnel_setup(bsdf, sd); - } -}; +/* Volume */ -ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_fresnel_params() +void OSLShader::eval_volume(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag) { - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_y), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.ior), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, color), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, cspec0), - CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXFresnelClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetMultiGGXFresnelClosure)}; - return params; -} -CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_aniso_fresnel_prepare, - MicrofacetMultiGGXAnisoFresnelClosure); - -class MicrofacetMultiGGXGlassFresnelClosure : public MicrofacetMultiFresnelClosure { - public: - MicrofacetMultiGGXGlassFresnelClosure() : MicrofacetMultiFresnelClosure() - { + /* setup shader globals from shader data */ + OSLThreadData *tdata = kg->osl_tdata; + shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata); + + /* execute shader */ + OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; + OSL::ShaderGlobals *globals = &tdata->globals; + OSL::ShadingContext *octx = tdata->context; + int shader = sd->shader & SHADER_MASK; + + if (kg->osl->volume_state[shader]) { + ss->execute(octx, *(kg->osl->volume_state[shader]), *globals); } - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); - - MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); - if (!bsdf) { - return; - } - - bsdf->T = zero_float3(); - bsdf->alpha_y = bsdf->alpha_x; - sd->flag |= bsdf_microfacet_multi_ggx_glass_fresnel_setup(bsdf, sd); + /* flatten closure tree */ + if (globals->Ci) { + flatten_closure_tree(kg, sd, path_flag, globals->Ci); } -}; - -ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_fresnel_params() -{ - static ClosureParam params[] = { - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.ior), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, color), - CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, cspec0), - CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXFresnelClosure, label, "label"), - CLOSURE_FINISH_PARAM(MicrofacetMultiGGXFresnelClosure)}; - return params; } -CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_glass_fresnel_prepare, - MicrofacetMultiGGXGlassFresnelClosure); -/* Transparent */ +/* Displacement */ -class TransparentClosure : public CBSDFClosure { - public: - ShaderClosure params; - float3 unused; - - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - bsdf_transparent_setup(sd, rgb_to_spectrum(weight), path_flag); - } -}; - -ClosureParam *closure_bsdf_transparent_params() +void OSLShader::eval_displacement(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd) { - static ClosureParam params[] = {CLOSURE_STRING_KEYPARAM(TransparentClosure, label, "label"), - CLOSURE_FINISH_PARAM(TransparentClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_bsdf_transparent_prepare, TransparentClosure) - -/* Volume */ - -class VolumeAbsorptionClosure : public CBSDFClosure { - public: - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - volume_extinction_setup(sd, rgb_to_spectrum(weight)); - } -}; - -ClosureParam *closure_absorption_params() -{ - static ClosureParam params[] = {CLOSURE_STRING_KEYPARAM(VolumeAbsorptionClosure, label, "label"), - CLOSURE_FINISH_PARAM(VolumeAbsorptionClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_absorption_prepare, VolumeAbsorptionClosure) - -class VolumeHenyeyGreensteinClosure : public CBSDFClosure { - public: - HenyeyGreensteinVolume params; - - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) - { - volume_extinction_setup(sd, rgb_to_spectrum(weight)); - - HenyeyGreensteinVolume *volume = (HenyeyGreensteinVolume *)bsdf_alloc_osl( - sd, sizeof(HenyeyGreensteinVolume), rgb_to_spectrum(weight), ¶ms); - if (!volume) { - return; - } - - sd->flag |= volume_henyey_greenstein_setup(volume); + /* setup shader globals from shader data */ + OSLThreadData *tdata = kg->osl_tdata; + shaderdata_to_shaderglobals(kg, sd, state, 0, tdata); + + /* execute shader */ + OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; + OSL::ShaderGlobals *globals = &tdata->globals; + OSL::ShadingContext *octx = tdata->context; + int shader = sd->shader & SHADER_MASK; + + if (kg->osl->displacement_state[shader]) { + ss->execute(octx, *(kg->osl->displacement_state[shader]), *globals); } -}; -ClosureParam *closure_henyey_greenstein_params() -{ - static ClosureParam params[] = { - CLOSURE_FLOAT_PARAM(VolumeHenyeyGreensteinClosure, params.g), - CLOSURE_STRING_KEYPARAM(VolumeHenyeyGreensteinClosure, label, "label"), - CLOSURE_FINISH_PARAM(VolumeHenyeyGreensteinClosure)}; - return params; + /* get back position */ + sd->P = TO_FLOAT3(globals->P); } -CCLOSURE_PREPARE(closure_henyey_greenstein_prepare, VolumeHenyeyGreensteinClosure) - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/closures.h b/intern/cycles/kernel/osl/closures.h deleted file mode 100644 index 97666be7a1e..00000000000 --- a/intern/cycles/kernel/osl/closures.h +++ /dev/null @@ -1,142 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Adapted from Open Shading Language - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2011-2022 Blender Foundation. */ - -#ifndef __OSL_CLOSURES_H__ -#define __OSL_CLOSURES_H__ - -#include "kernel/types.h" -#include "util/types.h" - -#include <OSL/genclosure.h> -#include <OSL/oslclosure.h> -#include <OSL/oslexec.h> - -CCL_NAMESPACE_BEGIN - -OSL::ClosureParam *closure_emission_params(); -OSL::ClosureParam *closure_background_params(); -OSL::ClosureParam *closure_holdout_params(); -OSL::ClosureParam *closure_bsdf_diffuse_ramp_params(); -OSL::ClosureParam *closure_bsdf_phong_ramp_params(); -OSL::ClosureParam *closure_bsdf_transparent_params(); -OSL::ClosureParam *closure_bssrdf_params(); -OSL::ClosureParam *closure_absorption_params(); -OSL::ClosureParam *closure_henyey_greenstein_params(); -OSL::ClosureParam *closure_bsdf_microfacet_params(); -OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_params(); -OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_params(); -OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_params(); -OSL::ClosureParam *closure_bsdf_microfacet_ggx_fresnel_params(); -OSL::ClosureParam *closure_bsdf_microfacet_ggx_aniso_fresnel_params(); -OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_fresnel_params(); -OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_fresnel_params(); -OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_fresnel_params(); -OSL::ClosureParam *closure_bsdf_principled_clearcoat_params(); - -void closure_emission_prepare(OSL::RendererServices *, int id, void *data); -void closure_background_prepare(OSL::RendererServices *, int id, void *data); -void closure_holdout_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_diffuse_ramp_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_phong_ramp_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_transparent_prepare(OSL::RendererServices *, int id, void *data); -void closure_bssrdf_prepare(OSL::RendererServices *, int id, void *data); -void closure_absorption_prepare(OSL::RendererServices *, int id, void *data); -void closure_henyey_greenstein_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_microfacet_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_microfacet_multi_ggx_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_microfacet_multi_ggx_glass_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_microfacet_multi_ggx_aniso_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_microfacet_ggx_fresnel_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_microfacet_ggx_aniso_fresnel_prepare(OSL::RendererServices *, - int id, - void *data); -void closure_bsdf_microfacet_multi_ggx_fresnel_prepare(OSL::RendererServices *, - int id, - void *data); -void closure_bsdf_microfacet_multi_ggx_glass_fresnel_prepare(OSL::RendererServices *, - int id, - void *data); -void closure_bsdf_microfacet_multi_ggx_aniso_fresnel_prepare(OSL::RendererServices *, - int id, - void *data); -void closure_bsdf_principled_clearcoat_prepare(OSL::RendererServices *, int id, void *data); -void closure_bsdf_principled_hair_prepare(OSL::RendererServices *, int id, void *data); - -#define CCLOSURE_PREPARE(name, classname) \ - void name(RendererServices *, int id, void *data) \ - { \ - memset(data, 0, sizeof(classname)); \ - new (data) classname(); \ - } - -#define CCLOSURE_PREPARE_STATIC(name, classname) static CCLOSURE_PREPARE(name, classname) - -#define CLOSURE_FLOAT3_PARAM(st, fld) \ - { \ - TypeDesc::TypeVector, (int)reckless_offsetof(st, fld), NULL, sizeof(OSL::Vec3) \ - } - -#define BSDF_CLOSURE_FLOAT_PARAM(st, fld) CLOSURE_FLOAT_PARAM(st, fld), -#define BSDF_CLOSURE_FLOAT3_PARAM(st, fld) CLOSURE_FLOAT3_PARAM(st, fld), - -#define TO_VEC3(v) OSL::Vec3(v.x, v.y, v.z) -#define TO_COLOR3(v) OSL::Color3(v.x, v.y, v.z) -#define TO_FLOAT3(v) make_float3(v[0], v[1], v[2]) - -/* Closure */ - -class CClosurePrimitive { - public: - virtual void setup(ShaderData *sd, uint32_t path_flag, float3 weight) = 0; - - OSL::ustring label; -}; - -/* BSDF */ - -class CBSDFClosure : public CClosurePrimitive { - public: - bool skip(const ShaderData *sd, uint32_t path_flag, int scattering); -}; - -#define BSDF_CLOSURE_CLASS_BEGIN(Upper, lower, structname, TYPE) \ -\ - class Upper##Closure : public CBSDFClosure { \ - public: \ - structname params; \ - float3 unused; \ -\ - void setup(ShaderData *sd, uint32_t path_flag, float3 weight) \ - { \ - if (!skip(sd, path_flag, TYPE)) { \ - params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); \ - structname *bsdf = (structname *)bsdf_alloc_osl( \ - sd, sizeof(structname), rgb_to_spectrum(weight), ¶ms); \ - sd->flag |= (bsdf) ? bsdf_##lower##_setup(bsdf) : 0; \ - } \ - } \ - }; \ -\ - static ClosureParam *bsdf_##lower##_params() \ - { \ - static ClosureParam params[] = { - -/* parameters */ - -#define BSDF_CLOSURE_CLASS_END(Upper, lower) \ - CLOSURE_STRING_KEYPARAM(Upper##Closure, label, "label"), CLOSURE_FINISH_PARAM(Upper##Closure) \ - } \ - ; \ - return params; \ - } \ -\ - CCLOSURE_PREPARE_STATIC(bsdf_##lower##_prepare, Upper##Closure) - -CCL_NAMESPACE_END - -#endif /* __OSL_CLOSURES_H__ */ diff --git a/intern/cycles/kernel/osl/closures_setup.h b/intern/cycles/kernel/osl/closures_setup.h new file mode 100644 index 00000000000..f8d68444f90 --- /dev/null +++ b/intern/cycles/kernel/osl/closures_setup.h @@ -0,0 +1,1166 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Adapted from Open Shading Language + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011-2022 Blender Foundation. */ + +#pragma once + +// clang-format off +#include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf_util.h" +#include "kernel/closure/bsdf_ashikhmin_velvet.h" +#include "kernel/closure/bsdf_diffuse.h" +#include "kernel/closure/bsdf_microfacet.h" +#include "kernel/closure/bsdf_microfacet_multi.h" +#include "kernel/closure/bsdf_oren_nayar.h" +#include "kernel/closure/bsdf_reflection.h" +#include "kernel/closure/bsdf_refraction.h" +#include "kernel/closure/bsdf_transparent.h" +#include "kernel/closure/bsdf_ashikhmin_shirley.h" +#include "kernel/closure/bsdf_toon.h" +#include "kernel/closure/bsdf_hair.h" +#include "kernel/closure/bsdf_hair_principled.h" +#include "kernel/closure/bsdf_principled_diffuse.h" +#include "kernel/closure/bsdf_principled_sheen.h" +#include "kernel/closure/volume.h" +#include "kernel/closure/bsdf_diffuse_ramp.h" +#include "kernel/closure/bsdf_phong_ramp.h" +#include "kernel/closure/bssrdf.h" +#include "kernel/closure/emissive.h" +// clang-format on + +CCL_NAMESPACE_BEGIN + +#define OSL_CLOSURE_STRUCT_BEGIN(Upper, lower) \ + struct ccl_align(8) Upper##Closure \ + { \ + const char *label; +#define OSL_CLOSURE_STRUCT_END(Upper, lower) \ + } \ + ; \ + ccl_device void osl_closure_##lower##_setup(KernelGlobals kg, \ + ccl_private ShaderData *sd, \ + uint32_t path_flag, \ + float3 weight, \ + ccl_private Upper##Closure *closure); +#define OSL_CLOSURE_STRUCT_MEMBER(Upper, TYPE, type, name, key) type name; +#define OSL_CLOSURE_STRUCT_ARRAY_MEMBER(Upper, TYPE, type, name, key, size) type name[size]; + +#include "closures_template.h" + +ccl_device_forceinline bool osl_closure_skip(KernelGlobals kg, + ccl_private const ShaderData *sd, + uint32_t path_flag, + int scattering) +{ + /* caustic options */ + if ((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) { + if ((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) || + (!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT))) { + return true; + } + } + + return false; +} + +/* Diffuse */ + +ccl_device void osl_closure_diffuse_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const DiffuseClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_DIFFUSE)) { + return; + } + + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + + sd->flag |= bsdf_diffuse_setup(bsdf); +} + +ccl_device void osl_closure_oren_nayar_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const OrenNayarClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_DIFFUSE)) { + return; + } + + ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc( + sd, sizeof(OrenNayarBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->roughness = closure->roughness; + + sd->flag |= bsdf_oren_nayar_setup(bsdf); +} + +ccl_device void osl_closure_translucent_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const TranslucentClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_DIFFUSE)) { + return; + } + + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + + sd->flag |= bsdf_translucent_setup(bsdf); +} + +ccl_device void osl_closure_reflection_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const ReflectionClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_SINGULAR)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + + sd->flag |= bsdf_reflection_setup(bsdf); +} + +ccl_device void osl_closure_refraction_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const RefractionClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_SINGULAR)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->ior = closure->ior; + + sd->flag |= bsdf_refraction_setup(bsdf); +} + +ccl_device void osl_closure_transparent_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const TransparentClosure *closure) +{ + bsdf_transparent_setup(sd, rgb_to_spectrum(weight), path_flag); +} + +/* Standard microfacet closures */ + +ccl_device void osl_closure_microfacet_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetClosure *closure) +{ + const int label = (closure->refract) ? LABEL_TRANSMIT : LABEL_REFLECT; + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | label)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = closure->alpha_y; + bsdf->ior = closure->ior; + bsdf->T = closure->T; + + static OSL::ustring u_ggx("ggx"); + static OSL::ustring u_default("default"); + + /* GGX */ + if (closure->distribution == u_ggx || closure->distribution == u_default) { + if (!closure->refract) { + if (closure->alpha_x == closure->alpha_y) { + /* Isotropic */ + sd->flag |= bsdf_microfacet_ggx_isotropic_setup(bsdf); + } + else { + /* Anisotropic */ + sd->flag |= bsdf_microfacet_ggx_setup(bsdf); + } + } + else { + sd->flag |= bsdf_microfacet_ggx_refraction_setup(bsdf); + } + } + /* Beckmann */ + else { + if (!closure->refract) { + if (closure->alpha_x == closure->alpha_y) { + /* Isotropic */ + sd->flag |= bsdf_microfacet_beckmann_isotropic_setup(bsdf); + } + else { + /* Anisotropic */ + sd->flag |= bsdf_microfacet_beckmann_setup(bsdf); + } + } + else { + sd->flag |= bsdf_microfacet_beckmann_refraction_setup(bsdf); + } + } +} + +ccl_device void osl_closure_microfacet_ggx_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetGGXIsotropicClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + + sd->flag |= bsdf_microfacet_ggx_isotropic_setup(bsdf); +} + +ccl_device void osl_closure_microfacet_ggx_aniso_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetGGXClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = closure->alpha_y; + bsdf->T = closure->T; + + sd->flag |= bsdf_microfacet_ggx_setup(bsdf); +} + +ccl_device void osl_closure_microfacet_ggx_refraction_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetGGXRefractionClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_TRANSMIT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->ior = closure->ior; + + sd->flag |= bsdf_microfacet_ggx_refraction_setup(bsdf); +} + +/* GGX closures with Fresnel */ + +ccl_device void osl_closure_microfacet_ggx_fresnel_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetGGXFresnelClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = bsdf->alpha_x; + bsdf->ior = closure->ior; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = rgb_to_spectrum(closure->cspec0); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = zero_float3(); + + sd->flag |= bsdf_microfacet_ggx_fresnel_setup(bsdf, sd); +} + +ccl_device void osl_closure_microfacet_ggx_aniso_fresnel_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetGGXAnisoFresnelClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = closure->alpha_y; + bsdf->ior = closure->ior; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = rgb_to_spectrum(closure->cspec0); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = closure->T; + + sd->flag |= bsdf_microfacet_ggx_fresnel_setup(bsdf, sd); +} + +/* Multi-scattering GGX closures */ + +ccl_device void osl_closure_microfacet_multi_ggx_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetMultiGGXClosure *closure) +{ + /* Technically, the MultiGGX closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = bsdf->alpha_x; + bsdf->ior = 0.0f; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = zero_spectrum(); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = zero_float3(); + + sd->flag |= bsdf_microfacet_multi_ggx_setup(bsdf); +} + +ccl_device void osl_closure_microfacet_multi_ggx_glass_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetMultiGGXGlassClosure *closure) +{ + /* Technically, the MultiGGX closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = bsdf->alpha_x; + bsdf->ior = closure->ior; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = zero_spectrum(); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = zero_float3(); + + sd->flag |= bsdf_microfacet_multi_ggx_glass_setup(bsdf); +} + +ccl_device void osl_closure_microfacet_multi_ggx_aniso_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetMultiGGXAnisoClosure *closure) +{ + /* Technically, the MultiGGX closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = closure->alpha_y; + bsdf->ior = 0.0f; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = zero_spectrum(); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = closure->T; + + sd->flag |= bsdf_microfacet_multi_ggx_setup(bsdf); +} + +/* Multi-scattering GGX closures with Fresnel */ + +ccl_device void osl_closure_microfacet_multi_ggx_fresnel_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetMultiGGXFresnelClosure *closure) +{ + /* Technically, the MultiGGX closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = bsdf->alpha_x; + bsdf->ior = closure->ior; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = rgb_to_spectrum(closure->cspec0); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = zero_float3(); + + sd->flag |= bsdf_microfacet_multi_ggx_fresnel_setup(bsdf, sd); +} + +ccl_device void osl_closure_microfacet_multi_ggx_glass_fresnel_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetMultiGGXGlassFresnelClosure *closure) +{ + /* Technically, the MultiGGX closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = bsdf->alpha_x; + bsdf->ior = closure->ior; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = rgb_to_spectrum(closure->cspec0); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = zero_float3(); + + sd->flag |= bsdf_microfacet_multi_ggx_glass_fresnel_setup(bsdf, sd); +} + +ccl_device void osl_closure_microfacet_multi_ggx_aniso_fresnel_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetMultiGGXAnisoFresnelClosure *closure) +{ + /* Technically, the MultiGGX closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = closure->alpha_y; + bsdf->ior = closure->ior; + + bsdf->extra = extra; + bsdf->extra->color = rgb_to_spectrum(closure->color); + bsdf->extra->cspec0 = rgb_to_spectrum(closure->cspec0); + bsdf->extra->clearcoat = 0.0f; + + bsdf->T = closure->T; + + sd->flag |= bsdf_microfacet_multi_ggx_fresnel_setup(bsdf, sd); +} + +/* Beckmann closures */ + +ccl_device void osl_closure_microfacet_beckmann_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetBeckmannIsotropicClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + + sd->flag |= bsdf_microfacet_beckmann_isotropic_setup(bsdf); +} + +ccl_device void osl_closure_microfacet_beckmann_aniso_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetBeckmannClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = closure->alpha_y; + bsdf->T = closure->T; + + sd->flag |= bsdf_microfacet_beckmann_setup(bsdf); +} + +ccl_device void osl_closure_microfacet_beckmann_refraction_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const MicrofacetBeckmannRefractionClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_TRANSMIT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->ior = closure->ior; + + sd->flag |= bsdf_microfacet_beckmann_refraction_setup(bsdf); +} + +/* Ashikhmin closures */ + +ccl_device void osl_closure_ashikhmin_velvet_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const AshikhminVelvetClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_DIFFUSE)) { + return; + } + + ccl_private VelvetBsdf *bsdf = (ccl_private VelvetBsdf *)bsdf_alloc( + sd, sizeof(VelvetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->sigma = closure->sigma; + + sd->flag |= bsdf_ashikhmin_velvet_setup(bsdf); +} + +ccl_device void osl_closure_ashikhmin_shirley_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const AshikhminShirleyClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + return; + } + + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->alpha_x; + bsdf->alpha_y = closure->alpha_y; + bsdf->T = closure->T; + + sd->flag |= bsdf_ashikhmin_shirley_setup(bsdf); +} + +ccl_device void osl_closure_diffuse_toon_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const DiffuseToonClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_DIFFUSE)) { + return; + } + + ccl_private ToonBsdf *bsdf = (ccl_private ToonBsdf *)bsdf_alloc( + sd, sizeof(ToonBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->size = closure->size; + bsdf->smooth = closure->smooth; + + sd->flag |= bsdf_diffuse_toon_setup(bsdf); +} + +ccl_device void osl_closure_glossy_toon_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const GlossyToonClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY)) { + return; + } + + ccl_private ToonBsdf *bsdf = (ccl_private ToonBsdf *)bsdf_alloc( + sd, sizeof(ToonBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->size = closure->size; + bsdf->smooth = closure->smooth; + + sd->flag |= bsdf_glossy_toon_setup(bsdf); +} + +/* Disney principled closures */ + +ccl_device void osl_closure_principled_diffuse_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const PrincipledDiffuseClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_DIFFUSE)) { + return; + } + + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( + sd, sizeof(PrincipledDiffuseBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->roughness = closure->roughness; + + sd->flag |= bsdf_principled_diffuse_setup(bsdf); +} + +ccl_device void osl_closure_principled_sheen_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const PrincipledSheenClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_DIFFUSE)) { + return; + } + + ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)bsdf_alloc( + sd, sizeof(PrincipledSheenBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->avg_value = 0.0f; + + sd->flag |= bsdf_principled_sheen_setup(sd, bsdf); +} + +ccl_device void osl_closure_principled_clearcoat_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const PrincipledClearcoatClosure *closure) +{ + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + MicrofacetExtra *extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->alpha_x = closure->clearcoat_roughness; + bsdf->alpha_y = closure->clearcoat_roughness; + bsdf->ior = 1.5f; + + bsdf->extra = extra; + bsdf->extra->color = zero_spectrum(); + bsdf->extra->cspec0 = make_spectrum(0.04f); + bsdf->extra->clearcoat = closure->clearcoat; + + bsdf->T = zero_float3(); + + sd->flag |= bsdf_microfacet_ggx_clearcoat_setup(bsdf, sd); +} + +/* Variable cone emissive closure + * + * This primitive emits in a cone having a configurable penumbra area where the light decays to 0 + * reaching the outer_angle limit. It can also behave as a lambertian emitter if the provided + * angles are PI/2, which is the default + */ +ccl_device void osl_closure_emission_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t /* path_flag */, + float3 weight, + ccl_private const GenericEmissiveClosure *closure) +{ + emission_setup(sd, rgb_to_spectrum(weight)); +} + +/* Generic background closure + * + * We only have a background closure for the shaders to return a color in background shaders. No + * methods, only the weight is taking into account + */ +ccl_device void osl_closure_background_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t /* path_flag */, + float3 weight, + ccl_private const GenericBackgroundClosure *closure) +{ + background_setup(sd, rgb_to_spectrum(weight)); +} + +/* Holdout closure + * + * This will be used by the shader to mark the amount of holdout for the current shading point. No + * parameters, only the weight will be used + */ +ccl_device void osl_closure_holdout_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t /* path_flag */, + float3 weight, + ccl_private const HoldoutClosure *closure) +{ + closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, rgb_to_spectrum(weight)); + sd->flag |= SD_HOLDOUT; +} + +ccl_device void osl_closure_diffuse_ramp_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t /* path_flag */, + float3 weight, + ccl_private const DiffuseRampClosure *closure) +{ + ccl_private DiffuseRampBsdf *bsdf = (ccl_private DiffuseRampBsdf *)bsdf_alloc( + sd, sizeof(DiffuseRampBsdf), rgb_to_spectrum(weight)); + + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + + bsdf->colors = (float3 *)closure_alloc_extra(sd, sizeof(float3) * 8); + if (!bsdf->colors) { + return; + } + + for (int i = 0; i < 8; i++) + bsdf->colors[i] = closure->colors[i]; + + sd->flag |= bsdf_diffuse_ramp_setup(bsdf); +} + +ccl_device void osl_closure_phong_ramp_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t /* path_flag */, + float3 weight, + ccl_private const PhongRampClosure *closure) +{ + ccl_private PhongRampBsdf *bsdf = (ccl_private PhongRampBsdf *)bsdf_alloc( + sd, sizeof(PhongRampBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->exponent = closure->exponent; + + bsdf->colors = (float3 *)closure_alloc_extra(sd, sizeof(float3) * 8); + if (!bsdf->colors) { + return; + } + + for (int i = 0; i < 8; i++) + bsdf->colors[i] = closure->colors[i]; + + sd->flag |= bsdf_phong_ramp_setup(bsdf); +} + +ccl_device void osl_closure_bssrdf_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const BSSRDFClosure *closure) +{ + static ustring u_burley("burley"); + static ustring u_random_walk_fixed_radius("random_walk_fixed_radius"); + static ustring u_random_walk("random_walk"); + + ClosureType type; + if (closure->method == u_burley) { + type = CLOSURE_BSSRDF_BURLEY_ID; + } + else if (closure->method == u_random_walk_fixed_radius) { + type = CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID; + } + else if (closure->method == u_random_walk) { + type = CLOSURE_BSSRDF_RANDOM_WALK_ID; + } + else { + return; + } + + ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, rgb_to_spectrum(weight)); + if (!bssrdf) { + return; + } + + /* disable in case of diffuse ancestor, can't see it well then and + * adds considerably noise due to probabilities of continuing path + * getting lower and lower */ + if (path_flag & PATH_RAY_DIFFUSE_ANCESTOR) { + bssrdf->radius = zero_spectrum(); + } + else { + bssrdf->radius = closure->radius; + } + + /* create one closure per color channel */ + bssrdf->albedo = closure->albedo; + bssrdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bssrdf->roughness = closure->roughness; + bssrdf->anisotropy = clamp(closure->anisotropy, 0.0f, 0.9f); + + sd->flag |= bssrdf_setup(sd, bssrdf, type, clamp(closure->ior, 1.01f, 3.8f)); +} + +/* Hair */ + +ccl_device void osl_closure_hair_reflection_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const HairReflectionClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY)) { + return; + } + + ccl_private HairBsdf *bsdf = (ccl_private HairBsdf *)bsdf_alloc( + sd, sizeof(HairBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->T = closure->T; + bsdf->roughness1 = closure->roughness1; + bsdf->roughness2 = closure->roughness2; + bsdf->offset = closure->offset; + + sd->flag |= bsdf_hair_reflection_setup(bsdf); +} + +ccl_device void osl_closure_hair_transmission_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const HairTransmissionClosure *closure) +{ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY)) { + return; + } + + ccl_private HairBsdf *bsdf = (ccl_private HairBsdf *)bsdf_alloc( + sd, sizeof(HairBsdf), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->T = closure->T; + bsdf->roughness1 = closure->roughness1; + bsdf->roughness2 = closure->roughness2; + bsdf->offset = closure->offset; + + sd->flag |= bsdf_hair_transmission_setup(bsdf); +} + +ccl_device void osl_closure_principled_hair_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const PrincipledHairClosure *closure) +{ +#ifdef __HAIR__ + if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY)) { + return; + } + + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)bsdf_alloc( + sd, sizeof(PrincipledHairBSDF), rgb_to_spectrum(weight)); + if (!bsdf) { + return; + } + + ccl_private PrincipledHairExtra *extra = (ccl_private PrincipledHairExtra *)closure_alloc_extra( + sd, sizeof(PrincipledHairExtra)); + if (!extra) { + return; + } + + bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N); + bsdf->sigma = closure->sigma; + bsdf->v = closure->v; + bsdf->s = closure->s; + bsdf->alpha = closure->alpha; + bsdf->eta = closure->eta; + bsdf->m0_roughness = closure->m0_roughness; + + bsdf->extra = extra; + + sd->flag |= bsdf_principled_hair_setup(sd, bsdf); +#endif +} + +/* Volume */ + +ccl_device void osl_closure_absorption_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const VolumeAbsorptionClosure *closure) +{ + volume_extinction_setup(sd, rgb_to_spectrum(weight)); +} + +ccl_device void osl_closure_henyey_greenstein_setup( + KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + float3 weight, + ccl_private const VolumeHenyeyGreensteinClosure *closure) +{ + volume_extinction_setup(sd, rgb_to_spectrum(weight)); + + ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc( + sd, sizeof(HenyeyGreensteinVolume), rgb_to_spectrum(weight)); + if (!volume) { + return; + } + + volume->g = closure->g; + + sd->flag |= volume_henyey_greenstein_setup(volume); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/closures_template.h b/intern/cycles/kernel/osl/closures_template.h new file mode 100644 index 00000000000..c808b275966 --- /dev/null +++ b/intern/cycles/kernel/osl/closures_template.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#ifndef OSL_CLOSURE_STRUCT_BEGIN +# define OSL_CLOSURE_STRUCT_BEGIN(Upper, lower) +#endif +#ifndef OSL_CLOSURE_STRUCT_END +# define OSL_CLOSURE_STRUCT_END(Upper, lower) +#endif +#ifndef OSL_CLOSURE_STRUCT_MEMBER +# define OSL_CLOSURE_STRUCT_MEMBER(Upper, TYPE, type, name, key) +#endif +#ifndef OSL_CLOSURE_STRUCT_ARRAY_MEMBER +# define OSL_CLOSURE_STRUCT_ARRAY_MEMBER(Upper, TYPE, type, name, key, size) +#endif + +OSL_CLOSURE_STRUCT_BEGIN(Diffuse, diffuse) + OSL_CLOSURE_STRUCT_MEMBER(Diffuse, VECTOR, packed_float3, N, NULL) +OSL_CLOSURE_STRUCT_END(Diffuse, diffuse) + +OSL_CLOSURE_STRUCT_BEGIN(OrenNayar, oren_nayar) + OSL_CLOSURE_STRUCT_MEMBER(OrenNayar, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(OrenNayar, FLOAT, float, roughness, NULL) +OSL_CLOSURE_STRUCT_END(OrenNayar, oren_nayar) + +OSL_CLOSURE_STRUCT_BEGIN(Translucent, translucent) + OSL_CLOSURE_STRUCT_MEMBER(Translucent, VECTOR, packed_float3, N, NULL) +OSL_CLOSURE_STRUCT_END(Translucent, translucent) + +OSL_CLOSURE_STRUCT_BEGIN(Reflection, reflection) + OSL_CLOSURE_STRUCT_MEMBER(Reflection, VECTOR, packed_float3, N, NULL) +OSL_CLOSURE_STRUCT_END(Reflection, reflection) + +OSL_CLOSURE_STRUCT_BEGIN(Refraction, refraction) + OSL_CLOSURE_STRUCT_MEMBER(Refraction, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(Refraction, FLOAT, float, ior, NULL) +OSL_CLOSURE_STRUCT_END(Refraction, refraction) + +OSL_CLOSURE_STRUCT_BEGIN(Transparent, transparent) +OSL_CLOSURE_STRUCT_END(Transparent, transparent) + +OSL_CLOSURE_STRUCT_BEGIN(Microfacet, microfacet) + OSL_CLOSURE_STRUCT_MEMBER(Microfacet, STRING, ustring, distribution, NULL) + OSL_CLOSURE_STRUCT_MEMBER(Microfacet, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(Microfacet, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(Microfacet, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(Microfacet, FLOAT, float, alpha_y, NULL) + OSL_CLOSURE_STRUCT_MEMBER(Microfacet, FLOAT, float, ior, NULL) + OSL_CLOSURE_STRUCT_MEMBER(Microfacet, INT, int, refract, NULL) +OSL_CLOSURE_STRUCT_END(Microfacet, microfacet) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetGGXIsotropic, microfacet_ggx) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXIsotropic, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXIsotropic, FLOAT, float, alpha_x, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetGGXIsotropic, microfacet_ggx) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetGGX, microfacet_ggx_aniso) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGX, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGX, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGX, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGX, FLOAT, float, alpha_y, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetGGX, microfacet_ggx_aniso) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetGGXRefraction, microfacet_ggx_refraction) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXRefraction, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXRefraction, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXRefraction, FLOAT, float, ior, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetGGXRefraction, microfacet_ggx_refraction) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGX, microfacet_multi_ggx) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGX, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGX, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGX, VECTOR, packed_float3, color, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetMultiGGX, microfacet_multi_ggx) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGXGlass, microfacet_multi_ggx_glass) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlass, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlass, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlass, FLOAT, float, ior, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlass, VECTOR, packed_float3, color, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetMultiGGXGlass, microfacet_multi_ggx_glass) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGXAniso, microfacet_multi_ggx_aniso) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAniso, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAniso, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAniso, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAniso, FLOAT, float, alpha_y, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAniso, VECTOR, packed_float3, color, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetMultiGGXAniso, microfacet_multi_ggx_aniso) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetGGXFresnel, microfacet_ggx_fresnel) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXFresnel, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXFresnel, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXFresnel, FLOAT, float, ior, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXFresnel, VECTOR, packed_float3, color, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXFresnel, VECTOR, packed_float3, cspec0, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetGGXFresnel, microfacet_ggx_fresnel) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetGGXAnisoFresnel, microfacet_ggx_aniso_fresnel) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXAnisoFresnel, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXAnisoFresnel, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXAnisoFresnel, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXAnisoFresnel, FLOAT, float, alpha_y, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXAnisoFresnel, FLOAT, float, ior, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXAnisoFresnel, VECTOR, packed_float3, color, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetGGXAnisoFresnel, VECTOR, packed_float3, cspec0, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetGGXAnisoFresnel, microfacet_ggx_aniso_fresnel) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGXFresnel, microfacet_multi_ggx_fresnel) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXFresnel, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXFresnel, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXFresnel, FLOAT, float, ior, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXFresnel, VECTOR, packed_float3, color, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXFresnel, VECTOR, packed_float3, cspec0, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetMultiGGXFresnel, microfacet_multi_ggx_fresnel) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGXGlassFresnel, microfacet_multi_ggx_glass_fresnel) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlassFresnel, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlassFresnel, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlassFresnel, FLOAT, float, ior, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlassFresnel, VECTOR, packed_float3, color, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlassFresnel, VECTOR, packed_float3, cspec0, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetMultiGGXGlassFresnel, microfacet_multi_ggx_glass_fresnel) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGXAnisoFresnel, microfacet_multi_ggx_aniso_fresnel) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAnisoFresnel, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAnisoFresnel, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAnisoFresnel, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAnisoFresnel, FLOAT, float, alpha_y, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAnisoFresnel, FLOAT, float, ior, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAnisoFresnel, VECTOR, packed_float3, color, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXAnisoFresnel, VECTOR, packed_float3, cspec0, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetMultiGGXAnisoFresnel, microfacet_multi_ggx_aniso_fresnel) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetBeckmannIsotropic, microfacet_beckmann) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmannIsotropic, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmannIsotropic, FLOAT, float, alpha_x, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetBeckmannIsotropic, microfacet_beckmann) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetBeckmann, microfacet_beckmann_aniso) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmann, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmann, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmann, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmann, FLOAT, float, alpha_y, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetBeckmann, microfacet_beckmann_aniso) + +OSL_CLOSURE_STRUCT_BEGIN(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmannRefraction, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmannRefraction, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetBeckmannRefraction, FLOAT, float, ior, NULL) +OSL_CLOSURE_STRUCT_END(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction) + +OSL_CLOSURE_STRUCT_BEGIN(AshikhminShirley, ashikhmin_shirley) + OSL_CLOSURE_STRUCT_MEMBER(AshikhminShirley, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(AshikhminShirley, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(AshikhminShirley, FLOAT, float, alpha_x, NULL) + OSL_CLOSURE_STRUCT_MEMBER(AshikhminShirley, FLOAT, float, alpha_y, NULL) +OSL_CLOSURE_STRUCT_END(AshikhminShirley, ashikhmin_shirley) + +OSL_CLOSURE_STRUCT_BEGIN(AshikhminVelvet, ashikhmin_velvet) + OSL_CLOSURE_STRUCT_MEMBER(AshikhminVelvet, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(AshikhminVelvet, FLOAT, float, sigma, NULL) +OSL_CLOSURE_STRUCT_END(AshikhminVelvet, ashikhmin_velvet) + +OSL_CLOSURE_STRUCT_BEGIN(DiffuseToon, diffuse_toon) + OSL_CLOSURE_STRUCT_MEMBER(DiffuseToon, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(DiffuseToon, FLOAT, float, size, NULL) + OSL_CLOSURE_STRUCT_MEMBER(DiffuseToon, FLOAT, float, smooth, NULL) +OSL_CLOSURE_STRUCT_END(DiffuseToon, diffuse_toon) + +OSL_CLOSURE_STRUCT_BEGIN(GlossyToon, glossy_toon) + OSL_CLOSURE_STRUCT_MEMBER(GlossyToon, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(GlossyToon, FLOAT, float, size, NULL) + OSL_CLOSURE_STRUCT_MEMBER(GlossyToon, FLOAT, float, smooth, NULL) +OSL_CLOSURE_STRUCT_END(GlossyToon, glossy_toon) + +OSL_CLOSURE_STRUCT_BEGIN(PrincipledDiffuse, principled_diffuse) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledDiffuse, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledDiffuse, FLOAT, float, roughness, NULL) +OSL_CLOSURE_STRUCT_END(PrincipledDiffuse, principled_diffuse) + +OSL_CLOSURE_STRUCT_BEGIN(PrincipledSheen, principled_sheen) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledSheen, VECTOR, packed_float3, N, NULL) +OSL_CLOSURE_STRUCT_END(PrincipledSheen, principled_sheen) + +OSL_CLOSURE_STRUCT_BEGIN(PrincipledClearcoat, principled_clearcoat) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledClearcoat, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledClearcoat, FLOAT, float, clearcoat, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledClearcoat, FLOAT, float, clearcoat_roughness, NULL) +OSL_CLOSURE_STRUCT_END(PrincipledClearcoat, principled_clearcoat) + +OSL_CLOSURE_STRUCT_BEGIN(GenericEmissive, emission) +OSL_CLOSURE_STRUCT_END(GenericEmissive, emission) + +OSL_CLOSURE_STRUCT_BEGIN(GenericBackground, background) +OSL_CLOSURE_STRUCT_END(GenericBackground, background) + +OSL_CLOSURE_STRUCT_BEGIN(Holdout, holdout) +OSL_CLOSURE_STRUCT_END(Holdout, holdout) + +OSL_CLOSURE_STRUCT_BEGIN(DiffuseRamp, diffuse_ramp) + OSL_CLOSURE_STRUCT_MEMBER(DiffuseRamp, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_ARRAY_MEMBER(DiffuseRamp, COLOR, packed_float3, colors, NULL, 8) +OSL_CLOSURE_STRUCT_END(DiffuseRamp, diffuse_ramp) + +OSL_CLOSURE_STRUCT_BEGIN(PhongRamp, phong_ramp) + OSL_CLOSURE_STRUCT_MEMBER(PhongRamp, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PhongRamp, FLOAT, float, exponent, NULL) + OSL_CLOSURE_STRUCT_ARRAY_MEMBER(PhongRamp, COLOR, packed_float3, colors, NULL, 8) +OSL_CLOSURE_STRUCT_END(PhongRamp, phong_ramp) + +OSL_CLOSURE_STRUCT_BEGIN(BSSRDF, bssrdf) + OSL_CLOSURE_STRUCT_MEMBER(BSSRDF, STRING, ustring, method, NULL) + OSL_CLOSURE_STRUCT_MEMBER(BSSRDF, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(BSSRDF, VECTOR, packed_float3, radius, NULL) + OSL_CLOSURE_STRUCT_MEMBER(BSSRDF, VECTOR, packed_float3, albedo, NULL) + OSL_CLOSURE_STRUCT_MEMBER(BSSRDF, FLOAT, float, roughness, "roughness") + OSL_CLOSURE_STRUCT_MEMBER(BSSRDF, FLOAT, float, ior, "ior") + OSL_CLOSURE_STRUCT_MEMBER(BSSRDF, FLOAT, float, anisotropy, "anisotropy") +OSL_CLOSURE_STRUCT_END(BSSRDF, bssrdf) + +OSL_CLOSURE_STRUCT_BEGIN(HairReflection, hair_reflection) + OSL_CLOSURE_STRUCT_MEMBER(HairReflection, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairReflection, FLOAT, float, roughness1, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairReflection, FLOAT, float, roughness2, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairReflection, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairReflection, FLOAT, float, offset, NULL) +OSL_CLOSURE_STRUCT_END(HairReflection, hair_reflection) + +OSL_CLOSURE_STRUCT_BEGIN(HairTransmission, hair_transmission) + OSL_CLOSURE_STRUCT_MEMBER(HairTransmission, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairTransmission, FLOAT, float, roughness1, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairTransmission, FLOAT, float, roughness2, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairReflection, VECTOR, packed_float3, T, NULL) + OSL_CLOSURE_STRUCT_MEMBER(HairReflection, FLOAT, float, offset, NULL) +OSL_CLOSURE_STRUCT_END(HairTransmission, hair_transmission) + +OSL_CLOSURE_STRUCT_BEGIN(PrincipledHair, principled_hair) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledHair, VECTOR, packed_float3, N, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledHair, VECTOR, packed_float3, sigma, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledHair, FLOAT, float, v, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledHair, FLOAT, float, s, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledHair, FLOAT, float, m0_roughness, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledHair, FLOAT, float, alpha, NULL) + OSL_CLOSURE_STRUCT_MEMBER(PrincipledHair, FLOAT, float, eta, NULL) +OSL_CLOSURE_STRUCT_END(PrincipledHair, principled_hair) + +OSL_CLOSURE_STRUCT_BEGIN(VolumeAbsorption, absorption) +OSL_CLOSURE_STRUCT_END(VolumeAbsorption, absorption) + +OSL_CLOSURE_STRUCT_BEGIN(VolumeHenyeyGreenstein, henyey_greenstein) + OSL_CLOSURE_STRUCT_MEMBER(VolumeHenyeyGreenstein, FLOAT, float, g, NULL) +OSL_CLOSURE_STRUCT_END(VolumeHenyeyGreenstein, henyey_greenstein) + +#undef OSL_CLOSURE_STRUCT_BEGIN +#undef OSL_CLOSURE_STRUCT_END +#undef OSL_CLOSURE_STRUCT_MEMBER +#undef OSL_CLOSURE_STRUCT_ARRAY_MEMBER diff --git a/intern/cycles/kernel/osl/emissive.cpp b/intern/cycles/kernel/osl/emissive.cpp deleted file mode 100644 index 8d1928d0126..00000000000 --- a/intern/cycles/kernel/osl/emissive.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * - * Adapted from Open Shading Language - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2011-2022 Blender Foundation. */ - -#include <OpenImageIO/fmath.h> - -#include <OSL/genclosure.h> - -#include "kernel/osl/closures.h" - -// clang-format off -#include "kernel/device/cpu/compat.h" -#include "kernel/device/cpu/globals.h" - -#include "kernel/types.h" -#include "kernel/closure/alloc.h" -#include "kernel/closure/emissive.h" - -#include "kernel/util/color.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -using namespace OSL; - -/// Variable cone emissive closure -/// -/// This primitive emits in a cone having a configurable -/// penumbra area where the light decays to 0 reaching the -/// outer_angle limit. It can also behave as a lambertian emitter -/// if the provided angles are PI/2, which is the default -/// -class GenericEmissiveClosure : public CClosurePrimitive { - public: - void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) - { - emission_setup(sd, rgb_to_spectrum(weight)); - } -}; - -ClosureParam *closure_emission_params() -{ - static ClosureParam params[] = {CLOSURE_STRING_KEYPARAM(GenericEmissiveClosure, label, "label"), - CLOSURE_FINISH_PARAM(GenericEmissiveClosure)}; - return params; -} - -CCLOSURE_PREPARE(closure_emission_prepare, GenericEmissiveClosure) - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/globals.cpp b/intern/cycles/kernel/osl/globals.cpp new file mode 100644 index 00000000000..92b91182178 --- /dev/null +++ b/intern/cycles/kernel/osl/globals.cpp @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include <OSL/oslexec.h> + +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + +#include "kernel/types.h" + +#include "kernel/osl/globals.h" +#include "kernel/osl/services.h" + +CCL_NAMESPACE_BEGIN + +void OSLGlobals::thread_init(KernelGlobalsCPU *kg, OSLGlobals *osl_globals) +{ + /* no osl used? */ + if (!osl_globals->use) { + kg->osl = NULL; + return; + } + + /* Per thread kernel data init. */ + kg->osl = osl_globals; + + OSL::ShadingSystem *ss = kg->osl->ss; + OSLThreadData *tdata = new OSLThreadData(); + + memset((void *)&tdata->globals, 0, sizeof(OSL::ShaderGlobals)); + tdata->globals.tracedata = &tdata->tracedata; + tdata->osl_thread_info = ss->create_thread_info(); + tdata->context = ss->get_context(tdata->osl_thread_info); + + tdata->oiio_thread_info = osl_globals->ts->get_perthread_info(); + + kg->osl_ss = (OSLShadingSystem *)ss; + kg->osl_tdata = tdata; +} + +void OSLGlobals::thread_free(KernelGlobalsCPU *kg) +{ + if (!kg->osl) + return; + + OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; + OSLThreadData *tdata = kg->osl_tdata; + ss->release_context(tdata->context); + + ss->destroy_thread_info(tdata->osl_thread_info); + + delete tdata; + + kg->osl = NULL; + kg->osl_ss = NULL; + kg->osl_tdata = NULL; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/globals.h b/intern/cycles/kernel/osl/globals.h index 172091c55f5..2b002a0033e 100644 --- a/intern/cycles/kernel/osl/globals.h +++ b/intern/cycles/kernel/osl/globals.h @@ -41,6 +41,10 @@ struct OSLGlobals { use = false; } + /* per thread data */ + static void thread_init(struct KernelGlobalsCPU *kg, OSLGlobals *osl_globals); + static void thread_free(struct KernelGlobalsCPU *kg); + bool use; /* shading system */ @@ -56,16 +60,8 @@ struct OSLGlobals { OSL::ShaderGroupRef background_state; /* attributes */ - struct Attribute { - TypeDesc type; - AttributeDescriptor desc; - ParamValue value; - }; - - typedef unordered_map<ustring, Attribute, ustringHash> AttributeMap; typedef unordered_map<ustring, int, ustringHash> ObjectNameMap; - vector<AttributeMap> attribute_map; ObjectNameMap object_name_map; vector<ustring> object_names; }; diff --git a/intern/cycles/kernel/osl/shader.h b/intern/cycles/kernel/osl/osl.h index f0ab49dd6a8..bef23f3eea1 100644 --- a/intern/cycles/kernel/osl/shader.h +++ b/intern/cycles/kernel/osl/osl.h @@ -1,10 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __OSL_SHADER_H__ -#define __OSL_SHADER_H__ - -#ifdef WITH_OSL +#pragma once /* OSL Shader Engine * @@ -16,30 +13,12 @@ * This means no thread state must be passed along in the kernel itself. */ -# include "kernel/types.h" +#include "kernel/osl/types.h" CCL_NAMESPACE_BEGIN -class Scene; - -struct ShaderClosure; -struct ShaderData; -struct IntegratorStateCPU; -struct differential3; -struct KernelGlobalsCPU; - -struct OSLGlobals; -struct OSLShadingSystem; - class OSLShader { public: - /* init */ - static void register_closures(OSLShadingSystem *ss); - - /* per thread data */ - static void thread_init(KernelGlobalsCPU *kg, OSLGlobals *osl_globals); - static void thread_free(KernelGlobalsCPU *kg); - /* eval */ static void eval_surface(const KernelGlobalsCPU *kg, const void *state, @@ -54,16 +33,6 @@ class OSLShader { ShaderData *sd, uint32_t path_flag); static void eval_displacement(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd); - - /* attributes */ - static int find_attribute(const KernelGlobalsCPU *kg, - const ShaderData *sd, - uint id, - AttributeDescriptor *desc); }; CCL_NAMESPACE_END - -#endif - -#endif /* __OSL_SHADER_H__ */ diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index faa027f4e1e..b744422ee78 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -18,10 +18,8 @@ #include "scene/pointcloud.h" #include "scene/scene.h" -#include "kernel/osl/closures.h" #include "kernel/osl/globals.h" #include "kernel/osl/services.h" -#include "kernel/osl/shader.h" #include "util/foreach.h" #include "util/log.h" @@ -31,8 +29,6 @@ #include "kernel/device/cpu/globals.h" #include "kernel/device/cpu/image.h" -#include "kernel/util/differential.h" - #include "kernel/integrator/state.h" #include "kernel/integrator/state_flow.h" @@ -124,14 +120,14 @@ ustring OSLRenderServices::u_v("v"); ustring OSLRenderServices::u_empty; OSLRenderServices::OSLRenderServices(OSL::TextureSystem *texture_system) - : texture_system(texture_system) + : OSL::RendererServices(texture_system) { } OSLRenderServices::~OSLRenderServices() { - if (texture_system) { - VLOG_INFO << "OSL texture system stats:\n" << texture_system->getstats(); + if (m_texturesys) { + VLOG_INFO << "OSL texture system stats:\n" << m_texturesys->getstats(); } } @@ -451,6 +447,7 @@ static bool set_attribute_float2(float2 f[3], TypeDesc type, bool derivatives, v return false; } +#if 0 static bool set_attribute_float2(float2 f, TypeDesc type, bool derivatives, void *val) { float2 fv[3]; @@ -461,6 +458,7 @@ static bool set_attribute_float2(float2 f, TypeDesc type, bool derivatives, void return set_attribute_float2(fv, type, derivatives, val); } +#endif static bool set_attribute_float3(float3 f[3], TypeDesc type, bool derivatives, void *val) { @@ -589,6 +587,7 @@ static bool set_attribute_float4(float4 f[3], TypeDesc type, bool derivatives, v return false; } +#if 0 static bool set_attribute_float4(float4 f, TypeDesc type, bool derivatives, void *val) { float4 fv[3]; @@ -599,6 +598,7 @@ static bool set_attribute_float4(float4 f, TypeDesc type, bool derivatives, void return set_attribute_float4(fv, type, derivatives, val); } +#endif static bool set_attribute_float(float f[3], TypeDesc type, bool derivatives, void *val) { @@ -740,76 +740,75 @@ static bool set_attribute_matrix(const Transform &tfm, TypeDesc type, void *val) return false; } -static bool get_primitive_attribute(const KernelGlobalsCPU *kg, - const ShaderData *sd, - const OSLGlobals::Attribute &attr, - const TypeDesc &type, - bool derivatives, - void *val) +static bool get_object_attribute(const KernelGlobalsCPU *kg, + ShaderData *sd, + const AttributeDescriptor &desc, + const TypeDesc &type, + bool derivatives, + void *val) { - if (attr.type == TypeDesc::TypePoint || attr.type == TypeDesc::TypeVector || - attr.type == TypeDesc::TypeNormal || attr.type == TypeDesc::TypeColor) { + if (desc.type == NODE_ATTR_FLOAT3) { float3 fval[3]; - if (primitive_is_volume_attribute(sd, attr.desc)) { - fval[0] = primitive_volume_attribute_float3(kg, sd, attr.desc); +#ifdef __VOLUME__ + if (primitive_is_volume_attribute(sd, desc)) { + fval[0] = primitive_volume_attribute_float3(kg, sd, desc); } - else { + else +#endif + { memset(fval, 0, sizeof(fval)); fval[0] = primitive_surface_attribute_float3( - kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); + kg, sd, desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); } return set_attribute_float3(fval, type, derivatives, val); } - else if (attr.type == TypeFloat2) { - if (primitive_is_volume_attribute(sd, attr.desc)) { + else if (desc.type == NODE_ATTR_FLOAT2) { +#ifdef __VOLUME__ + if (primitive_is_volume_attribute(sd, desc)) { assert(!"Float2 attribute not support for volumes"); return false; } - else { + else +#endif + { float2 fval[3]; fval[0] = primitive_surface_attribute_float2( - kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); + kg, sd, desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); return set_attribute_float2(fval, type, derivatives, val); } } - else if (attr.type == TypeDesc::TypeFloat) { + else if (desc.type == NODE_ATTR_FLOAT) { float fval[3]; - if (primitive_is_volume_attribute(sd, attr.desc)) { +#ifdef __VOLUME__ + if (primitive_is_volume_attribute(sd, desc)) { memset(fval, 0, sizeof(fval)); - fval[0] = primitive_volume_attribute_float(kg, sd, attr.desc); + fval[0] = primitive_volume_attribute_float(kg, sd, desc); } - else { + else +#endif + { fval[0] = primitive_surface_attribute_float( - kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); + kg, sd, desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); } return set_attribute_float(fval, type, derivatives, val); } - else if (attr.type == TypeDesc::TypeFloat4 || attr.type == TypeRGBA) { + else if (desc.type == NODE_ATTR_FLOAT4 || desc.type == NODE_ATTR_RGBA) { float4 fval[3]; - if (primitive_is_volume_attribute(sd, attr.desc)) { +#ifdef __VOLUME__ + if (primitive_is_volume_attribute(sd, desc)) { memset(fval, 0, sizeof(fval)); - fval[0] = primitive_volume_attribute_float4(kg, sd, attr.desc); + fval[0] = primitive_volume_attribute_float4(kg, sd, desc); } - else { + else +#endif + { fval[0] = primitive_surface_attribute_float4( - kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); + kg, sd, desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); } return set_attribute_float4(fval, type, derivatives, val); } - else { - return false; - } -} - -static bool get_mesh_attribute(const KernelGlobalsCPU *kg, - const ShaderData *sd, - const OSLGlobals::Attribute &attr, - const TypeDesc &type, - bool derivatives, - void *val) -{ - if (attr.type == TypeDesc::TypeMatrix) { - Transform tfm = primitive_attribute_matrix(kg, sd, attr.desc); + else if (desc.type == NODE_ATTR_MATRIX) { + Transform tfm = primitive_attribute_matrix(kg, desc); return set_attribute_matrix(tfm, type, val); } else { @@ -817,44 +816,6 @@ static bool get_mesh_attribute(const KernelGlobalsCPU *kg, } } -static bool get_object_attribute(const OSLGlobals::Attribute &attr, - TypeDesc type, - bool derivatives, - void *val) -{ - if (attr.type == TypeDesc::TypePoint || attr.type == TypeDesc::TypeVector || - attr.type == TypeDesc::TypeNormal || attr.type == TypeDesc::TypeColor) { - const float *data = (const float *)attr.value.data(); - return set_attribute_float3(make_float3(data[0], data[1], data[2]), type, derivatives, val); - } - else if (attr.type == TypeFloat2) { - const float *data = (const float *)attr.value.data(); - return set_attribute_float2(make_float2(data[0], data[1]), type, derivatives, val); - } - else if (attr.type == TypeDesc::TypeFloat) { - const float *data = (const float *)attr.value.data(); - return set_attribute_float(data[0], type, derivatives, val); - } - else if (attr.type == TypeRGBA || attr.type == TypeDesc::TypeFloat4) { - const float *data = (const float *)attr.value.data(); - return set_attribute_float4( - make_float4(data[0], data[1], data[2], data[3]), type, derivatives, val); - } - else if (attr.type == type) { - size_t datasize = attr.value.datasize(); - - memcpy(val, attr.value.data(), datasize); - if (derivatives) { - memset((char *)val + datasize, 0, datasize * 2); - } - - return true; - } - else { - return false; - } -} - bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg, ShaderData *sd, ustring name, @@ -979,6 +940,7 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg float f = ((sd->shader & SHADER_SMOOTH_NORMAL) != 0); return set_attribute_float(f, type, derivatives, val); } +#ifdef __HAIR__ /* Hair Attributes */ else if (name == u_is_curve) { float f = (sd->type & PRIMITIVE_CURVE) != 0; @@ -996,6 +958,8 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg float f = curve_random(kg, sd); return set_attribute_float(f, type, derivatives, val); } +#endif +#ifdef __POINTCLOUD__ /* point attributes */ else if (name == u_is_point) { float f = (sd->type & PRIMITIVE_POINT) != 0; @@ -1013,6 +977,7 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg float f = point_random(kg, sd); return set_attribute_float(f, type, derivatives, val); } +#endif else if (name == u_normal_map_normal) { if (sd->type & PRIMITIVE_TRIANGLE) { float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v); @@ -1023,7 +988,7 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg } } else { - return false; + return get_background_attribute(kg, sd, name, type, derivatives, val); } } @@ -1131,7 +1096,6 @@ bool OSLRenderServices::get_attribute( ShaderData *sd, bool derivatives, ustring object_name, TypeDesc type, ustring name, void *val) { const KernelGlobalsCPU *kg = sd->osl_globals; - int prim_type = 0; int object; /* lookup of attribute on another object */ @@ -1145,44 +1109,18 @@ bool OSLRenderServices::get_attribute( } else { object = sd->object; - prim_type = attribute_primitive_type(kg, sd); - - if (object == OBJECT_NONE) - return get_background_attribute(kg, sd, name, type, derivatives, val); } /* find attribute on object */ - object = object * ATTR_PRIM_TYPES + prim_type; - OSLGlobals::AttributeMap &attribute_map = kg->osl->attribute_map[object]; - OSLGlobals::AttributeMap::iterator it = attribute_map.find(name); - - if (it != attribute_map.end()) { - const OSLGlobals::Attribute &attr = it->second; - - if (attr.desc.element != ATTR_ELEMENT_OBJECT) { - /* triangle and vertex attributes */ - if (get_primitive_attribute(kg, sd, attr, type, derivatives, val)) - return true; - else - return get_mesh_attribute(kg, sd, attr, type, derivatives, val); - } - else { - /* object attribute */ - return get_object_attribute(attr, type, derivatives, val); - } + const AttributeDescriptor desc = find_attribute( + kg, object, sd->prim, object == sd->object ? sd->type : PRIMITIVE_NONE, name.hash()); + if (desc.offset != ATTR_STD_NOT_FOUND) { + return get_object_attribute(kg, sd, desc, type, derivatives, val); } else { /* not found in attribute, check standard object info */ - bool is_std_object_attribute = get_object_standard_attribute( - kg, sd, name, type, derivatives, val); - - if (is_std_object_attribute) - return true; - - return get_background_attribute(kg, sd, name, type, derivatives, val); + return get_object_standard_attribute(kg, sd, name, type, derivatives, val); } - - return false; } bool OSLRenderServices::get_userdata( @@ -1209,7 +1147,7 @@ TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring file } /* Get handle from OpenImageIO. */ - OSL::TextureSystem *ts = texture_system; + OSL::TextureSystem *ts = m_texturesys; TextureSystem::TextureHandle *handle = ts->get_texture_handle(filename); if (handle == NULL) { return NULL; @@ -1231,7 +1169,7 @@ bool OSLRenderServices::good(TextureSystem::TextureHandle *texture_handle) OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; if (handle->oiio_handle) { - OSL::TextureSystem *ts = texture_system; + OSL::TextureSystem *ts = m_texturesys; return ts->good(handle->oiio_handle); } else { @@ -1353,7 +1291,7 @@ bool OSLRenderServices::texture(ustring filename, } case OSLTextureHandle::OIIO: { /* OpenImageIO texture cache. */ - OSL::TextureSystem *ts = texture_system; + OSL::TextureSystem *ts = m_texturesys; if (handle && handle->oiio_handle) { if (texture_thread_info == NULL) { @@ -1457,7 +1395,7 @@ bool OSLRenderServices::texture3d(ustring filename, } case OSLTextureHandle::OIIO: { /* OpenImageIO texture cache. */ - OSL::TextureSystem *ts = texture_system; + OSL::TextureSystem *ts = m_texturesys; if (handle && handle->oiio_handle) { if (texture_thread_info == NULL) { @@ -1541,7 +1479,7 @@ bool OSLRenderServices::environment(ustring filename, ustring *errormessage) { OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; - OSL::TextureSystem *ts = texture_system; + OSL::TextureSystem *ts = m_texturesys; bool status = false; if (handle && handle->oiio_handle) { @@ -1613,7 +1551,7 @@ bool OSLRenderServices::get_texture_info(OSL::ShaderGlobals *sg, } /* Get texture info from OpenImageIO. */ - OSL::TextureSystem *ts = texture_system; + OSL::TextureSystem *ts = m_texturesys; return ts->get_texture_info(filename, subimage, dataname, datatype, data); } @@ -1667,8 +1605,8 @@ bool OSLRenderServices::trace(TraceOpt &options, /* setup ray */ Ray ray; - ray.P = TO_FLOAT3(P); - ray.D = TO_FLOAT3(R); + ray.P = make_float3(P.x, P.y, P.z); + ray.D = make_float3(R.x, R.y, R.z); ray.tmin = 0.0f; ray.tmax = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist; ray.time = sd->time; @@ -1691,12 +1629,12 @@ bool OSLRenderServices::trace(TraceOpt &options, /* ray differentials */ differential3 dP; - dP.dx = TO_FLOAT3(dPdx); - dP.dy = TO_FLOAT3(dPdy); + dP.dx = make_float3(dPdx.x, dPdx.y, dPdx.z); + dP.dy = make_float3(dPdy.x, dPdy.y, dPdy.z); ray.dP = differential_make_compact(dP); differential3 dD; - dD.dx = TO_FLOAT3(dRdx); - dD.dy = TO_FLOAT3(dRdy); + dD.dx = make_float3(dRdx.x, dRdx.y, dRdx.z); + dD.dy = make_float3(dRdy.x, dRdy.y, dRdy.z); ray.dD = differential_make_compact(dD); /* allocate trace data */ diff --git a/intern/cycles/kernel/osl/services.h b/intern/cycles/kernel/osl/services.h index edffd912bad..334b6682e34 100644 --- a/intern/cycles/kernel/osl/services.h +++ b/intern/cycles/kernel/osl/services.h @@ -76,6 +76,8 @@ class OSLRenderServices : public OSL::RendererServices { OSLRenderServices(OSL::TextureSystem *texture_system); ~OSLRenderServices(); + static void register_closures(OSL::ShadingSystem *ss); + bool get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, OSL::TransformationPtr xform, @@ -321,7 +323,6 @@ class OSLRenderServices : public OSL::RendererServices { * globals to be shared between different render sessions. This saves memory, * and is required because texture handles are cached as part of the shared * shading system. */ - OSL::TextureSystem *texture_system; OSLTextureHandleMap textures; }; diff --git a/intern/cycles/kernel/osl/shader.cpp b/intern/cycles/kernel/osl/shader.cpp deleted file mode 100644 index 5862b6a8a2b..00000000000 --- a/intern/cycles/kernel/osl/shader.cpp +++ /dev/null @@ -1,425 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#include <OSL/oslexec.h> - -// clang-format off -#include "kernel/device/cpu/compat.h" -#include "kernel/device/cpu/globals.h" - -#include "kernel/types.h" - -#include "kernel/geom/object.h" - -#include "kernel/integrator/state.h" - -#include "kernel/osl/closures.h" -#include "kernel/osl/globals.h" -#include "kernel/osl/services.h" -#include "kernel/osl/shader.h" - -#include "kernel/util/differential.h" -// clang-format on - -#include "scene/attribute.h" - -CCL_NAMESPACE_BEGIN - -/* Threads */ - -void OSLShader::thread_init(KernelGlobalsCPU *kg, OSLGlobals *osl_globals) -{ - /* no osl used? */ - if (!osl_globals->use) { - kg->osl = NULL; - return; - } - - /* Per thread kernel data init. */ - kg->osl = osl_globals; - - OSL::ShadingSystem *ss = kg->osl->ss; - OSLThreadData *tdata = new OSLThreadData(); - - memset((void *)&tdata->globals, 0, sizeof(OSL::ShaderGlobals)); - tdata->globals.tracedata = &tdata->tracedata; - tdata->globals.flipHandedness = false; - tdata->osl_thread_info = ss->create_thread_info(); - tdata->context = ss->get_context(tdata->osl_thread_info); - - tdata->oiio_thread_info = osl_globals->ts->get_perthread_info(); - - kg->osl_ss = (OSLShadingSystem *)ss; - kg->osl_tdata = tdata; -} - -void OSLShader::thread_free(KernelGlobalsCPU *kg) -{ - if (!kg->osl) - return; - - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; - OSLThreadData *tdata = kg->osl_tdata; - ss->release_context(tdata->context); - - ss->destroy_thread_info(tdata->osl_thread_info); - - delete tdata; - - kg->osl = NULL; - kg->osl_ss = NULL; - kg->osl_tdata = NULL; -} - -/* Globals */ - -static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, - ShaderData *sd, - const void *state, - uint32_t path_flag, - OSLThreadData *tdata) -{ - OSL::ShaderGlobals *globals = &tdata->globals; - - const differential3 dP = differential_from_compact(sd->Ng, sd->dP); - const differential3 dI = differential_from_compact(sd->I, sd->dI); - - /* copy from shader data to shader globals */ - globals->P = TO_VEC3(sd->P); - globals->dPdx = TO_VEC3(dP.dx); - globals->dPdy = TO_VEC3(dP.dy); - globals->I = TO_VEC3(sd->I); - globals->dIdx = TO_VEC3(dI.dx); - globals->dIdy = TO_VEC3(dI.dy); - globals->N = TO_VEC3(sd->N); - globals->Ng = TO_VEC3(sd->Ng); - globals->u = sd->u; - globals->dudx = sd->du.dx; - globals->dudy = sd->du.dy; - globals->v = sd->v; - globals->dvdx = sd->dv.dx; - globals->dvdy = sd->dv.dy; - globals->dPdu = TO_VEC3(sd->dPdu); - globals->dPdv = TO_VEC3(sd->dPdv); - globals->surfacearea = 1.0f; - globals->time = sd->time; - - /* booleans */ - globals->raytype = path_flag; - globals->backfacing = (sd->flag & SD_BACKFACING); - - /* shader data to be used in services callbacks */ - globals->renderstate = sd; - - /* hacky, we leave it to services to fetch actual object matrix */ - globals->shader2common = sd; - globals->object2common = sd; - - /* must be set to NULL before execute */ - globals->Ci = NULL; - - /* clear trace data */ - tdata->tracedata.init = false; - - /* Used by render-services. */ - sd->osl_globals = kg; - if (path_flag & PATH_RAY_SHADOW) { - sd->osl_path_state = nullptr; - sd->osl_shadow_path_state = (const IntegratorShadowStateCPU *)state; - } - else { - sd->osl_path_state = (const IntegratorStateCPU *)state; - sd->osl_shadow_path_state = nullptr; - } -} - -/* Surface */ - -static void flatten_surface_closure_tree(ShaderData *sd, - uint32_t path_flag, - const OSL::ClosureColor *closure, - float3 weight = make_float3(1.0f, 1.0f, 1.0f)) -{ - /* OSL gives us a closure tree, we flatten it into arrays per - * closure type, for evaluation, sampling, etc later on. */ - - switch (closure->id) { - case OSL::ClosureColor::MUL: { - OSL::ClosureMul *mul = (OSL::ClosureMul *)closure; - flatten_surface_closure_tree(sd, path_flag, mul->closure, TO_FLOAT3(mul->weight) * weight); - break; - } - case OSL::ClosureColor::ADD: { - OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure; - flatten_surface_closure_tree(sd, path_flag, add->closureA, weight); - flatten_surface_closure_tree(sd, path_flag, add->closureB, weight); - break; - } - default: { - OSL::ClosureComponent *comp = (OSL::ClosureComponent *)closure; - CClosurePrimitive *prim = (CClosurePrimitive *)comp->data(); - - if (prim) { -#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS - weight = weight * TO_FLOAT3(comp->w); -#endif - prim->setup(sd, path_flag, weight); - } - break; - } - } -} - -void OSLShader::eval_surface(const KernelGlobalsCPU *kg, - const void *state, - ShaderData *sd, - uint32_t path_flag) -{ - /* setup shader globals from shader data */ - OSLThreadData *tdata = kg->osl_tdata; - shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata); - - /* execute shader for this point */ - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; - OSL::ShaderGlobals *globals = &tdata->globals; - OSL::ShadingContext *octx = tdata->context; - int shader = sd->shader & SHADER_MASK; - - /* automatic bump shader */ - if (kg->osl->bump_state[shader]) { - /* save state */ - const float3 P = sd->P; - const float dP = sd->dP; - const OSL::Vec3 dPdx = globals->dPdx; - const OSL::Vec3 dPdy = globals->dPdy; - - /* set state as if undisplaced */ - if (sd->flag & SD_HAS_DISPLACEMENT) { - float data[9]; - bool found = kg->osl->services->get_attribute(sd, - true, - OSLRenderServices::u_empty, - TypeDesc::TypeVector, - OSLRenderServices::u_geom_undisplaced, - data); - (void)found; - assert(found); - - differential3 tmp_dP; - memcpy(&sd->P, data, sizeof(float) * 3); - memcpy(&tmp_dP.dx, data + 3, sizeof(float) * 3); - memcpy(&tmp_dP.dy, data + 6, sizeof(float) * 3); - - object_position_transform(kg, sd, &sd->P); - object_dir_transform(kg, sd, &tmp_dP.dx); - object_dir_transform(kg, sd, &tmp_dP.dy); - - sd->dP = differential_make_compact(tmp_dP); - - globals->P = TO_VEC3(sd->P); - globals->dPdx = TO_VEC3(tmp_dP.dx); - globals->dPdy = TO_VEC3(tmp_dP.dy); - } - - /* execute bump shader */ - ss->execute(octx, *(kg->osl->bump_state[shader]), *globals); - - /* reset state */ - sd->P = P; - sd->dP = dP; - - globals->P = TO_VEC3(P); - globals->dPdx = TO_VEC3(dPdx); - globals->dPdy = TO_VEC3(dPdy); - } - - /* surface shader */ - if (kg->osl->surface_state[shader]) { - ss->execute(octx, *(kg->osl->surface_state[shader]), *globals); - } - - /* flatten closure tree */ - if (globals->Ci) - flatten_surface_closure_tree(sd, path_flag, globals->Ci); -} - -/* Background */ - -static void flatten_background_closure_tree(ShaderData *sd, - const OSL::ClosureColor *closure, - float3 weight = make_float3(1.0f, 1.0f, 1.0f)) -{ - /* OSL gives us a closure tree, if we are shading for background there - * is only one supported closure type at the moment, which has no evaluation - * functions, so we just sum the weights */ - - switch (closure->id) { - case OSL::ClosureColor::MUL: { - OSL::ClosureMul *mul = (OSL::ClosureMul *)closure; - flatten_background_closure_tree(sd, mul->closure, weight * TO_FLOAT3(mul->weight)); - break; - } - case OSL::ClosureColor::ADD: { - OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure; - - flatten_background_closure_tree(sd, add->closureA, weight); - flatten_background_closure_tree(sd, add->closureB, weight); - break; - } - default: { - OSL::ClosureComponent *comp = (OSL::ClosureComponent *)closure; - CClosurePrimitive *prim = (CClosurePrimitive *)comp->data(); - - if (prim) { -#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS - weight = weight * TO_FLOAT3(comp->w); -#endif - prim->setup(sd, 0, weight); - } - break; - } - } -} - -void OSLShader::eval_background(const KernelGlobalsCPU *kg, - const void *state, - ShaderData *sd, - uint32_t path_flag) -{ - /* setup shader globals from shader data */ - OSLThreadData *tdata = kg->osl_tdata; - shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata); - - /* execute shader for this point */ - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; - OSL::ShaderGlobals *globals = &tdata->globals; - OSL::ShadingContext *octx = tdata->context; - - if (kg->osl->background_state) { - ss->execute(octx, *(kg->osl->background_state), *globals); - } - - /* return background color immediately */ - if (globals->Ci) - flatten_background_closure_tree(sd, globals->Ci); -} - -/* Volume */ - -static void flatten_volume_closure_tree(ShaderData *sd, - const OSL::ClosureColor *closure, - float3 weight = make_float3(1.0f, 1.0f, 1.0f)) -{ - /* OSL gives us a closure tree, we flatten it into arrays per - * closure type, for evaluation, sampling, etc later on. */ - - switch (closure->id) { - case OSL::ClosureColor::MUL: { - OSL::ClosureMul *mul = (OSL::ClosureMul *)closure; - flatten_volume_closure_tree(sd, mul->closure, TO_FLOAT3(mul->weight) * weight); - break; - } - case OSL::ClosureColor::ADD: { - OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure; - flatten_volume_closure_tree(sd, add->closureA, weight); - flatten_volume_closure_tree(sd, add->closureB, weight); - break; - } - default: { - OSL::ClosureComponent *comp = (OSL::ClosureComponent *)closure; - CClosurePrimitive *prim = (CClosurePrimitive *)comp->data(); - - if (prim) { -#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS - weight = weight * TO_FLOAT3(comp->w); -#endif - prim->setup(sd, 0, weight); - } - } - } -} - -void OSLShader::eval_volume(const KernelGlobalsCPU *kg, - const void *state, - ShaderData *sd, - uint32_t path_flag) -{ - /* setup shader globals from shader data */ - OSLThreadData *tdata = kg->osl_tdata; - shaderdata_to_shaderglobals(kg, sd, state, path_flag, tdata); - - /* execute shader */ - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; - OSL::ShaderGlobals *globals = &tdata->globals; - OSL::ShadingContext *octx = tdata->context; - int shader = sd->shader & SHADER_MASK; - - if (kg->osl->volume_state[shader]) { - ss->execute(octx, *(kg->osl->volume_state[shader]), *globals); - } - - /* flatten closure tree */ - if (globals->Ci) - flatten_volume_closure_tree(sd, globals->Ci); -} - -/* Displacement */ - -void OSLShader::eval_displacement(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd) -{ - /* setup shader globals from shader data */ - OSLThreadData *tdata = kg->osl_tdata; - - shaderdata_to_shaderglobals(kg, sd, state, 0, tdata); - - /* execute shader */ - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)kg->osl_ss; - OSL::ShaderGlobals *globals = &tdata->globals; - OSL::ShadingContext *octx = tdata->context; - int shader = sd->shader & SHADER_MASK; - - if (kg->osl->displacement_state[shader]) { - ss->execute(octx, *(kg->osl->displacement_state[shader]), *globals); - } - - /* get back position */ - sd->P = TO_FLOAT3(globals->P); -} - -/* Attributes */ - -int OSLShader::find_attribute(const KernelGlobalsCPU *kg, - const ShaderData *sd, - uint id, - AttributeDescriptor *desc) -{ - /* for OSL, a hash map is used to lookup the attribute by name. */ - int object = sd->object * ATTR_PRIM_TYPES; - - OSLGlobals::AttributeMap &attr_map = kg->osl->attribute_map[object]; - ustring stdname(std::string("geom:") + - std::string(Attribute::standard_name((AttributeStandard)id))); - OSLGlobals::AttributeMap::const_iterator it = attr_map.find(stdname); - - if (it != attr_map.end()) { - const OSLGlobals::Attribute &osl_attr = it->second; - *desc = osl_attr.desc; - - if (sd->prim == PRIM_NONE && (AttributeElement)osl_attr.desc.element != ATTR_ELEMENT_MESH) { - desc->offset = ATTR_STD_NOT_FOUND; - return ATTR_STD_NOT_FOUND; - } - - /* return result */ - if (osl_attr.desc.element == ATTR_ELEMENT_NONE) { - desc->offset = ATTR_STD_NOT_FOUND; - } - return desc->offset; - } - else { - desc->offset = ATTR_STD_NOT_FOUND; - return (int)ATTR_STD_NOT_FOUND; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/types.h b/intern/cycles/kernel/osl/types.h new file mode 100644 index 00000000000..46e06114360 --- /dev/null +++ b/intern/cycles/kernel/osl/types.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Closure */ + +enum ClosureTypeOSL { + OSL_CLOSURE_MUL_ID = -1, + OSL_CLOSURE_ADD_ID = -2, + + OSL_CLOSURE_NONE_ID = 0, + +#define OSL_CLOSURE_STRUCT_BEGIN(Upper, lower) OSL_CLOSURE_##Upper##_ID, +#include "closures_template.h" +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 873d594f1f8..bd3791594e0 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -655,12 +655,11 @@ typedef struct AttributeDescriptor { /* For looking up attributes on objects and geometry. */ typedef struct AttributeMap { - uint id; /* Global unique identifier. */ - uint element; /* AttributeElement. */ - int offset; /* Offset into __attributes global arrays. */ - uint8_t type; /* NodeAttributeType. */ - uint8_t flags; /* AttributeFlag. */ - uint8_t pad[2]; + uint64_t id; /* Global unique identifier. */ + int offset; /* Offset into __attributes global arrays. */ + uint16_t element; /* AttributeElement. */ + uint8_t type; /* NodeAttributeType. */ + uint8_t flags; /* AttributeFlag. */ } AttributeMap; /* Closure data */ diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index ae8dcaa43b6..d1a3df851c1 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -302,111 +302,32 @@ GeometryManager::~GeometryManager() { } -void GeometryManager::update_osl_attributes(Device *device, - Scene *scene, - vector<AttributeRequestSet> &geom_attributes) +void GeometryManager::update_osl_globals(Device *device, Scene *scene) { #ifdef WITH_OSL - /* for OSL, a hash map is used to lookup the attribute by name. */ OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); og->object_name_map.clear(); - og->attribute_map.clear(); og->object_names.clear(); - og->attribute_map.resize(scene->objects.size() * ATTR_PRIM_TYPES); - for (size_t i = 0; i < scene->objects.size(); i++) { /* set object name to object index map */ Object *object = scene->objects[i]; og->object_name_map[object->name] = i; og->object_names.push_back(object->name); - - /* set object attributes */ - foreach (ParamValue &attr, object->attributes) { - OSLGlobals::Attribute osl_attr; - - osl_attr.type = attr.type(); - osl_attr.desc.element = ATTR_ELEMENT_OBJECT; - osl_attr.value = attr; - osl_attr.desc.offset = 0; - osl_attr.desc.flags = 0; - - og->attribute_map[i * ATTR_PRIM_TYPES + ATTR_PRIM_GEOMETRY][attr.name()] = osl_attr; - og->attribute_map[i * ATTR_PRIM_TYPES + ATTR_PRIM_SUBD][attr.name()] = osl_attr; - } - - /* find geometry attributes */ - size_t j = object->geometry->index; - assert(j < scene->geometry.size() && scene->geometry[j] == object->geometry); - - AttributeRequestSet &attributes = geom_attributes[j]; - - /* set mesh attributes */ - foreach (AttributeRequest &req, attributes.requests) { - OSLGlobals::Attribute osl_attr; - - if (req.desc.element != ATTR_ELEMENT_NONE) { - osl_attr.desc = req.desc; - - if (req.type == TypeDesc::TypeFloat) - osl_attr.type = TypeDesc::TypeFloat; - else if (req.type == TypeDesc::TypeMatrix) - osl_attr.type = TypeDesc::TypeMatrix; - else if (req.type == TypeFloat2) - osl_attr.type = TypeFloat2; - else if (req.type == TypeRGBA) - osl_attr.type = TypeRGBA; - else - osl_attr.type = TypeDesc::TypeColor; - - if (req.std != ATTR_STD_NONE) { - /* if standard attribute, add lookup by geom: name convention */ - ustring stdname(string("geom:") + string(Attribute::standard_name(req.std))); - og->attribute_map[i * ATTR_PRIM_TYPES + ATTR_PRIM_GEOMETRY][stdname] = osl_attr; - } - else if (req.name != ustring()) { - /* add lookup by geometry attribute name */ - og->attribute_map[i * ATTR_PRIM_TYPES + ATTR_PRIM_GEOMETRY][req.name] = osl_attr; - } - } - - if (req.subd_desc.element != ATTR_ELEMENT_NONE) { - osl_attr.desc = req.subd_desc; - - if (req.subd_type == TypeDesc::TypeFloat) - osl_attr.type = TypeDesc::TypeFloat; - else if (req.subd_type == TypeDesc::TypeMatrix) - osl_attr.type = TypeDesc::TypeMatrix; - else if (req.subd_type == TypeFloat2) - osl_attr.type = TypeFloat2; - else if (req.subd_type == TypeRGBA) - osl_attr.type = TypeRGBA; - else - osl_attr.type = TypeDesc::TypeColor; - - if (req.std != ATTR_STD_NONE) { - /* if standard attribute, add lookup by geom: name convention */ - ustring stdname(string("geom:") + string(Attribute::standard_name(req.std))); - og->attribute_map[i * ATTR_PRIM_TYPES + ATTR_PRIM_SUBD][stdname] = osl_attr; - } - else if (req.name != ustring()) { - /* add lookup by geometry attribute name */ - og->attribute_map[i * ATTR_PRIM_TYPES + ATTR_PRIM_SUBD][req.name] = osl_attr; - } - } - } } #else (void)device; (void)scene; - (void)geom_attributes; #endif } /* Generate a normal attribute map entry from an attribute descriptor. */ -static void emit_attribute_map_entry( - AttributeMap *attr_map, int index, uint id, TypeDesc type, const AttributeDescriptor &desc) +static void emit_attribute_map_entry(AttributeMap *attr_map, + size_t index, + uint64_t id, + TypeDesc type, + const AttributeDescriptor &desc) { attr_map[index].id = id; attr_map[index].element = desc.element; @@ -431,7 +352,7 @@ static void emit_attribute_map_entry( /* Generate an attribute map end marker, optionally including a link to another map. * Links are used to connect object attribute maps to mesh attribute maps. */ static void emit_attribute_map_terminator(AttributeMap *attr_map, - int index, + size_t index, bool chain, uint chain_link) { @@ -446,15 +367,8 @@ static void emit_attribute_map_terminator(AttributeMap *attr_map, /* Generate all necessary attribute map entries from the attribute request. */ static void emit_attribute_mapping( - AttributeMap *attr_map, int index, Scene *scene, AttributeRequest &req, Geometry *geom) + AttributeMap *attr_map, size_t index, uint64_t id, AttributeRequest &req, Geometry *geom) { - uint id; - - if (req.std == ATTR_STD_NONE) - id = scene->shader_manager->get_attribute_id(req.name); - else - id = scene->shader_manager->get_attribute_id(req.std); - emit_attribute_map_entry(attr_map, index, id, req.type, req.desc); if (geom->is_mesh()) { @@ -475,12 +389,26 @@ void GeometryManager::update_svm_attributes(Device *, * attribute, based on a unique shader attribute id. */ /* compute array stride */ - int attr_map_size = 0; + size_t attr_map_size = 0; for (size_t i = 0; i < scene->geometry.size(); i++) { Geometry *geom = scene->geometry[i]; geom->attr_map_offset = attr_map_size; - attr_map_size += (geom_attributes[i].size() + 1) * ATTR_PRIM_TYPES; + +#ifdef WITH_OSL + size_t attr_count = 0; + foreach (AttributeRequest &req, geom_attributes[i].requests) { + if (req.std != ATTR_STD_NONE && + scene->shader_manager->get_attribute_id(req.std) != (uint64_t)req.std) + attr_count += 2; + else + attr_count += 1; + } +#else + const size_t attr_count = geom_attributes[i].size(); +#endif + + attr_map_size += (attr_count + 1) * ATTR_PRIM_TYPES; } for (size_t i = 0; i < scene->objects.size(); i++) { @@ -512,11 +440,26 @@ void GeometryManager::update_svm_attributes(Device *, AttributeRequestSet &attributes = geom_attributes[i]; /* set geometry attributes */ - int index = geom->attr_map_offset; + size_t index = geom->attr_map_offset; foreach (AttributeRequest &req, attributes.requests) { - emit_attribute_mapping(attr_map, index, scene, req, geom); + uint64_t id; + if (req.std == ATTR_STD_NONE) + id = scene->shader_manager->get_attribute_id(req.name); + else + id = scene->shader_manager->get_attribute_id(req.std); + + emit_attribute_mapping(attr_map, index, id, req, geom); index += ATTR_PRIM_TYPES; + +#ifdef WITH_OSL + /* Some standard attributes are explicitly referenced via their standard ID, so add those + * again in case they were added under a different attribute ID. */ + if (req.std != ATTR_STD_NONE && id != (uint64_t)req.std) { + emit_attribute_mapping(attr_map, index, (uint64_t)req.std, req, geom); + index += ATTR_PRIM_TYPES; + } +#endif } emit_attribute_map_terminator(attr_map, index, false, 0); @@ -528,10 +471,16 @@ void GeometryManager::update_svm_attributes(Device *, /* set object attributes */ if (attributes.size() > 0) { - int index = object->attr_map_offset; + size_t index = object->attr_map_offset; foreach (AttributeRequest &req, attributes.requests) { - emit_attribute_mapping(attr_map, index, scene, req, object->geometry); + uint64_t id; + if (req.std == ATTR_STD_NONE) + id = scene->shader_manager->get_attribute_id(req.name); + else + id = scene->shader_manager->get_attribute_id(req.std); + + emit_attribute_mapping(attr_map, index, id, req, object->geometry); index += ATTR_PRIM_TYPES; } @@ -982,7 +931,7 @@ void GeometryManager::device_update_attributes(Device *device, /* create attribute lookup maps */ if (scene->shader_manager->use_osl()) - update_osl_attributes(device, scene, geom_attributes); + update_osl_globals(device, scene); update_svm_attributes(device, dscene, scene, geom_attributes, object_attributes); @@ -2188,7 +2137,6 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc if (og) { og->object_name_map.clear(); - og->attribute_map.clear(); og->object_names.clear(); } #else diff --git a/intern/cycles/scene/geometry.h b/intern/cycles/scene/geometry.h index 6210a64509a..8a1bdc33a6f 100644 --- a/intern/cycles/scene/geometry.h +++ b/intern/cycles/scene/geometry.h @@ -219,9 +219,7 @@ class GeometryManager { void create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress); /* Attributes */ - void update_osl_attributes(Device *device, - Scene *scene, - vector<AttributeRequestSet> &geom_attributes); + void update_osl_globals(Device *device, Scene *scene); void update_svm_attributes(Device *device, DeviceScene *dscene, Scene *scene, diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index f5ee0c0f1d3..93839facdbe 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -17,7 +17,6 @@ # include "kernel/osl/globals.h" # include "kernel/osl/services.h" -# include "kernel/osl/shader.h" # include "util/aligned_malloc.h" # include "util/foreach.h" @@ -78,6 +77,18 @@ void OSLShaderManager::reset(Scene * /*scene*/) shading_system_init(); } +uint64_t OSLShaderManager::get_attribute_id(ustring name) +{ + return name.hash(); +} + +uint64_t OSLShaderManager::get_attribute_id(AttributeStandard std) +{ + /* if standard attribute, use geom: name convention */ + ustring stdname(string("geom:") + string(Attribute::standard_name(std))); + return stdname.hash(); +} + void OSLShaderManager::device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, @@ -286,7 +297,7 @@ void OSLShaderManager::shading_system_init() const int nraytypes = sizeof(raytypes) / sizeof(raytypes[0]); ss_shared->attribute("raytypes", TypeDesc(TypeDesc::STRING, nraytypes), raytypes); - OSLShader::register_closures((OSLShadingSystem *)ss_shared); + OSLRenderServices::register_closures(ss_shared); loaded_shaders.clear(); } diff --git a/intern/cycles/scene/osl.h b/intern/cycles/scene/osl.h index bf27069b1b1..76c6bd96ce1 100644 --- a/intern/cycles/scene/osl.h +++ b/intern/cycles/scene/osl.h @@ -66,6 +66,9 @@ class OSLShaderManager : public ShaderManager { return true; } + uint64_t get_attribute_id(ustring name) override; + uint64_t get_attribute_id(AttributeStandard std) override; + void device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index bd647ab55e7..96a8f40bbad 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -414,7 +414,7 @@ ShaderManager *ShaderManager::create(int shadingsystem) return manager; } -uint ShaderManager::get_attribute_id(ustring name) +uint64_t ShaderManager::get_attribute_id(ustring name) { thread_scoped_spin_lock lock(attribute_lock_); @@ -424,14 +424,14 @@ uint ShaderManager::get_attribute_id(ustring name) if (it != unique_attribute_id.end()) return it->second; - uint id = (uint)ATTR_STD_NUM + unique_attribute_id.size(); + uint64_t id = ATTR_STD_NUM + unique_attribute_id.size(); unique_attribute_id[name] = id; return id; } -uint ShaderManager::get_attribute_id(AttributeStandard std) +uint64_t ShaderManager::get_attribute_id(AttributeStandard std) { - return (uint)std; + return (uint64_t)std; } int ShaderManager::get_shader_id(Shader *shader, bool smooth) diff --git a/intern/cycles/scene/shader.h b/intern/cycles/scene/shader.h index 274bb9b4fa1..2670776aca4 100644 --- a/intern/cycles/scene/shader.h +++ b/intern/cycles/scene/shader.h @@ -192,8 +192,8 @@ class ShaderManager { void device_free_common(Device *device, DeviceScene *dscene, Scene *scene); /* get globally unique id for a type of attribute */ - uint get_attribute_id(ustring name); - uint get_attribute_id(AttributeStandard std); + virtual uint64_t get_attribute_id(ustring name); + virtual uint64_t get_attribute_id(AttributeStandard std); /* get shader id for mesh faces */ int get_shader_id(Shader *shader, bool smooth = false); @@ -223,7 +223,7 @@ class ShaderManager { uint32_t update_flags; - typedef unordered_map<ustring, uint, ustringHash> AttributeIDMap; + typedef unordered_map<ustring, uint64_t, ustringHash> AttributeIDMap; AttributeIDMap unique_attribute_id; static thread_mutex lookup_table_mutex; diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index 267d19dcecb..6a7eb25925a 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -123,27 +123,44 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { void initFromGhostContext(GHOST_Context &ghost_ctx) override { #if defined(WITH_GHOST_X11) || defined(WITH_GHOST_WAYLAND) - if (dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx)) { + /* WAYLAND/X11 may be dynamically selected at load time but both may also be + * supported at compile time individually. + * Without `is_ctx_egl` & `is_wayland` preprocessor checks become an unmanageable soup. */ + const bool is_ctx_egl = dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx) != nullptr; + if (is_ctx_egl) { GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx); + const bool is_wayland = ( +# if defined(WITH_GHOST_WAYLAND) + dynamic_cast<const GHOST_SystemWayland *const>(ctx_egl.m_system) != nullptr +# else + false +# endif + ); + if (is_wayland) { # if defined(WITH_GHOST_WAYLAND) - if (dynamic_cast<const GHOST_SystemWayland *const>(ctx_egl.m_system)) { + /* #GHOST_SystemWayland */ oxr_binding.wl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR; oxr_binding.wl.display = (struct wl_display *)ctx_egl.m_nativeDisplay; +# else + GHOST_ASSERT(false, "Unexpected State: logical error, unreachable!"); +# endif /* !WITH_GHOST_WAYLAND */ } - else -# endif + else { /* `!is_wayland` */ # if defined(WITH_GHOST_X11) - { - /* SystemX11. */ + /* #GHOST_SystemX11. */ oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX; oxr_binding.egl.getProcAddress = eglGetProcAddress; oxr_binding.egl.display = ctx_egl.getDisplay(); oxr_binding.egl.config = ctx_egl.getConfig(); oxr_binding.egl.context = ctx_egl.getContext(); +# else + GHOST_ASSERT(false, "Unexpected State: built with only WAYLAND and no System found!"); +# endif /* !WITH_GHOST_X11 */ } } - else { + else { /* `!is_ctx_egl` */ +# if defined(WITH_GHOST_X11) GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx); XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig); @@ -155,7 +172,9 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { oxr_binding.glx.visualid = visual_info->visualid; XFree(visual_info); -# endif +# else + GHOST_ASSERT(false, "Unexpected State: built without X11 and no EGL context is available!"); +# endif /* !WITH_GHOST_X11 */ } #elif defined(WIN32) GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx); @@ -163,7 +182,7 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; oxr_binding.wgl.hDC = ctx_wgl.m_hDC; oxr_binding.wgl.hGLRC = ctx_wgl.m_hGLRC; -#endif +#endif /* WIN32 */ /* Generate a frame-buffer to use for blitting into the texture. */ glGenFramebuffers(1, &m_fbo); diff --git a/intern/libc_compat/CMakeLists.txt b/intern/libc_compat/CMakeLists.txt index c318b3a303b..298d6f49bd9 100644 --- a/intern/libc_compat/CMakeLists.txt +++ b/intern/libc_compat/CMakeLists.txt @@ -18,3 +18,9 @@ set(LIB add_c_flag(-ffast-math) blender_add_lib(bf_intern_libc_compat "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_LIBC_MALLOC_HOOK_WORKAROUND) + target_compile_definitions(bf_intern_libc_compat + PRIVATE WITH_LIBC_MALLOC_HOOK_WORKAROUND + ) +endif() diff --git a/intern/libc_compat/libc_compat.c b/intern/libc_compat/libc_compat.c index 5b969d80501..79efb1c009b 100644 --- a/intern/libc_compat/libc_compat.c +++ b/intern/libc_compat/libc_compat.c @@ -12,6 +12,7 @@ #ifdef __linux__ # include <features.h> # include <math.h> +# include <stdlib.h> # if defined(__GLIBC_PREREQ) # if __GLIBC_PREREQ(2, 31) @@ -114,5 +115,20 @@ float __powf_finite(float x, float y) } # endif /* __GLIBC_PREREQ(2, 31) */ -# endif /* __GLIBC_PREREQ */ -#endif /* __linux__ */ + +# if __GLIBC_PREREQ(2, 34) && defined(WITH_LIBC_MALLOC_HOOK_WORKAROUND) + +extern void *(*__malloc_hook)(size_t __size, const void *); +extern void *(*__realloc_hook)(void *__ptr, size_t __size, const void *); +extern void *(*__memalign_hook)(size_t __alignment, size_t __size, const void *); +extern void (*__free_hook)(void *__ptr, const void *); + +void *(*__malloc_hook)(size_t __size, const void *) = NULL; +void *(*__realloc_hook)(void *__ptr, size_t __size, const void *) = NULL; +void *(*__memalign_hook)(size_t __alignment, size_t __size, const void *) = NULL; +void (*__free_hook)(void *__ptr, const void *) = NULL; + +# endif /* __GLIBC_PREREQ(2, 34) */ + +# endif /* __GLIBC_PREREQ */ +#endif /* __linux__ */ diff --git a/intern/mikktspace/mikk_util.hh b/intern/mikktspace/mikk_util.hh index 224ed647b30..857ca95910b 100644 --- a/intern/mikktspace/mikk_util.hh +++ b/intern/mikktspace/mikk_util.hh @@ -108,7 +108,7 @@ void radixsort(std::vector<T> &data, std::vector<T> &data2, KeyGetter getKey) static_assert(datasize % 2 == 0); static_assert(std::is_integral<key_t>::value); - uint bins[datasize][257] = {0}; + uint bins[datasize][257] = {{0}}; /* Count number of elements per bin. */ for (const T &item : data) { diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 89aaa43cd52..77ab70f8d91 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -318,6 +318,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "glTF 2.0 (.glb/.gltf)", "glTF Binary (.glb)", "glTF Embedded (.gltf)", + "glTF Material Output", "glTF Original PBR data", "glTF Separate (.gltf + .bin + textures)", "invoke() needs to be called before execute()", @@ -368,10 +369,11 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "and AMD Radeon Pro 21.Q4 driver or newer", "and Linux driver version xx.xx.23570 or newer", "and NVIDIA driver version 470 or newer", - "and Windows driver version 101.1660 or newer", + "and Windows driver version 101.3268 or newer", "available with", "brown fox", "can't save image while rendering", + "category", "constructive modifier", "cursor", "custom", @@ -398,6 +400,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "local", "matrices", "no matrices", "multi-res modifier", + "name", "non-triangle face", "normal", "or AMD with macOS 12.3 or newer", @@ -423,6 +426,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "unsupported format", "unsupported image format", "unsupported movie clip format", + "untitled", "vertex data", "verts only", "view", diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index a2fe2dd42ba..a93f1323562 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -750,6 +750,7 @@ class SpellChecker: "unix", "uuid", "vbo", "vbos", + "vfx", "vr", "wxyz", "xr", diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 8170cfccbf9..10925e78ef3 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -60,6 +60,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"), ("bpy.types.lineartgpencilmodifier.use_image_boundary_trimming*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-image-boundary-trimming"), + ("bpy.types.materiallineart.use_intersection_priority_override*", "render/materials/line_art.html#bpy-types-materiallineart-use-intersection-priority-override"), ("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"), ("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"), ("bpy.types.brushcurvessculptsettings.interpolate_point_count*", "sculpt_paint/curves_sculpting/tools/add_curves.html#bpy-types-brushcurvessculptsettings-interpolate-point-count"), @@ -71,6 +72,7 @@ url_manual_mapping = ( ("bpy.types.cyclesrendersettings.preview_denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-prefilter"), ("bpy.types.cyclesrendersettings.preview_scrambling_distance*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-scrambling-distance"), ("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"), + ("bpy.types.objectlineart.use_intersection_priority_override*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-use-intersection-priority-override"), ("bpy.types.brushgpencilsettings.use_stroke_random_strength*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-stroke-random-strength"), ("bpy.types.cyclesrendersettings.film_transparent_roughness*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-roughness"), ("bpy.types.cyclesrendersettings.preview_adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-threshold"), @@ -88,6 +90,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.use_random_press_strength*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-random-press-strength"), ("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"), ("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"), + ("bpy.types.lineartgpencilmodifier.shadow_region_filtering*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-shadow-region-filtering"), ("bpy.types.sequencertimelineoverlay.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-waveform-display-type"), ("bpy.types.view3doverlay.use_normals_constant_screen_size*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-use-normals-constant-screen-size"), ("bpy.types.brushgpencilsettings.random_saturation_factor*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-random-saturation-factor"), @@ -109,6 +112,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.eraser_thickness_factor*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-eraser-thickness-factor"), ("bpy.types.brushgpencilsettings.use_random_press_radius*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-random-press-radius"), ("bpy.types.brushgpencilsettings.use_settings_stabilizer*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-stabilizer"), + ("bpy.types.collection.use_lineart_intersection_priority*", "scene_layout/collections/collections.html#bpy-types-collection-use-lineart-intersection-priority"), ("bpy.types.colormanagedsequencercolorspacesettings.name*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings-name"), ("bpy.types.cyclesrendersettings.max_transparent_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-max-transparent-bounces"), ("bpy.types.cyclesrendersettings.min_transparent_bounces*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-min-transparent-bounces"), @@ -118,6 +122,7 @@ url_manual_mapping = ( ("bpy.types.lineartgpencilmodifier.use_back_face_culling*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-back-face-culling"), ("bpy.types.lineartgpencilmodifier.use_intersection_mask*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-intersection-mask"), ("bpy.types.lineartgpencilmodifier.use_invert_collection*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-invert-collection"), + ("bpy.types.lineartgpencilmodifier.use_invert_silhouette*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-invert-silhouette"), ("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"), ("bpy.types.rendersettings.simplify_gpencil_antialiasing*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-antialiasing"), ("bpy.types.sequencertimelineoverlay.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-duration"), @@ -132,6 +137,8 @@ url_manual_mapping = ( ("bpy.types.cyclesrendersettings.film_transparent_glass*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-glass"), ("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_drag*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-drag"), + ("bpy.types.lineartgpencilmodifier.light_contour_object*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-light-contour-object"), + ("bpy.types.lineartgpencilmodifier.silhouette_filtering*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-silhouette-filtering"), ("bpy.types.lineartgpencilmodifier.use_crease_on_smooth*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-crease-on-smooth"), ("bpy.types.lineartgpencilmodifier.use_face_mark_invert*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark-invert"), ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/view_layer/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), @@ -191,6 +198,8 @@ url_manual_mapping = ( ("bpy.types.freestylelineset.select_material_boundary*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-material-boundary"), ("bpy.types.gpencillayer.annotation_onion_after_color*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-color"), ("bpy.types.gpencillayer.annotation_onion_after_range*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-range"), + ("bpy.types.lineartgpencilmodifier.shadow_camera_near*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-shadow-camera-near"), + ("bpy.types.lineartgpencilmodifier.shadow_camera_size*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-shadow-camera-size"), ("bpy.types.materialgpencilstyle.use_fill_texture_mix*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-texture-mix"), ("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"), ("bpy.types.rendersettings_simplify_gpencil_view_fill*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-fill"), @@ -206,6 +215,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.use_jitter_pressure*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-jitter-pressure"), ("bpy.types.brushgpencilsettings.use_random_press_uv*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-random-press-uv"), ("bpy.types.brushgpencilsettings.use_settings_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-random"), + ("bpy.types.collection.lineart_intersection_priority*", "scene_layout/collections/collections.html#bpy-types-collection-lineart-intersection-priority"), ("bpy.types.collection.lineart_use_intersection_mask*", "scene_layout/collections/collections.html#bpy-types-collection-lineart-use-intersection-mask"), ("bpy.types.colormanagedinputcolorspacesettings.name*", "editors/image/image_settings.html#bpy-types-colormanagedinputcolorspacesettings-name"), ("bpy.types.cyclesrendersettings.denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-prefilter"), @@ -220,7 +230,9 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sys_particle_maximum*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-sys-particle-maximum"), ("bpy.types.fluiddomainsettings.use_bubble_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-bubble-particles"), ("bpy.types.freestylelineset.select_external_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-external-contour"), + ("bpy.types.lineartgpencilmodifier.shadow_camera_far*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-shadow-camera-far"), ("bpy.types.lineartgpencilmodifier.use_custom_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-custom-camera"), + ("bpy.types.lineartgpencilmodifier.use_light_contour*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-light-contour"), ("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/view_layer/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"), ("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-overlap-strokes"), ("bpy.types.movietrackingtrack.use_grayscale_preview*", "movie_clip/tracking/clip/sidebar/track/track.html#bpy-types-movietrackingtrack-use-grayscale-preview"), @@ -256,7 +268,9 @@ url_manual_mapping = ( ("bpy.types.freestylesettings.kr_derivative_epsilon*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-kr-derivative-epsilon"), ("bpy.types.geometrynodecurveprimitivebeziersegment*", "modeling/geometry_nodes/curve_primitives/bezier_segment.html#bpy-types-geometrynodecurveprimitivebeziersegment"), ("bpy.types.geometrynodecurveprimitivequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurveprimitivequadrilateral"), + ("bpy.types.lineartgpencilmodifier.crease_threshold*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-crease-threshold"), ("bpy.types.lineartgpencilmodifier.smooth_tolerance*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-smooth-tolerance"), + ("bpy.types.lineartgpencilmodifier.use_intersection*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-intersection"), ("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"), ("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"), ("bpy.types.materialgpencilstyle.use_stroke_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-stroke-holdout"), @@ -366,6 +380,7 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_transform.html#bpy-types-linestylegeometrymodifier-2dtransform"), ("bpy.types.linestylegeometrymodifier_beziercurve*", "render/freestyle/view_layer/line_style/modifiers/geometry/bezier_curve.html#bpy-types-linestylegeometrymodifier-beziercurve"), ("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"), + ("bpy.types.materiallineart.intersection_priority*", "render/materials/line_art.html#bpy-types-materiallineart-intersection-priority"), ("bpy.types.movietrackingplanetrack.image_opacity*", "movie_clip/tracking/clip/sidebar/track/plane_track.html#bpy-types-movietrackingplanetrack-image-opacity"), ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"), @@ -409,6 +424,7 @@ url_manual_mapping = ( ("bpy.types.greasepencil.curve_edit_corner_angle*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-corner-angle"), ("bpy.types.imageformatsettings.color_management*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-management"), ("bpy.types.lineartgpencilmodifier.source_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-source-camera"), + ("bpy.types.lineartgpencilmodifier.use_edge_mark*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-edge-mark"), ("bpy.types.lineartgpencilmodifier.use_face_mark*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark"), ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/view_layer/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), ("bpy.types.movieclipuser.use_render_undistorted*", "editors/clip/display/clip_display.html#bpy-types-movieclipuser-use-render-undistorted"), @@ -423,8 +439,10 @@ url_manual_mapping = ( ("bpy.types.spaceoutliner.use_filter_object_mesh*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-mesh"), ("bpy.types.spaceoutliner.use_filter_view_layers*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-view-layers"), ("bpy.types.spacesequenceeditor.show_overexposed*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"), + ("bpy.types.toolsettings.snap_face_nearest_steps*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-snap-face-nearest-steps"), ("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"), ("bpy.types.toolsettings.use_snap_align_rotation*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-align-rotation"), + ("bpy.types.toolsettings.use_snap_to_same_target*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-to-same-target"), ("bpy.types.viewlayer.use_pass_cryptomatte_asset*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-asset"), ("bpy.ops.outliner.collection_indirect_only_set*", "render/layers/introduction.html#bpy-ops-outliner-collection-indirect-only-set"), ("bpy.ops.scene.freestyle_geometry_modifier_add*", "render/freestyle/view_layer/line_style/geometry.html#bpy-ops-scene-freestyle-geometry-modifier-add"), @@ -466,10 +484,12 @@ url_manual_mapping = ( ("bpy.types.geometrynodedistributepointsonfaces*", "modeling/geometry_nodes/point/distribute_points_on_faces.html#bpy-types-geometrynodedistributepointsonfaces"), ("bpy.types.geometrynodesetcurvehandlepositions*", "modeling/geometry_nodes/curve/set_handle_positions.html#bpy-types-geometrynodesetcurvehandlepositions"), ("bpy.types.greasepencil.stroke_thickness_space*", "grease_pencil/properties/strokes.html#bpy-types-greasepencil-stroke-thickness-space"), + ("bpy.types.lineartgpencilmodifier.use_material*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-material"), ("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/view_layer/line_style/modifiers/geometry/blueprint.html#bpy-types-linestylegeometrymodifier-blueprint"), ("bpy.types.materialgpencilstyle.alignment_mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-alignment-mode"), ("bpy.types.movietrackingtrack.use_blue_channel*", "movie_clip/tracking/clip/sidebar/track/track.html#bpy-types-movietrackingtrack-use-blue-channel"), ("bpy.types.movietrackingtrack.use_custom_color*", "movie_clip/tracking/clip/sidebar/track/track.html#bpy-types-movietrackingtrack-use-custom-color"), + ("bpy.types.objectlineart.intersection_priority*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-intersection-priority"), ("bpy.types.particlesettings.use_modifier_stack*", "physics/particles/emitter/emission.html#bpy-types-particlesettings-use-modifier-stack"), ("bpy.types.rendersettings.sequencer_gl_preview*", "editors/video_sequencer/preview/sidebar.html#bpy-types-rendersettings-sequencer-gl-preview"), ("bpy.types.rendersettings.simplify_subdivision*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-subdivision"), @@ -531,6 +551,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodeinputshortestedgepaths*", "modeling/geometry_nodes/mesh/shortest_edge_paths.html#bpy-types-geometrynodeinputshortestedgepaths"), ("bpy.types.gpencilsculptguide.reference_point*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-reference-point"), ("bpy.types.greasepencil.edit_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-edit-curve-resolution"), + ("bpy.types.lineartgpencilmodifier.use_contour*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-contour"), ("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_offset.html#bpy-types-linestylegeometrymodifier-2doffset"), ("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/view_layer/line_style/modifiers/geometry/sampling.html#bpy-types-linestylegeometrymodifier-sampling"), ("bpy.types.moviesequence.animation_offset_end*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-moviesequence-animation-offset-end"), @@ -580,6 +601,8 @@ url_manual_mapping = ( ("bpy.types.geometrynodeinputmeshfaceisplanar*", "modeling/geometry_nodes/mesh/face_is_planar.html#bpy-types-geometrynodeinputmeshfaceisplanar"), ("bpy.types.geometrynodeinputsplineresolution*", "modeling/geometry_nodes/curve/spline_resolution.html#bpy-types-geometrynodeinputsplineresolution"), ("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"), + ("bpy.types.lineartgpencilmodifier.use_crease*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-crease"), + ("bpy.types.lineartgpencilmodifier.use_shadow*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-shadow"), ("bpy.types.materialgpencilstyle.stroke_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-stroke-style"), ("bpy.types.materiallineart.use_material_mask*", "render/materials/line_art.html#bpy-types-materiallineart-use-material-mask"), ("bpy.types.movietracking.active_object_index*", "movie_clip/tracking/clip/sidebar/track/objects.html#bpy-types-movietracking-active-object-index"), @@ -607,6 +630,7 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.wireframe_threshold*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-threshold"), ("bpy.types.viewlayer.active_lightgroup_index*", "render/layers/passes.html#bpy-types-viewlayer-active-lightgroup-index"), ("bpy.ops.ed.lib_id_override_editable_toggle*", "editors/outliner/interface.html#bpy-ops-ed-lib-id-override-editable-toggle"), + ("bpy.ops.geometry.color_attribute_duplicate*", "modeling/meshes/properties/object_data.html#bpy-ops-geometry-color-attribute-duplicate"), ("bpy.ops.object.constraint_add_with_targets*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"), ("bpy.ops.object.material_slot_remove_unused*", "scene_layout/object/editing/cleanup.html#bpy-ops-object-material-slot-remove-unused"), ("bpy.ops.outliner.collection_disable_render*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable-render"), @@ -667,6 +691,7 @@ url_manual_mapping = ( ("bpy.types.spacenodeoverlay.show_wire_color*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeoverlay-show-wire-color"), ("bpy.types.spacesequenceeditor.display_mode*", "editors/video_sequencer/preview/display/display_mode.html#bpy-types-spacesequenceeditor-display-mode"), ("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"), + ("bpy.types.toolsettings.use_snap_selectable*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-selectable"), ("bpy.types.view3doverlay.show_fade_inactive*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-fade-inactive"), ("bpy.types.viewlayer.pass_cryptomatte_depth*", "render/layers/passes.html#bpy-types-viewlayer-pass-cryptomatte-depth"), ("bpy.ops.clip.stabilize_2d_rotation_select*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-stabilize-2d-rotation-select"), @@ -850,6 +875,7 @@ url_manual_mapping = ( ("bpy.ops.poselib.restore_previous_action*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-restore-previous-action"), ("bpy.ops.preferences.reset_default_theme*", "editors/preferences/themes.html#bpy-ops-preferences-reset-default-theme"), ("bpy.ops.scene.view_layer_add_lightgroup*", "render/layers/passes.html#bpy-ops-scene-view-layer-add-lightgroup"), + ("bpy.ops.sculpt.dyntopo_detail_size_edit*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-ops-sculpt-dyntopo-detail-size-edit"), ("bpy.ops.sculpt_curves.min_distance_edit*", "sculpt_paint/curves_sculpting/tools/density_curves.html#bpy-ops-sculpt-curves-min-distance-edit"), ("bpy.ops.sequencer.strip_transform_clear*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-strip-transform-clear"), ("bpy.ops.spreadsheet.add_row_filter_rule*", "editors/spreadsheet.html#bpy-ops-spreadsheet-add-row-filter-rule"), @@ -922,6 +948,7 @@ url_manual_mapping = ( ("bpy.types.toolsettings.double_threshold*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-double-threshold"), ("bpy.types.toolsettings.lock_object_mode*", "interface/window_system/topbar.html#bpy-types-toolsettings-lock-object-mode"), ("bpy.types.toolsettings.mesh_select_mode*", "modeling/meshes/selecting/introduction.html#bpy-types-toolsettings-mesh-select-mode"), + ("bpy.types.toolsettings.use_snap_nonedit*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-nonedit"), ("bpy.types.toolsettings.use_snap_project*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-project"), ("bpy.types.transformorientationslot.type*", "editors/3dview/controls/orientation.html#bpy-types-transformorientationslot-type"), ("bpy.types.unitsettings.temperature_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-temperature-unit"), @@ -933,6 +960,8 @@ url_manual_mapping = ( ("bpy.ops.mesh.vertices_smooth_laplacian*", "modeling/meshes/editing/vertex/laplacian_smooth.html#bpy-ops-mesh-vertices-smooth-laplacian"), ("bpy.ops.object.multires_rebuild_subdiv*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-rebuild-subdiv"), ("bpy.ops.outliner.liboverride_operation*", "files/linked_libraries/library_overrides.html#bpy-ops-outliner-liboverride-operation"), + ("bpy.ops.screen.space_type_set_or_cycle*", "editors/index.html#bpy-ops-screen-space-type-set-or-cycle"), + ("bpy.ops.sculpt.dynamic_topology_toggle*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-ops-sculpt-dynamic-topology-toggle"), ("bpy.ops.sequencer.select_side_of_frame*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-side-of-frame"), ("bpy.types.animvizmotionpaths.frame_end*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-frame-end"), ("bpy.types.armature.rigify_colors_index*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-colors-index"), @@ -1003,11 +1032,13 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.display_handle*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-display-handle"), ("bpy.types.volumedisplay.wireframe_type*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-type"), ("bpy.ops.anim.channels_editable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-editable-toggle"), + ("bpy.ops.anim.channels_setting_disable*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-setting-disable"), ("bpy.ops.curve.normals_make_consistent*", "modeling/curves/editing/control_points.html#bpy-ops-curve-normals-make-consistent"), ("bpy.ops.curves.snap_curves_to_surface*", "sculpt_paint/curves_sculpting/introduction.html#bpy-ops-curves-snap-curves-to-surface"), ("bpy.ops.ed.lib_id_load_custom_preview*", "editors/asset_browser.html#bpy-ops-ed-lib-id-load-custom-preview"), ("bpy.ops.gpencil.frame_clean_duplicate*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-duplicate"), ("bpy.ops.gpencil.stroke_simplify_fixed*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify-fixed"), + ("bpy.ops.mask.add_feather_vertex_slide*", "movie_clip/masking/editing.html#bpy-ops-mask-add-feather-vertex-slide"), ("bpy.ops.mesh.faces_select_linked_flat*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-faces-select-linked-flat"), ("bpy.ops.mesh.primitive_ico_sphere_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-ico-sphere-add"), ("bpy.ops.object.clear_override_library*", "files/linked_libraries/library_overrides.html#bpy-ops-object-clear-override-library"), @@ -1023,6 +1054,7 @@ url_manual_mapping = ( ("bpy.ops.sequencer.change_effect_input*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-effect-input"), ("bpy.ops.sequencer.strip_color_tag_set*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-ops-sequencer-strip-color-tag-set"), ("bpy.ops.sequencer.strip_transform_fit*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-strip-transform-fit"), + ("bpy.ops.wm.doc_view_manual_ui_context*", "getting_started/help.html#bpy-ops-wm-doc-view-manual-ui-context"), ("bpy.types.armature.rigify_colors_lock*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-colors-lock"), ("bpy.types.bakesettings.cage_extrusion*", "render/cycles/baking.html#bpy-types-bakesettings-cage-extrusion"), ("bpy.types.bakesettings.use_pass_color*", "render/cycles/baking.html#bpy-types-bakesettings-use-pass-color"), @@ -1098,12 +1130,13 @@ url_manual_mapping = ( ("bpy.types.toolsettings.use_snap_scale*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-scale"), ("bpy.types.toolsettings.uv_select_mode*", "editors/uv/selecting.html#bpy-types-toolsettings-uv-select-mode"), ("bpy.types.viewlayer.material_override*", "render/layers/introduction.html#bpy-types-viewlayer-material-override"), - ("bpy.ops.anim.channels_disable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-disable-toggle"), ("bpy.ops.anim.channels_fcurves_enable*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-fcurves-enable"), + ("bpy.ops.anim.channels_setting_enable*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-setting-enable"), ("bpy.ops.anim.channels_setting_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-setting-toggle"), ("bpy.ops.clip.set_viewport_background*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-viewport-background"), ("bpy.ops.geometry.color_attribute_add*", "modeling/meshes/properties/object_data.html#bpy-ops-geometry-color-attribute-add"), ("bpy.ops.gpencil.interpolate_sequence*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-interpolate-sequence"), + ("bpy.ops.mask.normals_make_consistent*", "movie_clip/masking/editing.html#bpy-ops-mask-normals-make-consistent"), ("bpy.ops.mesh.normals_make_consistent*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-normals-make-consistent"), ("bpy.ops.mesh.offset_edge_loops_slide*", "modeling/meshes/editing/edge/offset_edge_slide.html#bpy-ops-mesh-offset-edge-loops-slide"), ("bpy.ops.mesh.primitive_uv_sphere_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-uv-sphere-add"), @@ -1178,10 +1211,10 @@ url_manual_mapping = ( ("bpy.types.spaceuveditor.show_stretch*", "editors/uv/overlays.html#bpy-types-spaceuveditor-show-stretch"), ("bpy.types.toolsettings.keyframe_type*", "editors/timeline.html#bpy-types-toolsettings-keyframe-type"), ("bpy.types.toolsettings.snap_elements*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-snap-elements"), + ("bpy.types.toolsettings.use_snap_edit*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-edit"), ("bpy.types.toolsettings.use_snap_node*", "interface/controls/nodes/arranging.html#bpy-types-toolsettings-use-snap-node"), ("bpy.types.toolsettings.use_snap_self*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-self"), ("bpy.types.viewlayer.active_aov_index*", "render/layers/passes.html#bpy-types-viewlayer-active-aov-index"), - ("bpy.ops.anim.channels_enable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-enable-toggle"), ("bpy.ops.clip.tracking_object_remove*", "movie_clip/tracking/clip/sidebar/track/objects.html#bpy-ops-clip-tracking-object-remove"), ("bpy.ops.constraint.copy_to_selected*", "animation/constraints/interface/header.html#bpy-ops-constraint-copy-to-selected"), ("bpy.ops.gpencil.bake_mesh_animation*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-bake-mesh-animation"), @@ -1191,6 +1224,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.stroke_cyclical_set*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-cyclical-set"), ("bpy.ops.gpencil.vertex_color_invert*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-invert"), ("bpy.ops.gpencil.vertex_color_levels*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-levels"), + ("bpy.ops.mask.slide_spline_curvature*", "movie_clip/masking/editing.html#bpy-ops-mask-slide-spline-curvature"), ("bpy.ops.mesh.primitive_cylinder_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-cylinder-add"), ("bpy.ops.mesh.set_normals_from_faces*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-set-normals-from-faces"), ("bpy.ops.mesh.shape_propagate_to_all*", "modeling/meshes/editing/vertex/propagate_shapes.html#bpy-ops-mesh-shape-propagate-to-all"), @@ -1320,10 +1354,12 @@ url_manual_mapping = ( ("bpy.ops.palette.extract_from_image*", "editors/image/editing.html#bpy-ops-palette-extract-from-image"), ("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"), ("bpy.ops.poselib.browse_interactive*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-browse-interactive"), + ("bpy.ops.screen.region_context_menu*", "interface/controls/buttons/menus.html#bpy-ops-screen-region-context-menu"), ("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"), ("bpy.ops.sequencer.crossfade_sounds*", "video_editing/edit/montage/strips/transitions/sound_crossfade.html#bpy-ops-sequencer-crossfade-sounds"), ("bpy.ops.sequencer.export_subtitles*", "editors/video_sequencer/preview/header.html#bpy-ops-sequencer-export-subtitles"), ("bpy.ops.transform.edge_bevelweight*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-bevelweight"), + ("bpy.ops.view3d.clear_render_border*", "editors/3dview/navigate/regions.html#bpy-ops-view3d-clear-render-border"), ("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"), ("bpy.types.animvizmotionpaths.range*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-range"), ("bpy.types.assetmetadata.active_tag*", "editors/asset_browser.html#bpy-types-assetmetadata-active-tag"), @@ -1407,6 +1443,8 @@ url_manual_mapping = ( ("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"), ("bpy.ops.action.markers_make_local*", "animation/markers.html#bpy-ops-action-markers-make-local"), ("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"), + ("bpy.ops.anim.driver_button_remove*", "animation/drivers/usage.html#bpy-ops-anim-driver-button-remove"), + ("bpy.ops.anim.keyingset_button_add*", "animation/keyframes/keying_sets.html#bpy-ops-anim-keyingset-button-add"), ("bpy.ops.armature.select_hierarchy*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-hierarchy"), ("bpy.ops.armature.switch_direction*", "animation/armatures/bones/editing/switch_direction.html#bpy-ops-armature-switch-direction"), ("bpy.ops.clip.apply_solution_scale*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-apply-solution-scale"), @@ -1416,13 +1454,17 @@ url_manual_mapping = ( ("bpy.ops.font.text_paste_from_file*", "modeling/texts/editing.html#bpy-ops-font-text-paste-from-file"), ("bpy.ops.geometry.attribute_remove*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-geometry-attribute-remove"), ("bpy.ops.gpencil.frame_clean_loose*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-loose"), + ("bpy.ops.gpencil.selectmode_toggle*", "grease_pencil/selecting.html#bpy-ops-gpencil-selectmode-toggle"), + ("bpy.ops.mask.feather_weight_clear*", "movie_clip/masking/editing.html#bpy-ops-mask-feather-weight-clear"), ("bpy.ops.mask.primitive_circle_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-circle-add"), ("bpy.ops.mask.primitive_square_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-square-add"), + ("bpy.ops.mesh.dupli_extrude_cursor*", "modeling/meshes/tools/extrude_cursor.html#bpy-ops-mesh-dupli-extrude-cursor"), ("bpy.ops.mesh.primitive_circle_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-circle-add"), ("bpy.ops.mesh.primitive_monkey_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-monkey-add"), ("bpy.ops.mesh.select_face_by_sides*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-face-by-sides"), ("bpy.ops.mesh.shortest_path_select*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-shortest-path-select"), ("bpy.ops.mesh.vert_connect_concave*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-vert-connect-concave"), + ("bpy.ops.nla.duplicate_linked_move*", "editors/nla/editing.html#bpy-ops-nla-duplicate-linked-move"), ("bpy.ops.object.multires_subdivide*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-subdivide"), ("bpy.ops.object.shape_key_transfer*", "animation/shape_keys/shape_keys_panel.html#bpy-ops-object-shape-key-transfer"), ("bpy.ops.object.vertex_group_clean*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-clean"), @@ -1432,11 +1474,13 @@ url_manual_mapping = ( ("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-set-pivot-position"), ("bpy.ops.sculpt_curves.select_grow*", "sculpt_paint/curves_sculpting/introduction.html#bpy-ops-sculpt-curves-select-grow"), ("bpy.ops.sequencer.image_strip_add*", "video_editing/edit/montage/strips/image.html#bpy-ops-sequencer-image-strip-add"), + ("bpy.ops.sequencer.images_separate*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-images-separate"), ("bpy.ops.sequencer.movie_strip_add*", "video_editing/edit/montage/strips/movie.html#bpy-ops-sequencer-movie-strip-add"), ("bpy.ops.sequencer.reassign_inputs*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-reassign-inputs"), ("bpy.ops.sequencer.sound_strip_add*", "video_editing/edit/montage/strips/sound.html#bpy-ops-sequencer-sound-strip-add"), ("bpy.ops.ui.remove_override_button*", "files/linked_libraries/library_overrides.html#bpy-ops-ui-remove-override-button"), ("bpy.ops.view3d.view_center_camera*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-view-center-camera"), + ("bpy.ops.view3d.zoom_camera_1_to_1*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-zoom-camera-1-to-1"), ("bpy.types.animvizmotionpaths.type*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-type"), ("bpy.types.armaturegpencilmodifier*", "grease_pencil/modifiers/deform/armature.html#bpy-types-armaturegpencilmodifier"), ("bpy.types.brush.cloth_deform_type*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-deform-type"), @@ -1528,11 +1572,11 @@ url_manual_mapping = ( ("bpy.types.worldmistsettings.depth*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-depth"), ("bpy.types.worldmistsettings.start*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-start"), ("bpy.ops.armature.armature_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-armature-layers"), - ("bpy.ops.armature.select_linked()*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-linked"), ("bpy.ops.clip.stabilize_2d_select*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-stabilize-2d-select"), ("bpy.ops.clip.tracking_object_new*", "movie_clip/tracking/clip/sidebar/track/objects.html#bpy-ops-clip-tracking-object-new"), ("bpy.ops.constraint.move_to_index*", "animation/constraints/interface/header.html#bpy-ops-constraint-move-to-index"), ("bpy.ops.gpencil.frame_clean_fill*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-fill"), + ("bpy.ops.gpencil.select_alternate*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-alternate"), ("bpy.ops.gpencil.stroke_subdivide*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-subdivide"), ("bpy.ops.gpencil.vertex_color_hsv*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-hsv"), ("bpy.ops.gpencil.vertex_color_set*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-set"), @@ -1564,6 +1608,7 @@ url_manual_mapping = ( ("bpy.ops.sequencer.duplicate_move*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-duplicate-move"), ("bpy.ops.sequencer.select_grouped*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-grouped"), ("bpy.ops.sequencer.select_handles*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-handles"), + ("bpy.ops.ui.copy_data_path_button*", "interface/controls/buttons/menus.html#bpy-ops-ui-copy-data-path-button"), ("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"), ("bpy.types.action.use_frame_range*", "animation/actions.html#bpy-types-action-use-frame-range"), ("bpy.types.armature.axes_position*", "animation/armatures/properties/display.html#bpy-types-armature-axes-position"), @@ -1663,6 +1708,7 @@ url_manual_mapping = ( ("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"), ("bpy.ops.armature.calculate_roll*", "animation/armatures/bones/editing/bone_roll.html#bpy-ops-armature-calculate-roll"), ("bpy.ops.armature.duplicate_move*", "animation/armatures/bones/editing/duplicate.html#bpy-ops-armature-duplicate-move"), + ("bpy.ops.armature.extrude_forked*", "animation/armatures/bones/editing/extrude.html#bpy-ops-armature-extrude-forked"), ("bpy.ops.armature.select_similar*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-similar"), ("bpy.ops.clip.create_plane_track*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-create-plane-track"), ("bpy.ops.curve.spline_weight_set*", "modeling/curves/editing/other.html#bpy-ops-curve-spline-weight-set"), @@ -1685,6 +1731,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.primitive_cone_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-cone-add"), ("bpy.ops.mesh.primitive_cube_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-cube-add"), ("bpy.ops.mesh.primitive_grid_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-grid-add"), + ("bpy.ops.mesh.shortest_path_pick*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-shortest-path-pick"), ("bpy.ops.mesh.subdivide_edgering*", "modeling/meshes/editing/edge/subdivide_edge_ring.html#bpy-ops-mesh-subdivide-edgering"), ("bpy.ops.node.hide_socket_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-socket-toggle"), ("bpy.ops.node.tree_socket_remove*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-remove"), @@ -1705,7 +1752,6 @@ url_manual_mapping = ( ("bpy.ops.outliner.show_one_level*", "editors/outliner/editing.html#bpy-ops-outliner-show-one-level"), ("bpy.ops.paint.brush_colors_flip*", "sculpt_paint/texture_paint/tool_settings/brush_settings.html#bpy-ops-paint-brush-colors-flip"), ("bpy.ops.paint.weight_from_bones*", "sculpt_paint/weight_paint/editing.html#bpy-ops-paint-weight-from-bones"), - ("bpy.ops.pose.blend_to_neighbour*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-blend-to-neighbour"), ("bpy.ops.pose.paths_range_update*", "animation/motion_paths.html#bpy-ops-pose-paths-range-update"), ("bpy.ops.poselib.action_sanitize*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-action-sanitize"), ("bpy.ops.preferences.studiolight*", "editors/preferences/lights.html#bpy-ops-preferences-studiolight"), @@ -1718,9 +1764,11 @@ url_manual_mapping = ( ("bpy.ops.transform.vertex_random*", "modeling/meshes/editing/mesh/transform/randomize.html#bpy-ops-transform-vertex-random"), ("bpy.ops.ui.reset_default_button*", "interface/controls/buttons/menus.html#bpy-ops-ui-reset-default-button"), ("bpy.ops.uv.shortest_path_select*", "editors/uv/selecting.html#bpy-ops-uv-shortest-path-select"), + ("bpy.ops.view3d.object_as_camera*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-object-as-camera"), ("bpy.ops.wm.operator_cheat_sheet*", "advanced/operators.html#bpy-ops-wm-operator-cheat-sheet"), ("bpy.ops.wm.previews_batch_clear*", "files/blend/previews.html#bpy-ops-wm-previews-batch-clear"), ("bpy.ops.wm.recover_last_session*", "files/blend/open_save.html#bpy-ops-wm-recover-last-session"), + ("bpy.ops.wm.toolbar_fallback_pie*", "interface/tool_system.html#bpy-ops-wm-toolbar-fallback-pie"), ("bpy.types.armature.display_type*", "animation/armatures/properties/display.html#bpy-types-armature-display-type"), ("bpy.types.armature.use_mirror_x*", "animation/armatures/bones/tools/tool_settings.html#bpy-types-armature-use-mirror-x"), ("bpy.types.bakesettings.normal_b*", "render/cycles/baking.html#bpy-types-bakesettings-normal-b"), @@ -1805,13 +1853,19 @@ url_manual_mapping = ( ("bpy.types.volumerender.clipping*", "modeling/volumes/properties.html#bpy-types-volumerender-clipping"), ("bpy.types.workspace.object_mode*", "interface/window_system/workspaces.html#bpy-types-workspace-object-mode"), ("bpy.ops.anim.channels_collapse*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-collapse"), + ("bpy.ops.anim.driver_button_add*", "animation/drivers/usage.html#bpy-ops-anim-driver-button-add"), + ("bpy.ops.armature.click_extrude*", "animation/armatures/bones/editing/extrude.html#bpy-ops-armature-click-extrude"), + ("bpy.ops.armature.select_linked*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-linked"), ("bpy.ops.armature.select_mirror*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-mirror"), ("bpy.ops.curve.switch_direction*", "modeling/curves/editing/segments.html#bpy-ops-curve-switch-direction"), ("bpy.ops.geometry.attribute_add*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-geometry-attribute-add"), ("bpy.ops.gpencil.duplicate_move*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-duplicate-move"), + ("bpy.ops.gpencil.select_grouped*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-grouped"), ("bpy.ops.gpencil.stroke_arrange*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-arrange"), ("bpy.ops.graph.blend_to_default*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-blend-to-default"), ("bpy.ops.graph.equalize_handles*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-equalize-handles"), + ("bpy.ops.mball.delete_metaelems*", "modeling/metas/editing.html#bpy-ops-mball-delete-metaelems"), + ("bpy.ops.mball.reveal_metaelems*", "modeling/metas/properties.html#bpy-ops-mball-reveal-metaelems"), ("bpy.ops.mesh.bridge-edge-loops*", "modeling/meshes/editing/edge/bridge_edge_loops.html#bpy-ops-mesh-bridge-edge-loops"), ("bpy.ops.mesh.intersect_boolean*", "modeling/meshes/editing/face/intersect_boolean.html#bpy-ops-mesh-intersect-boolean"), ("bpy.ops.mesh.loop_multi_select*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-loop-multi-select"), @@ -1825,12 +1879,15 @@ url_manual_mapping = ( ("bpy.ops.outliner.lib_operation*", "files/linked_libraries/link_append.html#bpy-ops-outliner-lib-operation"), ("bpy.ops.outliner.orphans_purge*", "editors/outliner/interface.html#bpy-ops-outliner-orphans-purge"), ("bpy.ops.paint.mask_box_gesture*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-box-gesture"), + ("bpy.ops.pose.blend_to_neighbor*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-blend-to-neighbor"), ("bpy.ops.screen.region_quadview*", "editors/3dview/navigate/views.html#bpy-ops-screen-region-quadview"), ("bpy.ops.screen.screenshot_area*", "interface/window_system/topbar.html#bpy-ops-screen-screenshot-area"), + ("bpy.ops.screen.workspace_cycle*", "interface/window_system/workspaces.html#bpy-ops-screen-workspace-cycle"), ("bpy.ops.sequencer.change_scene*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-scene"), ("bpy.ops.sequencer.offset_clear*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-offset-clear"), ("bpy.ops.spreadsheet.toggle_pin*", "editors/spreadsheet.html#bpy-ops-spreadsheet-toggle-pin"), ("bpy.ops.uv.follow_active_quads*", "modeling/meshes/editing/uv.html#bpy-ops-uv-follow-active-quads"), + ("bpy.ops.view3d.view_persportho*", "editors/3dview/navigate/projections.html#bpy-ops-view3d-view-persportho"), ("bpy.types.arraygpencilmodifier*", "grease_pencil/modifiers/generate/array.html#bpy-types-arraygpencilmodifier"), ("bpy.types.assetmetadata.author*", "editors/asset_browser.html#bpy-types-assetmetadata-author"), ("bpy.types.bone.envelope_weight*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-envelope-weight"), @@ -1929,16 +1986,24 @@ url_manual_mapping = ( ("bpy.ops.file.unpack_libraries*", "files/blend/packed_data.html#bpy-ops-file-unpack-libraries"), ("bpy.ops.gpencil.layer_isolate*", "grease_pencil/properties/layers.html#bpy-ops-gpencil-layer-isolate"), ("bpy.ops.gpencil.move_to_layer*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-move-to-layer"), + ("bpy.ops.gpencil.select_linked*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-linked"), + ("bpy.ops.gpencil.select_random*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-random"), ("bpy.ops.gpencil.stroke_sample*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-sample"), ("bpy.ops.gpencil.stroke_smooth*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-smooth"), ("bpy.ops.graph.keyframe_insert*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-keyframe-insert"), ("bpy.ops.image.read_viewlayers*", "editors/image/editing.html#bpy-ops-image-read-viewlayers"), + ("bpy.ops.mask.add_vertex_slide*", "movie_clip/masking/editing.html#bpy-ops-mask-add-vertex-slide"), + ("bpy.ops.mask.shape_key_insert*", "movie_clip/masking/editing.html#bpy-ops-mask-shape-key-insert"), + ("bpy.ops.mask.switch_direction*", "movie_clip/masking/editing.html#bpy-ops-mask-switch-direction"), ("bpy.ops.mesh.blend_from_shape*", "modeling/meshes/editing/vertex/blend_shape.html#bpy-ops-mesh-blend-from-shape"), ("bpy.ops.mesh.dissolve_limited*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-limited"), ("bpy.ops.mesh.face_make_planar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-face-make-planar"), ("bpy.ops.mesh.face_set_extract*", "sculpt_paint/sculpting/editing/face_sets.html#bpy-ops-mesh-face-set-extract"), ("bpy.ops.mesh.faces_shade_flat*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-flat"), ("bpy.ops.mesh.paint_mask_slice*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-paint-mask-slice"), + ("bpy.ops.mesh.select_edge_ring*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-select-edge-ring"), + ("bpy.ops.mesh.select_next_item*", "modeling/meshes/selecting/more_less.html#bpy-ops-mesh-select-next-item"), + ("bpy.ops.mesh.select_prev_item*", "modeling/meshes/selecting/more_less.html#bpy-ops-mesh-select-prev-item"), ("bpy.ops.mesh.select_ungrouped*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-ungrouped"), ("bpy.ops.node.delete_reconnect*", "interface/controls/nodes/editing.html#bpy-ops-node-delete-reconnect"), ("bpy.ops.node.tree_path_parent*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-path-parent"), @@ -1971,6 +2036,8 @@ url_manual_mapping = ( ("bpy.ops.transform.edge_crease*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-crease"), ("bpy.ops.transform.skin_resize*", "modeling/meshes/editing/mesh/transform/skin_resize.html#bpy-ops-transform-skin-resize"), ("bpy.ops.uv.seams_from_islands*", "modeling/meshes/uv/editing.html#bpy-ops-uv-seams-from-islands"), + ("bpy.ops.uv.shortest_path_pick*", "editors/uv/selecting.html#bpy-ops-uv-shortest-path-pick"), + ("bpy.ops.view3d.camera_to_view*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-camera-to-view"), ("bpy.types.bakesettings.margin*", "render/cycles/baking.html#bpy-types-bakesettings-margin"), ("bpy.types.bakesettings.target*", "render/cycles/baking.html#bpy-types-bakesettings-target"), ("bpy.types.brush.cloth_damping*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-damping"), @@ -2053,6 +2120,9 @@ url_manual_mapping = ( ("bpy.ops.armature.select_less*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-less"), ("bpy.ops.armature.select_more*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-more"), ("bpy.ops.asset.bundle_install*", "editors/asset_browser.html#bpy-ops-asset-bundle-install"), + ("bpy.ops.buttons.clear_filter*", "interface/controls/buttons/fields.html#bpy-ops-buttons-clear-filter"), + ("bpy.ops.buttons.context_menu*", "interface/controls/buttons/menus.html#bpy-ops-buttons-context-menu"), + ("bpy.ops.buttons.start_filter*", "interface/controls/buttons/fields.html#bpy-ops-buttons-start-filter"), ("bpy.ops.clip.add_marker_move*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-add-marker-move"), ("bpy.ops.clip.bundles_to_mesh*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-bundles-to-mesh"), ("bpy.ops.clip.detect_features*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-detect-features"), @@ -2063,9 +2133,14 @@ url_manual_mapping = ( ("bpy.ops.fluid.bake_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-ops-fluid-bake-particles"), ("bpy.ops.fluid.free_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-ops-fluid-free-particles"), ("bpy.ops.gpencil.extrude_move*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-extrude-move"), + ("bpy.ops.gpencil.select_first*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-first"), ("bpy.ops.gpencil.stroke_merge*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-merge"), ("bpy.ops.gpencil.stroke_split*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-split"), ("bpy.ops.graph.duplicate_move*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-duplicate-move"), + ("bpy.ops.mask.handle_type_set*", "movie_clip/masking/editing.html#bpy-ops-mask-handle-type-set"), + ("bpy.ops.mask.hide_view_clear*", "movie_clip/masking/editing.html#bpy-ops-mask-hide-view-clear"), + ("bpy.ops.mask.shape_key_clear*", "movie_clip/masking/editing.html#bpy-ops-mask-shape-key-clear"), + ("bpy.ops.mball.hide_metaelems*", "modeling/metas/properties.html#bpy-ops-mball-hide-metaelems"), ("bpy.ops.mesh.average_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-average-normals"), ("bpy.ops.mesh.delete_edgeloop*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete-edgeloop"), ("bpy.ops.mesh.vertices_smooth*", "modeling/meshes/editing/vertex/smooth_vertices.html#bpy-ops-mesh-vertices-smooth"), @@ -2098,7 +2173,9 @@ url_manual_mapping = ( ("bpy.ops.sound.bake_animation*", "scene_layout/scene/properties.html#bpy-ops-sound-bake-animation"), ("bpy.ops.transform.edge_slide*", "modeling/meshes/editing/edge/edge_slide.html#bpy-ops-transform-edge-slide"), ("bpy.ops.transform.vert_slide*", "modeling/meshes/editing/vertex/slide_vertices.html#bpy-ops-transform-vert-slide"), + ("bpy.ops.ui.list_start_filter*", "interface/controls/templates/list_view.html#bpy-ops-ui-list-start-filter"), ("bpy.ops.uv.project_from_view*", "modeling/meshes/editing/uv.html#bpy-ops-uv-project-from-view"), + ("bpy.ops.view3d.render_border*", "editors/3dview/navigate/regions.html#bpy-ops-view3d-render-border"), ("bpy.ops.view3d.view_selected*", "editors/3dview/navigate/navigation.html#bpy-ops-view3d-view-selected"), ("bpy.ops.wm.memory_statistics*", "advanced/operators.html#bpy-ops-wm-memory-statistics"), ("bpy.ops.wm.recover_auto_save*", "files/blend/open_save.html#bpy-ops-wm-recover-auto-save"), @@ -2185,11 +2262,16 @@ url_manual_mapping = ( ("bpy.ops.curve.smooth_weight*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-weight"), ("bpy.ops.file.pack_libraries*", "files/blend/packed_data.html#bpy-ops-file-pack-libraries"), ("bpy.ops.font.change_spacing*", "modeling/texts/editing.html#bpy-ops-font-change-spacing"), + ("bpy.ops.gpencil.interpolate*", "grease_pencil/modes/draw/tools/interpolate.html#bpy-ops-gpencil-interpolate"), ("bpy.ops.gpencil.layer_merge*", "grease_pencil/properties/layers.html#bpy-ops-gpencil-layer-merge"), + ("bpy.ops.gpencil.select_last*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-last"), + ("bpy.ops.gpencil.select_less*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-less"), + ("bpy.ops.gpencil.select_more*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-more"), ("bpy.ops.gpencil.stroke_flip*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-flip"), ("bpy.ops.gpencil.stroke_join*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-join"), ("bpy.ops.gpencil.stroke_trim*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-trim"), ("bpy.ops.gpencil.trace_image*", "grease_pencil/modes/object/trace_image.html#bpy-ops-gpencil-trace-image"), + ("bpy.ops.graph.fmodifier_add*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-fmodifier-add"), ("bpy.ops.image.external_edit*", "editors/image/editing.html#bpy-ops-image-external-edit"), ("bpy.ops.mesh.colors_reverse*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-colors-reverse"), ("bpy.ops.mesh.dissolve_edges*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-edges"), @@ -2221,6 +2303,9 @@ url_manual_mapping = ( ("bpy.ops.poselib.pose_remove*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-remove"), ("bpy.ops.poselib.pose_rename*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-rename"), ("bpy.ops.preferences.keyitem*", "editors/preferences/keymap.html#bpy-ops-preferences-keyitem"), + ("bpy.ops.screen.area_options*", "interface/window_system/areas.html#bpy-ops-screen-area-options"), + ("bpy.ops.screen.region_blend*", "interface/window_system/regions.html#bpy-ops-screen-region-blend"), + ("bpy.ops.screen.region_scale*", "interface/window_system/regions.html#bpy-ops-screen-region-scale"), ("bpy.ops.sequencer.swap_data*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-swap-data"), ("bpy.ops.transform.push_pull*", "modeling/meshes/editing/mesh/transform/push_pull.html#bpy-ops-transform-push-pull"), ("bpy.ops.transform.seq_slide*", "video_editing/edit/montage/editing.html#bpy-ops-transform-seq-slide"), @@ -2299,6 +2384,7 @@ url_manual_mapping = ( ("bpy.ops.anim.channels_move*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-move"), ("bpy.ops.armature.subdivide*", "animation/armatures/bones/editing/subdivide.html#bpy-ops-armature-subdivide"), ("bpy.ops.buttons.toggle_pin*", "editors/properties_editor.html#bpy-ops-buttons-toggle-pin"), + ("bpy.ops.clip.delete_marker*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-delete-marker"), ("bpy.ops.clip.filter_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-filter-tracks"), ("bpy.ops.clip.select_circle*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-circle"), ("bpy.ops.clip.track_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-track-markers"), @@ -2307,6 +2393,9 @@ url_manual_mapping = ( ("bpy.ops.file.directory_new*", "editors/file_browser.html#bpy-ops-file-directory-new"), ("bpy.ops.graph.euler_filter*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-euler-filter"), ("bpy.ops.marker.camera_bind*", "animation/markers.html#bpy-ops-marker-camera-bind"), + ("bpy.ops.mask.cyclic_toggle*", "movie_clip/masking/editing.html#bpy-ops-mask-cyclic-toggle"), + ("bpy.ops.mask.hide_view_set*", "movie_clip/masking/editing.html#bpy-ops-mask-hide-view-set"), + ("bpy.ops.mask.paste_splines*", "movie_clip/masking/editing.html#bpy-ops-mask-paste-splines"), ("bpy.ops.mask.select_circle*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-circle"), ("bpy.ops.mask.select_linked*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-linked"), ("bpy.ops.mesh.beautify_fill*", "modeling/meshes/editing/face/beautify_faces.html#bpy-ops-mesh-beautify-fill"), @@ -2321,6 +2410,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.point_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-point-normals"), ("bpy.ops.mesh.rip_edge_move*", "modeling/meshes/editing/vertex/rip_vertices_extend.html#bpy-ops-mesh-rip-edge-move"), ("bpy.ops.mesh.select_linked*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-select-linked"), + ("bpy.ops.mesh.select_mirror*", "modeling/meshes/selecting/mirror.html#bpy-ops-mesh-select-mirror"), ("bpy.ops.mesh.select_random*", "modeling/meshes/selecting/random.html#bpy-ops-mesh-select-random"), ("bpy.ops.mesh.sort_elements*", "modeling/meshes/editing/mesh/sort_elements.html#bpy-ops-mesh-sort-elements"), ("bpy.ops.mesh.split_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-split-normals"), @@ -2342,6 +2432,7 @@ url_manual_mapping = ( ("bpy.ops.transform.tosphere*", "modeling/meshes/editing/mesh/transform/to_sphere.html#bpy-ops-transform-tosphere"), ("bpy.ops.view3d.clip_border*", "editors/3dview/navigate/regions.html#bpy-ops-view3d-clip-border"), ("bpy.ops.view3d.toggle_xray*", "modeling/meshes/selecting/introduction.html#bpy-ops-view3d-toggle-xray"), + ("bpy.ops.view3d.view_camera*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-view-camera"), ("bpy.ops.view3d.zoom_border*", "editors/3dview/navigate/navigation.html#bpy-ops-view3d-zoom-border"), ("bpy.ops.wm.previews_ensure*", "files/blend/previews.html#bpy-ops-wm-previews-ensure"), ("bpy.ops.wm.properties_edit*", "files/data_blocks.html#bpy-ops-wm-properties-edit"), @@ -2432,6 +2523,7 @@ url_manual_mapping = ( ("bpy.ops.graph.easing_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-easing-type"), ("bpy.ops.graph.handle_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-handle-type"), ("bpy.ops.marker.select_all*", "animation/markers.html#bpy-ops-marker-select-all"), + ("bpy.ops.mask.copy_splines*", "movie_clip/masking/editing.html#bpy-ops-mask-copy-splines"), ("bpy.ops.mask.parent_clear*", "movie_clip/masking/editing.html#bpy-ops-mask-parent-clear"), ("bpy.ops.mask.select_lasso*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-lasso"), ("bpy.ops.mesh.bevel.vertex*", "modeling/meshes/editing/vertex/bevel_vertices.html#bpy-ops-mesh-bevel-vertex"), @@ -2455,11 +2547,14 @@ url_manual_mapping = ( ("bpy.ops.poselib.pose_move*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-pose-move"), ("bpy.ops.preferences.addon*", "editors/preferences/addons.html#bpy-ops-preferences-addon"), ("bpy.ops.scene.light_cache*", "render/eevee/render_settings/indirect_lighting.html#bpy-ops-scene-light-cache"), + ("bpy.ops.screen.actionzone*", "interface/window_system/areas.html#bpy-ops-screen-actionzone"), ("bpy.ops.screen.area_dupli*", "interface/window_system/areas.html#bpy-ops-screen-area-dupli"), + ("bpy.ops.screen.area_split*", "interface/window_system/areas.html#bpy-ops-screen-area-split"), ("bpy.ops.screen.screenshot*", "interface/window_system/topbar.html#bpy-ops-screen-screenshot"), ("bpy.ops.sculpt.dirty_mask*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-dirty-mask"), ("bpy.ops.sculpt.symmetrize*", "sculpt_paint/sculpting/tool_settings/symmetry.html#bpy-ops-sculpt-symmetrize"), ("bpy.ops.uv.remove_doubles*", "modeling/meshes/uv/editing.html#bpy-ops-uv-remove-doubles"), + ("bpy.ops.uv.select_similar*", "editors/uv/selecting.html#bpy-ops-uv-select-similar"), ("bpy.ops.uv.sphere_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-sphere-project"), ("bpy.ops.view3d.view_orbit*", "editors/3dview/navigate/navigation.html#bpy-ops-view3d-view-orbit"), ("bpy.ops.wm.previews_clear*", "files/blend/previews.html#bpy-ops-wm-previews-clear"), @@ -2523,6 +2618,10 @@ url_manual_mapping = ( ("bpy.ops.mask.select_more*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-more"), ("bpy.ops.mesh.convex_hull*", "modeling/meshes/editing/mesh/convex_hull.html#bpy-ops-mesh-convex-hull"), ("bpy.ops.mesh.edge_rotate*", "modeling/meshes/editing/edge/rotate_edge.html#bpy-ops-mesh-edge-rotate"), + ("bpy.ops.mesh.loop_select*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-loop-select"), + ("bpy.ops.mesh.select_less*", "modeling/meshes/selecting/more_less.html#bpy-ops-mesh-select-less"), + ("bpy.ops.mesh.select_mode*", "modeling/meshes/selecting/introduction.html#bpy-ops-mesh-select-mode"), + ("bpy.ops.mesh.select_more*", "modeling/meshes/selecting/more_less.html#bpy-ops-mesh-select-more"), ("bpy.ops.mesh.unsubdivide*", "modeling/meshes/editing/edge/unsubdivide.html#bpy-ops-mesh-unsubdivide"), ("bpy.ops.mesh.uvs_reverse*", "modeling/meshes/uv/editing.html#bpy-ops-mesh-uvs-reverse"), ("bpy.ops.node.hide_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-toggle"), @@ -2533,6 +2632,9 @@ url_manual_mapping = ( ("bpy.ops.pose.scale_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-scale-clear"), ("bpy.ops.poselib.pose_add*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-add"), ("bpy.ops.scene.view_layer*", "render/layers/introduction.html#bpy-ops-scene-view-layer"), + ("bpy.ops.screen.area_join*", "interface/window_system/areas.html#bpy-ops-screen-area-join"), + ("bpy.ops.screen.area_move*", "interface/window_system/areas.html#bpy-ops-screen-area-move"), + ("bpy.ops.screen.area_swap*", "interface/window_system/areas.html#bpy-ops-screen-area-swap"), ("bpy.ops.screen.redo_last*", "interface/undo_redo.html#bpy-ops-screen-redo-last"), ("bpy.ops.sculpt.mask_init*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-init"), ("bpy.ops.sequencer.delete*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-delete"), @@ -2615,8 +2717,10 @@ url_manual_mapping = ( ("bpy.ops.nla.apply_scale*", "editors/nla/editing.html#bpy-ops-nla-apply-scale"), ("bpy.ops.nla.clear_scale*", "editors/nla/editing.html#bpy-ops-nla-clear-scale"), ("bpy.ops.nla.mute_toggle*", "editors/nla/editing.html#bpy-ops-nla-mute-toggle"), + ("bpy.ops.node.group_edit*", "interface/controls/nodes/groups.html#bpy-ops-node-group-edit"), ("bpy.ops.node.group_make*", "interface/controls/nodes/groups.html#bpy-ops-node-group-make"), ("bpy.ops.node.links_mute*", "interface/controls/nodes/editing.html#bpy-ops-node-links-mute"), + ("bpy.ops.node.parent_set*", "interface/controls/nodes/frame.html#bpy-ops-node-parent-set"), ("bpy.ops.object.armature*", "animation/armatures/index.html#bpy-ops-object-armature"), ("bpy.ops.object.face_map*", "modeling/meshes/properties/object_data.html#bpy-ops-object-face-map"), ("bpy.ops.object.join_uvs*", "scene_layout/object/editing/link_transfer/copy_uvmaps.html#bpy-ops-object-join-uvs"), @@ -2627,9 +2731,12 @@ url_manual_mapping = ( ("bpy.ops.sculpt.optimize*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-optimize"), ("bpy.ops.sequencer.split*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-split"), ("bpy.ops.transform.shear*", "modeling/meshes/editing/mesh/transform/shear.html#bpy-ops-transform-shear"), + ("bpy.ops.ui.eyedropper_**", "interface/controls/buttons/eyedropper.html#bpy-ops-ui-eyedropper"), ("bpy.ops.uv.cube_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-cube-project"), ("bpy.ops.uv.pack_islands*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pack-islands"), ("bpy.ops.uv.select_split*", "modeling/meshes/uv/editing.html#bpy-ops-uv-select-split"), + ("bpy.ops.view3d.cursor3d*", "editors/3dview/3d_cursor.html#bpy-ops-view3d-cursor3d"), + ("bpy.ops.view3d.navigate*", "editors/3dview/navigate/index.html#bpy-ops-view3d-navigate"), ("bpy.ops.view3d.view_all*", "editors/3dview/navigate/navigation.html#bpy-ops-view3d-view-all"), ("bpy.ops.view3d.view_pan*", "editors/3dview/navigate/navigation.html#bpy-ops-view3d-view-pan"), ("bpy.ops.wm.app_template*", "advanced/app_templates.html#bpy-ops-wm-app-template"), @@ -2879,10 +2986,12 @@ url_manual_mapping = ( ("bpy.ops.graph.clean*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-clean"), ("bpy.ops.graph.paste*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-paste"), ("bpy.ops.marker.move*", "animation/markers.html#bpy-ops-marker-move"), + ("bpy.ops.mask.delete*", "movie_clip/masking/editing.html#bpy-ops-mask-delete"), ("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"), ("bpy.ops.mesh.delete*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete"), ("bpy.ops.nla.move_up*", "editors/nla/editing.html#bpy-ops-nla-move-up"), ("bpy.ops.node.delete*", "interface/controls/nodes/editing.html#bpy-ops-node-delete"), + ("bpy.ops.node.detach*", "interface/controls/nodes/frame.html#bpy-ops-node-detach"), ("bpy.ops.object.bake*", "render/cycles/baking.html#bpy-ops-object-bake"), ("bpy.ops.object.hook*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook"), ("bpy.ops.object.join*", "scene_layout/object/editing/join.html#bpy-ops-object-join"), @@ -2891,6 +3000,7 @@ url_manual_mapping = ( ("bpy.ops.spreadsheet*", "editors/spreadsheet.html#bpy-ops-spreadsheet"), ("bpy.ops.uv.rip_move*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip-move"), ("bpy.ops.view3d.snap*", "scene_layout/object/editing/snap.html#bpy-ops-view3d-snap"), + ("bpy.ops.view3d.walk*", "editors/3dview/navigate/walk_fly.html#bpy-ops-view3d-walk"), ("bpy.ops.view3d.zoom*", "editors/3dview/navigate/navigation.html#bpy-ops-view3d-zoom"), ("bpy.types.*texspace*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-types-texspace"), ("bpy.types.arealight*", "render/lights/light_object.html#bpy-types-arealight"), @@ -2940,6 +3050,8 @@ url_manual_mapping = ( ("bpy.ops.pose.paste*", "animation/armatures/posing/editing/copy_paste.html#bpy-ops-pose-paste"), ("bpy.ops.pose.relax*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-relax"), ("bpy.ops.safe_areas*", "render/cameras.html#bpy-ops-safe-areas"), + ("bpy.ops.view3d.fly*", "editors/3dview/navigate/walk_fly.html#bpy-ops-view3d-fly"), + ("bpy.ops.wm.toolbar*", "interface/tool_system.html#bpy-ops-wm-toolbar"), ("bpy.types.aov.type*", "render/layers/passes.html#bpy-types-aov-type"), ("bpy.types.armature*", "animation/armatures/index.html#bpy-types-armature"), ("bpy.types.editbone*", "animation/armatures/bones/editing/index.html#bpy-types-editbone"), @@ -2967,6 +3079,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.poke*", "modeling/meshes/editing/face/poke_faces.html#bpy-ops-mesh-poke"), ("bpy.ops.mesh.spin*", "modeling/meshes/tools/spin.html#bpy-ops-mesh-spin"), ("bpy.ops.nla.split*", "editors/nla/editing.html#bpy-ops-nla-split"), + ("bpy.ops.node.join*", "interface/controls/nodes/frame.html#bpy-ops-node-join"), ("bpy.ops.pose.copy*", "animation/armatures/posing/editing/copy_paste.html#bpy-ops-pose-copy"), ("bpy.ops.pose.push*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-push"), ("bpy.ops.rigidbody*", "physics/rigid_body/index.html#bpy-ops-rigidbody"), diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 686d455b6b4..d878eea0cb9 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -493,11 +493,19 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel): else: col.operator("mesh.customdata_custom_splitnormals_add", icon='ADD') + if me.has_bevel_weight_edge: + col.operator("mesh.customdata_bevel_weight_edge_clear", icon='X') + else: + col.operator("mesh.customdata_bevel_weight_edge_add", icon='ADD') + + if me.has_bevel_weight_vertex: + col.operator("mesh.customdata_bevel_weight_vertex_clear", icon='X') + else: + col.operator("mesh.customdata_bevel_weight_vertex_add", icon='ADD') + col = layout.column(heading="Store") col.enabled = obj is not None and obj.mode != 'EDIT' - col.prop(me, "use_customdata_vertex_bevel", text="Vertex Bevel Weight") - col.prop(me, "use_customdata_edge_bevel", text="Edge Bevel Weight") col.prop(me, "use_customdata_vertex_crease", text="Vertex Crease") col.prop(me, "use_customdata_edge_crease", text="Edge Crease") diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index 78aec096510..c6d1ee2a065 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later -from bpy.types import Menu, Panel, UIList +from bpy.types import Menu, Panel, UIList, ViewLayer + +from rna_prop_ui import PropertyPanel class VIEWLAYER_UL_aov(UIList): @@ -207,7 +209,7 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel): class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerCryptomattePanel, Panel): bl_parent_id = "VIEWLAYER_PT_layer_passes" - COMPAT_ENGINES = {'BLENDER_EEVEE'} + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'} class VIEWLAYER_MT_lightgroup_sync(Menu): @@ -249,6 +251,14 @@ class VIEWLAYER_PT_layer_passes_lightgroups(ViewLayerLightgroupsPanel): COMPAT_ENGINES = {'CYCLES'} +class VIEWLAYER_PT_layer_custom_props(PropertyPanel, Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "view_layer" + _context_path = "view_layer" + _property_type = ViewLayer + + classes = ( VIEWLAYER_MT_lightgroup_sync, VIEWLAYER_PT_layer, @@ -260,6 +270,7 @@ classes = ( VIEWLAYER_PT_layer_passes_cryptomatte, VIEWLAYER_PT_layer_passes_aov, VIEWLAYER_PT_layer_passes_lightgroups, + VIEWLAYER_PT_layer_custom_props, VIEWLAYER_UL_aov, ) diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index dc4eea13ce3..2b60158e3ae 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -30,8 +30,15 @@ class OUTLINER_HT_header(Header): layout.separator_spacer() - row = layout.row(align=True) - row.prop(space, "filter_text", icon='VIEWZOOM', text="") + filter_text_supported = True + # No text filtering for library override hierarchies. The tree is lazy built to avoid + # performance issues in complex files. + if display_mode == 'LIBRARY_OVERRIDES' and space.lib_override_view_mode == 'HIERARCHIES': + filter_text_supported = False + + if filter_text_supported: + row = layout.row(align=True) + row.prop(space, "filter_text", icon='VIEWZOOM', text="") layout.separator_spacer() @@ -46,11 +53,8 @@ class OUTLINER_HT_header(Header): text="", icon='FILTER', ) - if display_mode == 'LIBRARY_OVERRIDES' and space.lib_override_view_mode == 'HIERARCHIES': - # Don't add ID type filter for library overrides hierarchies mode. Point of it is to see a hierarchy that is - # usually constructed out of different ID types. - pass - elif display_mode in {'LIBRARIES', 'LIBRARY_OVERRIDES', 'ORPHAN_DATA'}: + + if display_mode in {'LIBRARIES' 'ORPHAN_DATA'}: row.prop(space, "use_filter_id_type", text="", icon='FILTER') sub = row.row(align=True) sub.active = space.use_filter_id_type @@ -319,9 +323,22 @@ class OUTLINER_MT_object(Menu): OUTLINER_MT_context_menu.draw_common_operators(layout) +def has_selected_ids_in_context(context): + if hasattr(context, "id"): + return True + if len(context.selected_ids) > 0: + return True + + return False + + class OUTLINER_MT_asset(Menu): bl_label = "Assets" + @classmethod + def poll(cls, context): + return has_selected_ids_in_context(context) + def draw(self, _context): layout = self.layout @@ -333,6 +350,10 @@ class OUTLINER_MT_asset(Menu): class OUTLINER_MT_liboverride(Menu): bl_label = "Library Override" + @classmethod + def poll(cls, context): + return has_selected_ids_in_context(context) + def draw(self, _context): layout = self.layout @@ -394,14 +415,19 @@ class OUTLINER_PT_filter(Panel): row.prop(space, "show_mode_column", text="Show Mode Column") layout.separator() - col = layout.column(align=True) - col.label(text="Search") - col.prop(space, "use_filter_complete", text="Exact Match") - col.prop(space, "use_filter_case_sensitive", text="Case Sensitive") + filter_text_supported = True + # Same exception for library overrides as in OUTLINER_HT_header. + if display_mode == 'LIBRARY_OVERRIDES' and space.lib_override_view_mode == 'HIERARCHIES': + filter_text_supported = False + + if filter_text_supported: + col = layout.column(align=True) + col.label(text="Search") + col.prop(space, "use_filter_complete", text="Exact Match") + col.prop(space, "use_filter_case_sensitive", text="Case Sensitive") if display_mode == 'LIBRARY_OVERRIDES' and space.lib_override_view_mode == 'PROPERTIES' and bpy.data.libraries: - col.separator() - row = col.row() + row = layout.row() row.label(icon='LIBRARY_DATA_OVERRIDE') row.prop(space, "use_filter_lib_override_system", text="System Overrides") diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 23c3b0191d4..a687f3c937f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -5175,6 +5175,7 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): layout.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' layout.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps", property="type") layout.operator("gpencil.stroke_flip", text="Switch Direction") + layout.operator("gpencil.stroke_start_set", text="Set Start Point") layout.separator() layout.operator("gpencil.stroke_normalize", text="Normalize Thickness").mode = 'THICKNESS' @@ -7329,6 +7330,7 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.operator("transform.shear", text="Shear") col.operator("transform.tosphere", text="To Sphere") col.operator("transform.transform", text="Shrink/Fatten").mode = 'GPENCIL_SHRINKFATTEN' + col.operator("gpencil.stroke_start_set", text="Set Start Point") col.separator() diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt index 330cefa247a..6160d225d45 100644 --- a/source/blender/blendthumb/CMakeLists.txt +++ b/source/blender/blendthumb/CMakeLists.txt @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2006 Blender Foundation. All rights reserved. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Shared Thumbnail Extraction Logic include_directories( diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 4274ca97fd1..da1e45ababd 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -141,14 +141,6 @@ struct DerivedMesh { void (*copyLoopArray)(DerivedMesh *dm, struct MLoop *r_loop); void (*copyPolyArray)(DerivedMesh *dm, struct MPoly *r_poly); - /** Return a copy of all verts/edges/faces from the derived mesh - * it is the caller's responsibility to free the returned pointer - */ - struct MVert *(*dupVertArray)(DerivedMesh *dm); - struct MEdge *(*dupEdgeArray)(DerivedMesh *dm); - struct MLoop *(*dupLoopArray)(DerivedMesh *dm); - struct MPoly *(*dupPolyArray)(DerivedMesh *dm); - /** Return a pointer to the entire array of vert/edge/face custom data * from the derived mesh (this gives a pointer to the actual data, not * a copy) @@ -254,11 +246,6 @@ void DM_copy_vert_data(struct DerivedMesh *source, int count); /** - * Sets up mpolys for a DM based on face iterators in source. - */ -void DM_DupPolys(DerivedMesh *source, DerivedMesh *target); - -/** * Ensure the array is large enough. * * \note This function must always be thread-protected by caller. diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index dcacc2ca7b3..0f00ab9c321 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -56,7 +56,7 @@ const char *BKE_appdir_folder_home(void); * * \returns True if the path is valid and points to an existing directory. */ -bool BKE_appdir_folder_documents(char *dir); +bool BKE_appdir_folder_documents(char *dir) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Get the user's cache directory, i.e. * - Linux: `$HOME/.cache/blender/` @@ -66,7 +66,7 @@ bool BKE_appdir_folder_documents(char *dir); * \returns True if the path is valid. It doesn't create or checks format * if the `blender` folder exists. It does check if the parent of the path exists. */ -bool BKE_appdir_folder_caches(char *r_path, size_t path_len); +bool BKE_appdir_folder_caches(char *r_path, size_t path_len) ATTR_NONNULL(1); /** * Get a folder out of the \a folder_id presets for paths. * @@ -75,15 +75,17 @@ bool BKE_appdir_folder_caches(char *r_path, size_t path_len); * \return The path if found, NULL string if not. */ bool BKE_appdir_folder_id_ex(int folder_id, const char *subfolder, char *path, size_t path_len); -const char *BKE_appdir_folder_id(int folder_id, const char *subfolder); +const char *BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT; /** * Returns the path to a folder in the user area, creating it if it doesn't exist. */ -const char *BKE_appdir_folder_id_create(int folder_id, const char *subfolder); +const char *BKE_appdir_folder_id_create(int folder_id, + const char *subfolder) ATTR_WARN_UNUSED_RESULT; /** * Returns the path to a folder in the user area without checking that it actually exists first. */ -const char *BKE_appdir_folder_id_user_notest(int folder_id, const char *subfolder); +const char *BKE_appdir_folder_id_user_notest(int folder_id, + const char *subfolder) ATTR_WARN_UNUSED_RESULT; /** * Returns the path of the top-level version-specific local, user or system directory. * If check_is_dir, then the result will be NULL if the directory doesn't exist. @@ -99,23 +101,24 @@ bool BKE_appdir_app_is_portable_install(void); * Return true if templates exist */ bool BKE_appdir_app_template_any(void); -bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len); -bool BKE_appdir_app_template_has_userpref(const char *app_template); -void BKE_appdir_app_templates(struct ListBase *templates); +bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len) + ATTR_NONNULL(1); +bool BKE_appdir_app_template_has_userpref(const char *app_template) ATTR_NONNULL(1); +void BKE_appdir_app_templates(struct ListBase *templates) ATTR_NONNULL(1); /** * Initialize path to program executable. */ -void BKE_appdir_program_path_init(const char *argv0); +void BKE_appdir_program_path_init(const char *argv0) ATTR_NONNULL(1); /** * Path to executable */ -const char *BKE_appdir_program_path(void); +const char *BKE_appdir_program_path(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; /** * Path to directory of executable */ -const char *BKE_appdir_program_dir(void); +const char *BKE_appdir_program_dir(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; /** * Gets a good default directory for fonts. @@ -128,7 +131,7 @@ bool BKE_appdir_font_folder_default(char *dir); bool BKE_appdir_program_python_search(char *fullpath, size_t fullpath_len, int version_major, - int version_minor); + int version_minor) ATTR_NONNULL(1); /** * Initialize path to temporary directory. @@ -138,11 +141,11 @@ void BKE_tempdir_init(const char *userdir); /** * Path to persistent temporary directory (with trailing slash) */ -const char *BKE_tempdir_base(void); +const char *BKE_tempdir_base(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; /** * Path to temporary directory (with trailing slash) */ -const char *BKE_tempdir_session(void); +const char *BKE_tempdir_session(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; /** * Delete content of this instance's temp dir. */ diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index 83e1a3208ae..fbdacee139c 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -553,6 +553,11 @@ class MutableAttributeAccessor : public AttributeAccessor { GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id); /** + * Same as above, but returns a type that makes it easier to work with the attribute as a span. + */ + GSpanAttributeWriter lookup_for_write_span(const AttributeIDRef &attribute_id); + + /** * Get a writable attribute or non if it does not exist. * Make sure to call #finish after changes are done. */ @@ -569,6 +574,19 @@ class MutableAttributeAccessor : public AttributeAccessor { } /** + * Same as above, but returns a type that makes it easier to work with the attribute as a span. + */ + template<typename T> + SpanAttributeWriter<T> lookup_for_write_span(const AttributeIDRef &attribute_id) + { + AttributeWriter<T> attribute = this->lookup_for_write<T>(attribute_id); + if (attribute) { + return SpanAttributeWriter<T>{std::move(attribute), true}; + } + return {}; + } + + /** * Create a new attribute. * \return True, when a new attribute has been created. False, when it's not possible to create * this attribute or there is already an attribute with that id. @@ -692,6 +710,19 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer( eAttrDomainMask domain_mask, const Set<std::string> &skip = {}); +/** + * Copy attributes for the domain based on the elementwise mask. + * + * \param mask_indices: Indexed elements to copy from the source data-block. + * \param domain: Attribute domain to transfer. + * \param skip: Named attributes to ignore/skip. + */ +void copy_attribute_domain(AttributeAccessor src_attributes, + MutableAttributeAccessor dst_attributes, + IndexMask selection, + eAttrDomain domain, + const Set<std::string> &skip = {}); + bool allow_procedural_attribute_access(StringRef attribute_name); extern const char *no_procedural_access_message; @@ -755,12 +786,6 @@ class CustomDataAttributes { bool foreach_attribute(const AttributeForeachCallback callback, eAttrDomain domain) const; }; -AttributeAccessor mesh_attributes(const Mesh &mesh); -MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh); - -AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud); -MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud); - /* -------------------------------------------------------------------- */ /** \name #AttributeIDRef Inline Methods * \{ */ diff --git a/source/blender/blenkernel/BKE_cdderivedmesh.h b/source/blender/blenkernel/BKE_cdderivedmesh.h index 3c929857c14..2d1aca7c3c8 100644 --- a/source/blender/blenkernel/BKE_cdderivedmesh.h +++ b/source/blender/blenkernel/BKE_cdderivedmesh.h @@ -25,10 +25,6 @@ struct Mesh; * data to not overwrite the original. */ struct DerivedMesh *CDDM_from_mesh(struct Mesh *mesh); -/* Copies the given DerivedMesh with verts, faces & edges stored as - * custom element data. */ -struct DerivedMesh *CDDM_copy(struct DerivedMesh *source); - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h index e57679da4e6..291d76df4c8 100644 --- a/source/blender/blenkernel/BKE_collision.h +++ b/source/blender/blenkernel/BKE_collision.h @@ -129,7 +129,6 @@ typedef struct CollisionRelation { * lookup of colliders during evaluation. */ struct ListBase *BKE_collision_relations_create(struct Depsgraph *depsgraph, - const struct Scene *scene, struct Collection *collection, unsigned int modifier_type); void BKE_collision_relations_free(struct ListBase *relations); diff --git a/source/blender/blenkernel/BKE_compute_contexts.hh b/source/blender/blenkernel/BKE_compute_contexts.hh new file mode 100644 index 00000000000..a8f0022f49b --- /dev/null +++ b/source/blender/blenkernel/BKE_compute_contexts.hh @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * This file implements some specific compute contexts for concepts in Blender. + */ + +#include "BLI_compute_context.hh" + +namespace blender::bke { + +class ModifierComputeContext : public ComputeContext { + private: + static constexpr const char *s_static_type = "MODIFIER"; + + /** + * Use modifier name instead of something like `session_uuid` for now because: + * - It's more obvious that the name matches between the original and evaluated object. + * - We might want that the context hash is consistent between sessions in the future. + */ + std::string modifier_name_; + + public: + ModifierComputeContext(const ComputeContext *parent, std::string modifier_name); + + private: + void print_current_in_line(std::ostream &stream) const override; +}; + +class NodeGroupComputeContext : public ComputeContext { + private: + static constexpr const char *s_static_type = "NODE_GROUP"; + + std::string node_name_; + + public: + NodeGroupComputeContext(const ComputeContext *parent, std::string node_name); + + StringRefNull node_name() const; + + private: + void print_current_in_line(std::ostream &stream) const override; +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h index 56049ecf405..b2024f09278 100644 --- a/source/blender/blenkernel/BKE_cryptomatte.h +++ b/source/blender/blenkernel/BKE_cryptomatte.h @@ -25,6 +25,8 @@ struct CryptomatteSession *BKE_cryptomatte_init(void); struct CryptomatteSession *BKE_cryptomatte_init_from_render_result( const struct RenderResult *render_result); struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene); +struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer( + const struct ViewLayer *view_layer); void BKE_cryptomatte_free(struct CryptomatteSession *session); void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name); diff --git a/source/blender/blenkernel/BKE_cryptomatte.hh b/source/blender/blenkernel/BKE_cryptomatte.hh index cd3f8dc9f58..dd08f7b5c4f 100644 --- a/source/blender/blenkernel/BKE_cryptomatte.hh +++ b/source/blender/blenkernel/BKE_cryptomatte.hh @@ -12,6 +12,7 @@ #include "BKE_cryptomatte.h" +#include "BLI_hash_mm3.h" #include "BLI_map.hh" #include "BLI_string_ref.hh" @@ -54,10 +55,14 @@ struct CryptomatteHash { uint32_t hash; CryptomatteHash(uint32_t hash); - CryptomatteHash(const char *name, int name_len); - static CryptomatteHash from_hex_encoded(blender::StringRef hex_encoded); + CryptomatteHash(const char *name, int name_len) + { + hash = BLI_hash_mm3((const unsigned char *)name, name_len, 0); + } + static CryptomatteHash from_hex_encoded(blender::StringRef hex_encoded); std::string hex_encoded() const; + /** * Convert a cryptomatte hash to a float. * @@ -70,7 +75,20 @@ struct CryptomatteHash { * * Note that this conversion assumes to be running on a L-endian system. */ - float float_encoded() const; + float float_encoded() const + { + uint32_t mantissa = hash & ((1 << 23) - 1); + uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); + exponent = MAX2(exponent, (uint32_t)1); + exponent = MIN2(exponent, (uint32_t)254); + exponent = exponent << 23; + uint32_t sign = (hash >> 31); + sign = sign << 31; + uint32_t float_bits = sign | exponent | mantissa; + float f; + memcpy(&f, &float_bits, sizeof(uint32_t)); + return f; + } }; struct CryptomatteLayer { @@ -107,6 +125,8 @@ struct CryptomatteStampDataCallbackData { const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get( const CryptomatteSession &session); +CryptomatteLayer *BKE_cryptomatte_layer_get(CryptomatteSession &session, + const StringRef layer_name); struct CryptomatteSessionDeleter { void operator()(CryptomatteSession *session) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 4b0fc293b54..9f150c13d6e 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -22,6 +22,7 @@ #include "BLI_virtual_array.hh" #include "BKE_attribute.hh" +#include "BKE_attribute_math.hh" namespace blender::bke { @@ -162,6 +163,11 @@ class CurvesGeometry : public ::CurvesGeometry { IndexRange curves_range() const; /** + * Number of control points in the indexed curve. + */ + int points_num_for_curve(const int index) const; + + /** * 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. */ @@ -532,6 +538,16 @@ bool segment_is_vector(Span<int8_t> handle_types_left, int segment_index); /** + * True if the Bezier curve contains polygonal segments of HandleType::BEZIER_HANDLE_VECTOR. + * + * \param num_curve_points: Number of points in the curve. + * \param evaluated_size: Number of evaluated points in the curve. + * \param cyclic: If curve is cyclic. + * \param resolution: Curve resolution. + */ +bool has_vector_handles(int num_curve_points, int64_t evaluated_size, bool cyclic, int resolution); + +/** * Return true if the curve's last cyclic segment has a vector type. * This only makes a difference in the shape of cyclic curves. */ @@ -693,6 +709,36 @@ void interpolate_to_evaluated(const GSpan src, const Span<int> evaluated_offsets, GMutableSpan dst); +void calculate_basis(const float parameter, float r_weights[4]); + +/** + * Interpolate the control point values for the given parameter on the piecewise segment. + * \param a: Value associated with the first control point influencing the segment. + * \param d: Value associated with the fourth control point. + * \param parameter: Parameter in range [0, 1] to compute the interpolation for. + */ +template<typename T> +T interpolate(const T &a, const T &b, const T &c, const T &d, const float parameter) +{ + float n[4]; + calculate_basis(parameter, n); + /* 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>) { + T return_value; + attribute_math::DefaultMixer<T> mixer({&return_value, 1}); + mixer.mix_in(0, a, n[0] * 0.5f); + mixer.mix_in(0, b, n[1] * 0.5f); + mixer.mix_in(0, c, n[2] * 0.5f); + mixer.mix_in(0, d, n[3] * 0.5f); + mixer.finalize(); + return return_value; + } + else { + return 0.5f * (a * n[0] + b * n[1] + c * n[2] + d * n[3]); + } +} + } // namespace catmull_rom /** \} */ @@ -807,6 +853,16 @@ inline IndexRange CurvesGeometry::curves_range() const return IndexRange(this->curves_num()); } +inline int CurvesGeometry::points_num_for_curve(const int index) const +{ + BLI_assert(this->curve_num > 0); + BLI_assert(this->curve_num > index); + BLI_assert(this->curve_offsets != nullptr); + const int offset = this->curve_offsets[index]; + const int offset_next = this->curve_offsets[index + 1]; + return offset_next - offset; +} + inline bool CurvesGeometry::is_single_type(const CurveType type) const { return this->curve_type_counts()[type] == this->curves_num(); @@ -833,6 +889,7 @@ inline IndexRange CurvesGeometry::points_for_curve(const int index) const { /* Offsets are not allocated when there are no curves. */ BLI_assert(this->curve_num > 0); + BLI_assert(this->curve_num > index); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[index]; const int offset_next = this->curve_offsets[index + 1]; @@ -905,11 +962,13 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in /** \} */ +namespace curves { + /* -------------------------------------------------------------------- */ /** \name Bezier Inline Methods * \{ */ -namespace curves::bezier { +namespace bezier { inline bool point_is_sharp(const Span<int8_t> handle_types_left, const Span<int8_t> handle_types_right, @@ -929,14 +988,24 @@ inline bool segment_is_vector(const int8_t left, const int8_t right) return segment_is_vector(HandleType(left), HandleType(right)); } +inline bool has_vector_handles(const int num_curve_points, + const int64_t evaluated_size, + const bool cyclic, + const int resolution) +{ + return evaluated_size - !cyclic != (int64_t)segments_num(num_curve_points, cyclic) * resolution; +} + inline float3 calculate_vector_handle(const float3 &point, const float3 &next_point) { return math::interpolate(point, next_point, 1.0f / 3.0f); } +} // namespace bezier + /** \} */ -} // namespace curves::bezier +} // namespace curves struct CurvesSurfaceTransforms { float4x4 curves_to_world; diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh index 0fbd33002e1..5579ab5654a 100644 --- a/source/blender/blenkernel/BKE_curves_utils.hh +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -11,9 +11,301 @@ #include "BLI_function_ref.hh" #include "BLI_generic_pointer.hh" +#include "BLI_index_range.hh" namespace blender::bke::curves { +/* -------------------------------------------------------------------- + * Utility structs. + */ + +/** + * Reference to a piecewise segment on a spline curve. + */ +struct CurveSegment { + /** + * Index of the previous control/evaluated point on the curve. First point on the segment. + */ + int index; + /** + * Index of the next control/evaluated point on the curve. Last point on the curve segment. + * Should be 0 for looped segments. + */ + int next_index; +}; + +/** + * Reference to a point on a piecewise curve (spline). + * + * Tracks indices of the neighbouring control/evaluated point pair associated with the segment + * in which the point resides. Referenced point within the segment is defined by a + * normalized parameter in the range [0, 1]. + */ +struct CurvePoint : public CurveSegment { + /** + * Normalized parameter in the range [0, 1] defining the point on the piecewise segment. + * Note that the curve point representation is not unique at segment endpoints. + */ + float parameter; + + /** + * True if the parameter is an integer and references a control/evaluated point. + */ + inline bool is_controlpoint() const; + + /* + * Compare if the points are equal. + */ + inline bool operator==(const CurvePoint &other) const; + inline bool operator!=(const CurvePoint &other) const; + + /** + * Compare if 'this' point comes before 'other'. Loop segment for cyclical curves counts + * as the first (least) segment. + */ + inline bool operator<(const CurvePoint &other) const; +}; + +/** + * Cyclical index range. Iterates the interval [start, end). + */ +class IndexRangeCyclic { + /* Index to the start and end of the iterated range. + */ + int64_t start_ = 0; + int64_t end_ = 0; + /* Index for the start and end of the entire iterable range which contains the iterated range + * (e.g. the point range for an indiviudal spline/curve within the entire Curves point domain). + */ + int64_t range_start_ = 0; + int64_t range_end_ = 0; + /* Number of times the range end is passed when the range is iterated. + */ + int64_t cycles_ = 0; + + constexpr IndexRangeCyclic(int64_t begin, + int64_t end, + int64_t iterable_range_start, + int64_t iterable_range_end, + int64_t cycles) + : start_(begin), + end_(end), + range_start_(iterable_range_start), + range_end_(iterable_range_end), + cycles_(cycles) + { + } + + public: + constexpr IndexRangeCyclic() = default; + ~IndexRangeCyclic() = default; + + constexpr IndexRangeCyclic(int64_t start, int64_t end, IndexRange iterable_range, int64_t cycles) + : start_(start), + end_(end), + range_start_(iterable_range.first()), + range_end_(iterable_range.one_after_last()), + cycles_(cycles) + { + } + + /** + * Create an iterator over the cyclical interval [start_index, end_index). + */ + constexpr IndexRangeCyclic(int64_t start, int64_t end, IndexRange iterable_range) + : start_(start), + end_(end == iterable_range.one_after_last() ? iterable_range.first() : end), + range_start_(iterable_range.first()), + range_end_(iterable_range.one_after_last()), + cycles_(end < start) + { + } + + /** + * Increment the range by adding the given number of indices to the beginning of the range. + */ + constexpr IndexRangeCyclic push_forward(int n) + { + BLI_assert(n >= 0); + int64_t nstart = start_ - n; + int64_t cycles = cycles_; + if (nstart < range_start_) { + + cycles += (int64_t)(n / (range_end_ - range_start_)) + (end_ < nstart) - (end_ < start_); + } + return {nstart, end_, range_start_, range_end_, cycles}; + } + /** + * Increment the range by adding the given number of indices to the end of the range. + */ + constexpr IndexRangeCyclic push_backward(int n) + { + BLI_assert(n >= 0); + int64_t new_end = end_ + n; + int64_t cycles = cycles_; + if (range_end_ <= new_end) { + cycles += (int64_t)(n / (range_end_ - range_start_)) + (new_end < start_) - (end_ < start_); + } + return {start_, new_end, range_start_, range_end_, cycles}; + } + + /** + * Get the index range for the curve buffer. + */ + constexpr IndexRange curve_range() const + { + return IndexRange(range_start_, total_size()); + } + + /** + * Range between the first element up to the end of the range. + */ + constexpr IndexRange range_before_loop() const + { + return IndexRange(start_, size_before_loop()); + } + + /** + * Range between the first element in the iterable range up to the last element in the range. + */ + constexpr IndexRange range_after_loop() const + { + return IndexRange(range_start_, size_after_loop()); + } + + /** + * Size of the entire iterable range. + */ + constexpr int64_t total_size() const + { + return range_end_ - range_start_; + } + + /** + * Number of elements between the first element in the range up to the last element in the curve. + */ + constexpr int64_t size_before_loop() const + { + return range_end_ - start_; + } + + /** + * Number of elements between the first element in the iterable range up to the last element in + * the range. + */ + constexpr int64_t size_after_loop() const + { + return end_ - range_start_; + } + + /** + * Get number of elements iterated by the cyclical index range. + */ + constexpr int64_t size() const + { + if (cycles_ > 0) { + return size_before_loop() + end_ + (cycles_ - 1) * (range_end_ - range_start_); + } + else { + return end_ - start_; + } + } + + /** + * Return the number of times the iterator will cycle before ending. + */ + constexpr int64_t cycles() const + { + return cycles_; + } + + constexpr int64_t first() const + { + return start_; + } + + constexpr int64_t one_after_last() const + { + return end_; + } + + struct CyclicIterator; /* Forward declaration */ + + constexpr CyclicIterator begin() const + { + return CyclicIterator(range_start_, range_end_, start_, 0); + } + + constexpr CyclicIterator end() const + { + return CyclicIterator(range_start_, range_end_, end_, cycles_); + } + + struct CyclicIterator { + int64_t index_, begin_, end_, cycles_; + + constexpr CyclicIterator(int64_t range_begin, int64_t range_end, int64_t index, int64_t cycles) + : index_(index), begin_(range_begin), end_(range_end), cycles_(cycles) + { + BLI_assert(range_begin <= index && index <= range_end); + } + + constexpr CyclicIterator(const CyclicIterator ©) + : index_(copy.index_), begin_(copy.begin_), end_(copy.end_), cycles_(copy.cycles_) + { + } + ~CyclicIterator() = default; + + constexpr CyclicIterator &operator=(const CyclicIterator ©) + { + if (this == ©) { + return *this; + } + index_ = copy.index_; + begin_ = copy.begin_; + end_ = copy.end_; + cycles_ = copy.cycles_; + return *this; + } + constexpr CyclicIterator &operator++() + { + index_++; + if (index_ == end_) { + index_ = begin_; + cycles_++; + } + return *this; + } + + void increment(int64_t n) + { + for (int i = 0; i < n; i++) { + ++*this; + } + } + + constexpr const int64_t &operator*() const + { + return index_; + } + + constexpr bool operator==(const CyclicIterator &other) const + { + return index_ == other.index_ && cycles_ == other.cycles_; + } + constexpr bool operator!=(const CyclicIterator &other) const + { + return !this->operator==(other); + } + }; +}; + +/** \} */ + +/* -------------------------------------------------------------------- + * Utility functions. + */ + /** * Copy the provided point attribute values between all curves in the #curve_ranges index * ranges, assuming that all curves have the same number of control points in #src_curves @@ -88,4 +380,40 @@ void foreach_curve_by_type(const VArray<int8_t> &types, FunctionRef<void(IndexMask)> bezier_fn, FunctionRef<void(IndexMask)> nurbs_fn); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #CurvePoint Inline Methods + * \{ */ + +inline bool CurvePoint::is_controlpoint() const +{ + return parameter == 0.0 || parameter == 1.0; +} + +inline bool CurvePoint::operator==(const CurvePoint &other) const +{ + return (parameter == other.parameter && index == other.index) || + (parameter == 1.0 && other.parameter == 0.0 && next_index == other.index) || + (parameter == 0.0 && other.parameter == 1.0 && index == other.next_index); +} +inline bool CurvePoint::operator!=(const CurvePoint &other) const +{ + return !this->operator==(other); +} + +inline bool CurvePoint::operator<(const CurvePoint &other) const +{ + if (index == other.index) { + return parameter < other.parameter; + } + else { + /* Use next index for cyclic comparison due to loop segment < first segment. */ + return next_index < other.next_index && + !(next_index == other.index && parameter == 1.0 && other.parameter == 0.0); + } +} + +/** \} */ + } // namespace blender::bke::curves diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 44a4f4b5395..24fa5f0e87a 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -178,13 +178,11 @@ bool CustomData_merge_mesh_to_bmesh(const struct CustomData *source, int totelem); /** - * Reallocate custom data to a new element count. - * Only affects on data layers which are owned by the CustomData itself, - * referenced data is kept unchanged, - * - * \note Take care of referenced layers by yourself! + * Reallocate custom data to a new element count. If the new size is larger, the new values use + * the #CD_CONSTRUCT behavior, so trivial types must be initialized by the caller. After being + * resized, the #CustomData does not contain any referenced layers. */ -void CustomData_realloc(struct CustomData *data, int totelem); +void CustomData_realloc(struct CustomData *data, int old_size, int new_size); /** * BMesh version of CustomData_merge; merges the layouts of source and `dest`, @@ -640,7 +638,6 @@ enum { CD_FAKE_CREASE = CD_FAKE | CD_CREASE, /* *sigh*. */ /* Multiple types of mesh elements... */ - CD_FAKE_BWEIGHT = CD_FAKE | CD_BWEIGHT, /* *sigh*. */ CD_FAKE_UV = CD_FAKE | CD_MLOOPUV, /* UV flag, because we handle both loop's UVs and poly's textures. */ diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index f58a5502788..4023d6829d4 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -50,16 +50,34 @@ void BKE_defgroup_copy_list(struct ListBase *outbase, const struct ListBase *inb struct bDeformGroup *BKE_defgroup_duplicate(const struct bDeformGroup *ingroup); struct bDeformGroup *BKE_object_defgroup_find_name(const struct Object *ob, const char *name); /** - * \note caller must free. + * Returns flip map for the vertex-groups of `ob`. + * + * \param use_default: How to handle cases where no symmetrical group is found. + * - false: sets these indices to -1, indicating the group should be ignored. + * - true: sets the index to its location in the array (making the group point to it's self). + * Enable this for symmetrical actions which apply weight operations on symmetrical vertices + * where the symmetrical group will be used (if found), otherwise the same group is used. + * + * \return An index array `r_flip_map_num` length, + * (aligned with the list result from `BKE_id_defgroup_list_get(ob)`). + * referencing the index of the symmetrical vertex-group of a fall-back value (see `use_default`). + * The caller is responsible for freeing the array. + */ +int *BKE_object_defgroup_flip_map(const struct Object *ob, bool use_default, int *r_flip_map_num); + +/** + * A version of #BKE_object_defgroup_flip_map that ignores locked groups. */ -int *BKE_object_defgroup_flip_map(const struct Object *ob, int *flip_map_len, bool use_default); +int *BKE_object_defgroup_flip_map_unlocked(const struct Object *ob, + bool use_default, + int *r_flip_map_num); /** - * \note caller must free. + * A version of #BKE_object_defgroup_flip_map that only takes a single group into account. */ int *BKE_object_defgroup_flip_map_single(const struct Object *ob, - int *flip_map_len, bool use_default, - int defgroup); + int defgroup, + int *r_flip_map_num); int BKE_object_defgroup_flip_index(const struct Object *ob, int index, bool use_default); int BKE_object_defgroup_name_index(const struct Object *ob, const char *name); void BKE_object_defgroup_unique_name(struct bDeformGroup *dg, struct Object *ob); @@ -112,7 +130,7 @@ float BKE_defvert_array_find_weight_safe(const struct MDeformVert *dvert, int in * \return The total weight in all groups marked in the selection mask. */ float BKE_defvert_total_selected_weight(const struct MDeformVert *dv, - int defbase_tot, + int defbase_num, const bool *defbase_sel); /** @@ -124,9 +142,9 @@ float BKE_defvert_total_selected_weight(const struct MDeformVert *dv, * commutative with the collective weight function. */ float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv, - int defbase_tot, + int defbase_num, const bool *defbase_sel, - int defbase_tot_sel, + int defbase_sel_num, bool is_normalized); /* This much unlocked weight is considered equivalent to none. */ @@ -147,7 +165,7 @@ float BKE_defvert_calc_lock_relative_weight(float weight, */ float BKE_defvert_lock_relative_weight(float weight, const struct MDeformVert *dv, - int defbase_tot, + int defbase_num, const bool *defbase_locked, const bool *defbase_unlocked); @@ -160,7 +178,7 @@ void BKE_defvert_copy(struct MDeformVert *dvert_dst, const struct MDeformVert *d void BKE_defvert_copy_subset(struct MDeformVert *dvert_dst, const struct MDeformVert *dvert_src, const bool *vgroup_subset, - int vgroup_tot); + int vgroup_num); /** * Overwrite weights filtered by vgroup_subset and with mirroring specified by the flip map * - do nothing if neither are set. @@ -169,9 +187,9 @@ void BKE_defvert_copy_subset(struct MDeformVert *dvert_dst, void BKE_defvert_mirror_subset(struct MDeformVert *dvert_dst, const struct MDeformVert *dvert_src, const bool *vgroup_subset, - int vgroup_tot, + int vgroup_num, const int *flip_map, - int flip_map_len); + int flip_map_num); /** * Copy an index from one #MDeformVert to another. * - do nothing if neither are set. @@ -194,43 +212,43 @@ void BKE_defvert_sync(struct MDeformVert *dvert_dst, void BKE_defvert_sync_mapped(struct MDeformVert *dvert_dst, const struct MDeformVert *dvert_src, const int *flip_map, - int flip_map_len, + int flip_map_num, bool use_ensure); /** * be sure all flip_map values are valid */ void BKE_defvert_remap(struct MDeformVert *dvert, const int *map, int map_len); -void BKE_defvert_flip(struct MDeformVert *dvert, const int *flip_map, int flip_map_len); -void BKE_defvert_flip_merged(struct MDeformVert *dvert, const int *flip_map, int flip_map_len); +void BKE_defvert_flip(struct MDeformVert *dvert, const int *flip_map, int flip_map_num); +void BKE_defvert_flip_merged(struct MDeformVert *dvert, const int *flip_map, int flip_map_num); void BKE_defvert_normalize(struct MDeformVert *dvert); /** * Same as #BKE_defvert_normalize but takes a bool array. */ void BKE_defvert_normalize_subset(struct MDeformVert *dvert, const bool *vgroup_subset, - int vgroup_tot); + int vgroup_num); /** * Same as BKE_defvert_normalize() if the locked vgroup is not a member of the subset */ void BKE_defvert_normalize_lock_single(struct MDeformVert *dvert, const bool *vgroup_subset, - int vgroup_tot, + int vgroup_num, uint def_nr_lock); /** * Same as BKE_defvert_normalize() if no locked vgroup is a member of the subset */ void BKE_defvert_normalize_lock_map(struct MDeformVert *dvert, const bool *vgroup_subset, - int vgroup_tot, + int vgroup_num, const bool *lock_flags, - int defbase_tot); + int defbase_num); /* Utilities to 'extract' a given vgroup into a simple float array, * for verts, but also edges/polys/loops. */ void BKE_defvert_extract_vgroup_to_vertweights(const struct MDeformVert *dvert, int defgroup, - int num_verts, + int verts_num, bool invert_vgroup, float *r_weights); /** @@ -239,25 +257,25 @@ void BKE_defvert_extract_vgroup_to_vertweights(const struct MDeformVert *dvert, */ void BKE_defvert_extract_vgroup_to_edgeweights(const struct MDeformVert *dvert, int defgroup, - int num_verts, + int verts_num, const struct MEdge *edges, - int num_edges, + int edges_num, bool invert_vgroup, float *r_weights); void BKE_defvert_extract_vgroup_to_loopweights(const struct MDeformVert *dvert, int defgroup, - int num_verts, + int verts_num, const struct MLoop *loops, - int num_loops, + int loops_num, bool invert_vgroup, float *r_weights); void BKE_defvert_extract_vgroup_to_polyweights(const struct MDeformVert *dvert, int defgroup, - int num_verts, + int verts_num, const struct MLoop *loops, - int num_loops, + int loops_num, const struct MPoly *polys, - int num_polys, + int polys_num, bool invert_vgroup, float *r_weights); diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 7e2cd87cb0d..256ddec5505 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -85,7 +85,7 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id, typedef void (*IDTypeForeachPathFunction)(struct ID *id, struct BPathForeachPathData *bpath_data); -typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct ID *id); +typedef struct ID **(*IDTypeEmbeddedOwnerPointerGetFunction)(struct ID *id); typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer, struct ID *id, @@ -180,9 +180,9 @@ typedef struct IDTypeInfo { IDTypeForeachPathFunction foreach_path; /** - * For embedded IDs, return their owner ID. + * For embedded IDs, return the address of the pointer to their owner ID. */ - IDTypeEmbeddedOwnerGetFunction owner_get; + IDTypeEmbeddedOwnerPointerGetFunction owner_pointer_get; /* ********** Callbacks for reading and writing .blend files. ********** */ diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 9f506ded8e9..45a72e8d7a3 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -95,6 +95,9 @@ struct KeyBlock *BKE_keyblock_from_key(struct Key *key, int index); * Get the appropriate #KeyBlock given a name to search for. */ struct KeyBlock *BKE_keyblock_find_name(struct Key *key, const char name[]); + +struct KeyBlock *BKE_keyblock_find_uid(struct Key *key, int uid); + /** * \brief copy shape-key attributes, but not key data or name/UID. */ diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index 8f058432044..8cfc9ef8be9 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -253,8 +253,8 @@ void BKE_layer_collection_set_flag(struct LayerCollection *lc, int flag, bool va /** * Applies object's restrict flags on top of flags coming from the collection - * and stores those in `base->flag`. #BASE_VISIBLE_DEPSGRAPH ignores viewport flags visibility - * (i.e., restriction and local collection). + * and stores those in `base->flag`. #BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT ignores viewport + * flags visibility (i.e., restriction and local collection). */ void BKE_base_eval_flags(struct Base *base); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index febdad2ca0d..e5b013ce201 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -620,6 +620,13 @@ bool BKE_id_is_in_global_main(struct ID *id); bool BKE_id_can_be_asset(const struct ID *id); +/** + * Return the owner ID of the given `id`, if any. + * + * \note This will only return non-NULL for embedded IDs (master collections etc.), and shape-keys. + */ +struct ID *BKE_id_owner_get(struct ID *id); + /** Check if that ID can be considered as editable from a high-level (editor) perspective. * * NOTE: This used to be done with a check on whether ID was linked or not, but now with system diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index a5b0e21f0ca..ef57c9a2e0e 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -292,13 +292,10 @@ struct Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, bool build_shapekey_layers); /** - * Copies a nomain-Mesh into an existing Mesh. + * Move data from a mesh outside of the main data-base into a mesh in the data-base. + * Takes ownership of the source mesh. */ -void BKE_mesh_nomain_to_mesh(struct Mesh *mesh_src, - struct Mesh *mesh_dst, - struct Object *ob, - const struct CustomData_MeshMasks *mask, - bool take_ownership); +void BKE_mesh_nomain_to_mesh(struct Mesh *mesh_src, struct Mesh *mesh_dst, struct Object *ob); void BKE_mesh_nomain_to_meshkey(struct Mesh *mesh_src, struct Mesh *mesh_dst, struct KeyBlock *kb); /* vertex level transformations & checks (no derived mesh) */ @@ -665,18 +662,18 @@ void BKE_mesh_normals_loop_custom_set(const struct MVert *mverts, const float (*polynors)[3], int numPolys, short (*r_clnors_data)[2]); -void BKE_mesh_normals_loop_custom_from_vertices_set(const struct MVert *mverts, - const float (*vert_normals)[3], - float (*r_custom_vertnors)[3], - int numVerts, - struct MEdge *medges, - int numEdges, - const struct MLoop *mloops, - int numLoops, - const struct MPoly *mpolys, - const float (*polynors)[3], - int numPolys, - short (*r_clnors_data)[2]); +void BKE_mesh_normals_loop_custom_from_verts_set(const struct MVert *mverts, + const float (*vert_normals)[3], + float (*r_custom_vertnors)[3], + int numVerts, + struct MEdge *medges, + int numEdges, + const struct MLoop *mloops, + int numLoops, + const struct MPoly *mpolys, + const float (*polynors)[3], + int numPolys, + short (*r_clnors_data)[2]); /** * Computes average per-vertex normals from given custom loop normals. @@ -717,12 +714,12 @@ void BKE_mesh_calc_normals_split_ex(struct Mesh *mesh, void BKE_mesh_set_custom_normals(struct Mesh *mesh, float (*r_custom_loopnors)[3]); /** * Higher level functions hiding most of the code needed around call to - * #BKE_mesh_normals_loop_custom_from_vertices_set(). + * #BKE_mesh_normals_loop_custom_from_verts_set(). * * \param r_custom_vertnors: is not const, since code will replace zero_v3 normals there * with automatically computed vectors. */ -void BKE_mesh_set_custom_normals_from_vertices(struct Mesh *mesh, float (*r_custom_vertnors)[3]); +void BKE_mesh_set_custom_normals_from_verts(struct Mesh *mesh, float (*r_custom_vertnors)[3]); /* *** mesh_evaluate.cc *** */ @@ -812,10 +809,10 @@ void BKE_mesh_polygon_flip(const struct MPoly *mpoly, * * \note Invalidates tessellation, caller must handle that. */ -void BKE_mesh_polygons_flip(const struct MPoly *mpoly, - struct MLoop *mloop, - struct CustomData *ldata, - int totpoly); +void BKE_mesh_polys_flip(const struct MPoly *mpoly, + struct MLoop *mloop, + struct CustomData *ldata, + int totpoly); /* Merge verts. */ /* Enum for merge_mode of #BKE_mesh_merge_verts. diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h index 0dc44ecb247..9d94c692858 100644 --- a/source/blender/blenkernel/BKE_mesh_fair.h +++ b/source/blender/blenkernel/BKE_mesh_fair.h @@ -25,16 +25,16 @@ typedef enum eMeshFairingDepth { /* affect_vertices is used to define the fairing area. Indexed by vertex index, set to true when * the vertex should be modified by fairing. */ -void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm, - bool *affect_vertices, - eMeshFairingDepth depth); +void BKE_bmesh_prefair_and_fair_verts(struct BMesh *bm, + bool *affect_verts, + eMeshFairingDepth depth); /* This function can optionally use the MVert coordinates of deform_mverts to read and write the * fairing result. When NULL, the function will use mesh->mverts directly. */ -void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh, - struct MVert *deform_mverts, - bool *affect_vertices, - eMeshFairingDepth depth); +void BKE_mesh_prefair_and_fair_verts(struct Mesh *mesh, + struct MVert *deform_mverts, + bool *affect_verts, + eMeshFairingDepth depth); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h index 11ee86c62a7..e67aec0b9ce 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h +++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h @@ -18,6 +18,15 @@ struct Mesh; struct MFace; /** + * Copy bevel weights from separate layers into vertices and edges. + */ +void BKE_mesh_legacy_bevel_weight_from_layers(struct Mesh *mesh); +/** + * Copy bevel weights from vertices and edges to separate layers. + */ +void BKE_mesh_legacy_bevel_weight_to_layers(struct Mesh *mesh); + +/** * Convert the hidden element attributes to the old flag format for writing. */ void BKE_mesh_legacy_convert_hide_layers_to_flags(struct Mesh *mesh); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 46303a4e19c..55bf24f943e 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1337,15 +1337,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_CHAN_RGB 1 #define CMP_CHAN_A 2 -/* scale node type, in custom1 */ -#define CMP_SCALE_RELATIVE 0 -#define CMP_SCALE_ABSOLUTE 1 -#define CMP_SCALE_SCENEPERCENT 2 -#define CMP_SCALE_RENDERPERCENT 3 -/* custom2 */ -#define CMP_SCALE_RENDERSIZE_FRAME_ASPECT (1 << 0) -#define CMP_SCALE_RENDERSIZE_FRAME_CROP (1 << 1) - /* track position node, in custom1 */ #define CMP_TRACKPOS_ABSOLUTE 0 #define CMP_TRACKPOS_RELATIVE_START 1 diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index f2e551a9f32..194820aa4ba 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -21,6 +21,7 @@ struct bNodeType; namespace blender::nodes { struct FieldInferencingInterface; class NodeDeclaration; +struct GeometryNodesLazyFunctionGraphInfo; } // namespace blender::nodes namespace blender::bke { @@ -49,6 +50,15 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface; /** + * For geometry nodes, a lazy function graph with some additional info is cached. This is used to + * evaluate the node group. Caching it here allows us to reuse the preprocessed node tree in case + * its used multiple times. + */ + std::mutex geometry_nodes_lazy_function_graph_info_mutex; + std::unique_ptr<nodes::GeometryNodesLazyFunctionGraphInfo> + geometry_nodes_lazy_function_graph_info; + + /** * Protects access to all topology cache variables below. This is necessary so that the cache can * be updated on a const #bNodeTree. */ @@ -70,6 +80,7 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { MultiValueMap<const bNodeType *, bNode *> nodes_by_type; Vector<bNode *> toposort_left_to_right; Vector<bNode *> toposort_right_to_left; + Vector<bNode *> group_nodes; bool has_link_cycle = false; bool has_undefined_nodes_or_sockets = false; bNode *group_output_node = nullptr; @@ -148,6 +159,12 @@ class bNodeRuntime : NonCopyable, NonMovable { namespace node_tree_runtime { +/** + * Is executed when the depsgraph determines that something in the node group changed that will + * affect the output. + */ +void handle_node_tree_output_changed(bNodeTree &tree_cow); + class AllowUsingOutdatedInfo : NonCopyable, NonMovable { private: const bNodeTree &tree_; @@ -241,6 +258,18 @@ inline blender::Span<bNode *> bNodeTree::all_nodes() return this->runtime->nodes; } +inline blender::Span<const bNode *> bNodeTree::group_nodes() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->group_nodes; +} + +inline blender::Span<bNode *> bNodeTree::group_nodes() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->group_nodes; +} + inline bool bNodeTree::has_link_cycle() const { BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); @@ -413,7 +442,6 @@ inline blender::Span<const bNodeLink *> bNode::internal_links_span() const inline const blender::nodes::NodeDeclaration *bNode::declaration() const { - BLI_assert(this->runtime->declaration != nullptr); return this->runtime->declaration; } diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 2197fa3af1e..eef91bacc2f 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -213,9 +213,7 @@ bool BKE_paint_always_hide_test(struct Object *ob); /** * Returns non-zero if any of the face's vertices are hidden, zero otherwise. */ -bool paint_is_face_hidden(const struct MLoopTri *lt, - const bool *hide_vert, - const struct MLoop *mloop); +bool paint_is_face_hidden(const struct MLoopTri *lt, const bool *hide_poly); /** * Returns non-zero if any of the corners of the grid * face whose inner corner is at (x, y) are hidden, zero otherwise. @@ -402,7 +400,7 @@ typedef struct SculptBoundaryEditInfo { int original_vertex_i; /* How many steps were needed to reach this vertex from the boundary. */ - int num_propagation_steps; + int propagation_steps_num; /* Strength that is used to deform this vertex. */ float strength_factor; @@ -416,10 +414,10 @@ typedef struct SculptBoundaryPreviewEdge { typedef struct SculptBoundary { /* Vertex indices of the active boundary. */ - PBVHVertRef *vertices; - int *vertices_i; - int vertices_capacity; - int num_vertices; + PBVHVertRef *verts; + int *verts_i; + int verts_capacity; + int verts_num; /* Distance from a vertex in the boundary to initial vertex indexed by vertex index, taking into * account the length of all edges between them. Any vertex that is not in the boundary will have @@ -429,7 +427,7 @@ typedef struct SculptBoundary { /* Data for drawing the preview. */ SculptBoundaryPreviewEdge *edges; int edges_capacity; - int num_edges; + int edges_num; /* True if the boundary loops into itself. */ bool forms_loop; @@ -689,7 +687,7 @@ void BKE_sculpt_update_object_for_edit(struct Depsgraph *depsgraph, bool need_pmap, bool need_mask, bool is_paint_tool); -void BKE_sculpt_update_object_before_eval(const struct Scene *scene, struct Object *ob_eval); +void BKE_sculpt_update_object_before_eval(struct Object *ob_eval); void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Object *ob_eval); /** @@ -698,6 +696,7 @@ void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Obj */ struct MultiresModifierData *BKE_sculpt_multires_active(const struct Scene *scene, struct Object *ob); +int *BKE_sculpt_face_sets_ensure(struct Mesh *mesh); int BKE_sculpt_mask_layers_ensure(struct Object *ob, struct MultiresModifierData *mmd); void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene); @@ -719,18 +718,17 @@ void BKE_sculpt_sync_face_sets_visibility_to_grids(struct Mesh *mesh, struct SubdivCCG *subdiv_ccg); /** - * Ensures that a Face Set data-layers exists. If it does not, it creates one respecting the - * visibility stored in the vertices of the mesh. If it does, it copies the visibility from the - * mesh to the Face Sets. */ -void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(struct Mesh *mesh); + * If a face set layer exists, initialize its visibility (sign) from the mesh's hidden values. + */ +void BKE_sculpt_face_sets_update_from_base_mesh_visibility(struct Mesh *mesh); /** - * Ensures we do have expected mesh data in original mesh for the sculpt mode. + * Makes sculpt data consistent with other data on the mesh. * * \note IDs are expected to be original ones here, and calling code should ensure it updates its * depsgraph properly after calling this function if it needs up-to-date evaluated data. */ -void BKE_sculpt_ensure_orig_mesh_data(struct Scene *scene, struct Object *object); +void BKE_sculpt_ensure_orig_mesh_data(struct Object *object); /** * Test if PBVH can be used directly for drawing, which is faster than diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 8c9488b0b46..6a194698bd8 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -389,7 +389,7 @@ const struct CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh); struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh); BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh); -int BKE_pbvh_get_grid_num_vertices(const PBVH *pbvh); +int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh); int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh); /** @@ -491,6 +491,12 @@ void BKE_pbvh_grids_update(PBVH *pbvh, void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, struct SubdivCCG *subdiv_ccg); void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets); +/** + * If an operation causes the hide status stored in the mesh to change, this must be called + * to update the references to those attributes, since they are only added when necessary. + */ +void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh); + void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default); void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide); @@ -674,6 +680,8 @@ const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]; const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh); bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh); +const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh); + PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node); void BKE_pbvh_node_color_buffer_free(PBVH *pbvh); bool BKE_pbvh_get_color_layer(const struct Mesh *me, diff --git a/source/blender/blenkernel/BKE_subdiv_mesh.h b/source/blender/blenkernel/BKE_subdiv_mesh.h index b24db517143..49c45efafe0 100644 --- a/source/blender/blenkernel/BKE_subdiv_mesh.h +++ b/source/blender/blenkernel/BKE_subdiv_mesh.h @@ -14,7 +14,9 @@ extern "C" { #endif struct Mesh; +struct MeshElemMap; struct MEdge; +struct MVert; struct Subdiv; typedef struct SubdivToMeshSettings { @@ -37,8 +39,10 @@ struct Mesh *BKE_subdiv_to_mesh(struct Subdiv *subdiv, /* Interpolate a position along the `coarse_edge` at the relative `u` coordinate. If `is_simple` is * false, this will perform a B-Spline interpolation using the edge neighbors, otherwise a linear * interpolation will be done base on the edge vertices. */ -void BKE_subdiv_mesh_interpolate_position_on_edge(const struct Mesh *coarse_mesh, - const struct MEdge *coarse_edge, +void BKE_subdiv_mesh_interpolate_position_on_edge(const struct MVert *coarse_verts, + const struct MEdge *coarse_edges, + const struct MeshElemMap *vert_to_edge_map, + int coarse_edge_index, bool is_simple, float u, float pos_r[3]); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 9521da8417e..2f1e1897f8d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -98,6 +98,7 @@ set(SRC intern/collision.c intern/colorband.c intern/colortools.c + intern/compute_contexts.cc intern/constraint.c intern/context.c intern/crazyspace.cc @@ -352,6 +353,7 @@ set(SRC BKE_collision.h BKE_colorband.h BKE_colortools.h + BKE_compute_contexts.hh BKE_constraint.h BKE_context.h BKE_crazyspace.h @@ -659,6 +661,10 @@ if(WITH_PYTHON) ) add_definitions(-DWITH_PYTHON) + if(WITH_PYTHON_MODULE) + add_definitions(-DWITH_PYTHON_MODULE) + endif() + if(WITH_PYTHON_SAFETY) add_definitions(-DWITH_PYTHON_SAFETY) endif() diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index e9c32b760e4..0d07ea428bc 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -147,54 +147,6 @@ static MPoly *dm_getPolyArray(DerivedMesh *dm) return mpoly; } -static MVert *dm_dupVertArray(DerivedMesh *dm) -{ - MVert *tmp = (MVert *)MEM_malloc_arrayN( - dm->getNumVerts(dm), sizeof(*tmp), "dm_dupVertArray tmp"); - - if (tmp) { - dm->copyVertArray(dm, tmp); - } - - return tmp; -} - -static MEdge *dm_dupEdgeArray(DerivedMesh *dm) -{ - MEdge *tmp = (MEdge *)MEM_malloc_arrayN( - dm->getNumEdges(dm), sizeof(*tmp), "dm_dupEdgeArray tmp"); - - if (tmp) { - dm->copyEdgeArray(dm, tmp); - } - - return tmp; -} - -static MLoop *dm_dupLoopArray(DerivedMesh *dm) -{ - MLoop *tmp = (MLoop *)MEM_malloc_arrayN( - dm->getNumLoops(dm), sizeof(*tmp), "dm_dupLoopArray tmp"); - - if (tmp) { - dm->copyLoopArray(dm, tmp); - } - - return tmp; -} - -static MPoly *dm_dupPolyArray(DerivedMesh *dm) -{ - MPoly *tmp = (MPoly *)MEM_malloc_arrayN( - dm->getNumPolys(dm), sizeof(*tmp), "dm_dupPolyArray tmp"); - - if (tmp) { - dm->copyPolyArray(dm, tmp); - } - - return tmp; -} - static int dm_getNumLoopTri(DerivedMesh *dm) { const int numlooptris = poly_to_tri_count(dm->getNumPolys(dm), dm->getNumLoops(dm)); @@ -233,10 +185,6 @@ void DM_init_funcs(DerivedMesh *dm) dm->getEdgeArray = dm_getEdgeArray; dm->getLoopArray = dm_getLoopArray; dm->getPolyArray = dm_getPolyArray; - dm->dupVertArray = dm_dupVertArray; - dm->dupEdgeArray = dm_dupEdgeArray; - dm->dupLoopArray = dm_dupLoopArray; - dm->dupPolyArray = dm_dupPolyArray; dm->getLoopTriArray = dm_getLoopTriArray; @@ -331,36 +279,6 @@ bool DM_release(DerivedMesh *dm) return false; } -void DM_DupPolys(DerivedMesh *source, DerivedMesh *target) -{ - CustomData_free(&target->loopData, source->numLoopData); - CustomData_free(&target->polyData, source->numPolyData); - - CustomData_copy(&source->loopData, - &target->loopData, - CD_MASK_DERIVEDMESH.lmask, - CD_DUPLICATE, - source->numLoopData); - CustomData_copy(&source->polyData, - &target->polyData, - CD_MASK_DERIVEDMESH.pmask, - CD_DUPLICATE, - source->numPolyData); - - target->numLoopData = source->numLoopData; - target->numPolyData = source->numPolyData; - - if (!CustomData_has_layer(&target->polyData, CD_MPOLY)) { - MPoly *mpoly; - MLoop *mloop; - - mloop = source->dupLoopArray(source); - mpoly = source->dupPolyArray(source); - CustomData_add_layer(&target->loopData, CD_MLOOP, CD_ASSIGN, mloop, source->numLoopData); - CustomData_add_layer(&target->polyData, CD_MPOLY, CD_ASSIGN, mpoly, source->numPolyData); - } -} - void DM_ensure_looptri_data(DerivedMesh *dm) { const unsigned int totpoly = dm->numPolyData; @@ -826,7 +744,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, mesh_final = BKE_mesh_copy_for_eval(mesh_input, true); ASSERT_IS_VALID_MESH(mesh_final); } - MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh_final); + MutableAttributeAccessor attributes = mesh_final->attributes_for_write(); SpanAttributeWriter<float3> rest_positions = attributes.lookup_or_add_for_write_only_span<float3>("rest_position", ATTR_DOMAIN_POINT); if (rest_positions) { @@ -1806,7 +1724,7 @@ void makeDerivedMesh(struct Depsgraph *depsgraph, BKE_object_free_derived_caches(ob); if (DEG_is_active(depsgraph)) { - BKE_sculpt_update_object_before_eval(scene, ob); + BKE_sculpt_update_object_before_eval(ob); } /* NOTE: Access the `edit_mesh` after freeing the derived caches, so that `ob->data` is restored diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index c16d19588ed..e0ae1d88760 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -315,7 +315,7 @@ IDTypeInfo IDType_ID_AC = { .foreach_id = action_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = action_blend_write, .blend_read_data = action_blend_read_data, diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 031d3647878..96ac81fdb63 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -371,14 +371,16 @@ static bool get_path_local_ex(char *targetpath, relfolder[0] = '\0'; } - /* Try `{g_app.program_dirname}/2.xx/{folder_name}` the default directory + /* Try `{g_app.program_dirname}/3.xx/{folder_name}` the default directory * for a portable distribution. See `WITH_INSTALL_PORTABLE` build-option. */ const char *path_base = g_app.program_dirname; -#ifdef __APPLE__ +#if defined(__APPLE__) && !defined(WITH_PYTHON_MODULE) /* Due new code-sign situation in OSX > 10.9.5 - * we must move the blender_version dir with contents to Resources. */ - char osx_resourses[FILE_MAX]; - BLI_snprintf(osx_resourses, sizeof(osx_resourses), "%s../Resources", g_app.program_dirname); + * we must move the blender_version dir with contents to Resources. + * Add 4 + 9 for the temporary `/../` path & `Resources`. */ + char osx_resourses[FILE_MAX + 4 + 9]; + BLI_path_join( + osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources", NULL); /* Remove the '/../' added above. */ BLI_path_normalize(NULL, osx_resourses); path_base = osx_resourses; @@ -734,6 +736,7 @@ const char *BKE_appdir_folder_id_create(const int folder_id, const char *subfold BLENDER_USER_CONFIG, BLENDER_USER_SCRIPTS, BLENDER_USER_AUTOSAVE)) { + BLI_assert_unreachable(); return NULL; } @@ -782,6 +785,7 @@ const char *BKE_appdir_folder_id_version(const int folder_id, * Access locations of Blender & Python. * \{ */ +#ifndef WITH_PYTHON_MODULE /** * Checks if name is a fully qualified filename to an executable. * If not it searches `$PATH` for the file. On Windows it also @@ -796,7 +800,7 @@ const char *BKE_appdir_folder_id_version(const int folder_id, */ static void where_am_i(char *fullname, const size_t maxlen, const char *name) { -#ifdef WITH_BINRELOC +# ifdef WITH_BINRELOC /* Linux uses `binreloc` since `argv[0]` is not reliable, call `br_init(NULL)` first. */ { const char *path = NULL; @@ -807,9 +811,9 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name) return; } } -#endif +# endif -#ifdef _WIN32 +# ifdef _WIN32 { wchar_t *fullname_16 = MEM_mallocN(maxlen * sizeof(wchar_t), "ProgramPath"); if (GetModuleFileNameW(0, fullname_16, maxlen)) { @@ -825,7 +829,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name) MEM_freeN(fullname_16); } -#endif +# endif /* Unix and non Linux. */ if (name && name[0]) { @@ -833,16 +837,16 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name) BLI_strncpy(fullname, name, maxlen); if (name[0] == '.') { BLI_path_abs_from_cwd(fullname, maxlen); -#ifdef _WIN32 +# ifdef _WIN32 BLI_path_program_extensions_add_win32(fullname, maxlen); -#endif +# endif } else if (BLI_path_slash_rfind(name)) { /* Full path. */ BLI_strncpy(fullname, name, maxlen); -#ifdef _WIN32 +# ifdef _WIN32 BLI_path_program_extensions_add_win32(fullname, maxlen); -#endif +# endif } else { BLI_path_program_search(fullname, maxlen, name); @@ -850,23 +854,43 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name) /* Remove "/./" and "/../" so string comparisons can be used on the path. */ BLI_path_normalize(NULL, fullname); -#if defined(DEBUG) +# if defined(DEBUG) if (!STREQ(name, fullname)) { CLOG_INFO(&LOG, 2, "guessing '%s' == '%s'", name, fullname); } -#endif +# endif } } +#endif /* WITH_PYTHON_MODULE */ void BKE_appdir_program_path_init(const char *argv0) { +#ifdef WITH_PYTHON_MODULE + /* NOTE(@campbellbarton): Always use `argv[0]` as is, when building as a Python module. + * Otherwise other methods of detecting the binary that override this argument + * which must point to the Python module for data-files to be detected. */ + STRNCPY(g_app.program_filepath, argv0); + BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath)); + BLI_path_normalize(NULL, g_app.program_filepath); + + if (g_app.program_dirname[0] == '\0') { + /* First time initializing, the file binary path isn't valid from a Python module. + * Calling again must set the `filepath` and leave the directory as-is. */ + BLI_split_dir_part( + g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname)); + g_app.program_filepath[0] = '\0'; + } +#else where_am_i(g_app.program_filepath, sizeof(g_app.program_filepath), argv0); BLI_split_dir_part(g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname)); +#endif } const char *BKE_appdir_program_path(void) { +#ifndef WITH_PYTHON_MODULE /* Default's to empty when building as as Python module. */ BLI_assert(g_app.program_filepath[0]); +#endif return g_app.program_filepath; } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 7be3fe6f0e1..0027f6dd707 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -313,7 +313,7 @@ IDTypeInfo IDType_ID_AR = { .foreach_id = armature_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = armature_blend_write, .blend_read_data = armature_blend_read_data, diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index 941003d6c96..f66a1f9ee93 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -103,11 +103,11 @@ static std::optional<blender::bke::MutableAttributeAccessor> get_attribute_acces Mesh &mesh = reinterpret_cast<Mesh &>(id); /* The attribute API isn't implemented for BMesh, so edit mode meshes are not supported. */ BLI_assert(mesh.edit_mesh == nullptr); - return mesh_attributes_for_write(mesh); + return mesh.attributes_for_write(); } case ID_PT: { PointCloud &pointcloud = reinterpret_cast<PointCloud &>(id); - return pointcloud_attributes_for_write(pointcloud); + return pointcloud.attributes_for_write(); } case ID_CV: { Curves &curves_id = reinterpret_cast<Curves &>(id); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 6ca3a286a5e..1e237da8119 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -14,6 +14,7 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BLI_array_utils.hh" #include "BLI_color.hh" #include "BLI_math_vec_types.hh" #include "BLI_span.hh" @@ -726,8 +727,22 @@ bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id) void CustomDataAttributes::reallocate(const int size) { + const int old_size = size_; size_ = size; - CustomData_realloc(&data, size); + CustomData_realloc(&data, old_size, size_); + if (size_ > old_size) { + /* Fill default new values. */ + const int new_elements_num = size_ - old_size; + this->foreach_attribute( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) { + GMutableSpan new_data = this->get_for_write(id)->take_back(new_elements_num); + const CPPType &type = new_data.type(); + type.fill_assign_n(type.default_value(), new_data.data(), new_data.size()); + return true; + }, + /* Dummy. */ + ATTR_DOMAIN_POINT); + } } void CustomDataAttributes::clear() @@ -875,6 +890,16 @@ GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef return attribute; } +GSpanAttributeWriter MutableAttributeAccessor::lookup_for_write_span( + const AttributeIDRef &attribute_id) +{ + GAttributeWriter attribute = this->lookup_for_write(attribute_id); + if (attribute) { + return GSpanAttributeWriter{std::move(attribute), true}; + } + return {}; +} + GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write( const AttributeIDRef &attribute_id, const eAttrDomain domain, @@ -950,6 +975,37 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer( return attributes; } +void copy_attribute_domain(const AttributeAccessor src_attributes, + MutableAttributeAccessor dst_attributes, + const IndexMask selection, + const eAttrDomain domain, + const Set<std::string> &skip) +{ + src_attributes.for_all( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { + if (meta_data.domain != domain) { + return true; + } + if (id.is_named() && skip.contains(id.name())) { + return true; + } + if (!id.should_be_kept()) { + return true; + } + + const GVArray src = src_attributes.lookup(id, meta_data.domain); + BLI_assert(src); + + /* Copy attribute. */ + GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( + id, domain, meta_data.data_type); + array_utils::copy(src, selection, dst.span); + dst.finish(); + + return true; + }); +} + } // namespace blender::bke /** \} */ diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 34b87dda338..c206a04fecc 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -413,7 +413,7 @@ IDTypeInfo IDType_ID_BR = { /* foreach_id */ brush_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ brush_foreach_path, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ brush_blend_write, /* blend_read_data */ brush_blend_read_data, diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 1d8b53a28ba..9bea8a0d6d3 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -1294,7 +1294,7 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, break; case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: { - blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*mesh); + blender::bke::AttributeAccessor attributes = mesh->attributes(); mask = looptri_no_hidden_map_get( mesh->polys().data(), attributes.lookup_or_default(".hide_poly", ATTR_DOMAIN_FACE, false), @@ -1454,7 +1454,7 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data, return nullptr; } - blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(*pointcloud); + blender::bke::AttributeAccessor attributes = pointcloud->attributes(); blender::VArraySpan<blender::float3> positions = attributes.lookup_or_default<blender::float3>( "position", ATTR_DOMAIN_POINT, blender::float3(0)); diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index fd83ac50cad..5d19db323f8 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -146,7 +146,7 @@ IDTypeInfo IDType_ID_CF = { .foreach_id = NULL, .foreach_cache = NULL, .foreach_path = cache_file_foreach_path, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = cache_file_blend_write, .blend_read_data = cache_file_blend_read_data, diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 9aea3b2768f..158e0bb776c 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -186,7 +186,7 @@ IDTypeInfo IDType_ID_CA = { .foreach_id = camera_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = camera_blend_write, .blend_read_data = camera_blend_read_data, diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 93286751f92..0261b2d7674 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -244,43 +244,3 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh) { return cdDM_from_mesh_ex(mesh, CD_REFERENCE, &CD_MASK_MESH); } - -DerivedMesh *CDDM_copy(DerivedMesh *source) -{ - CDDerivedMesh *cddm = cdDM_create("CDDM_copy cddm"); - DerivedMesh *dm = &cddm->dm; - int numVerts = source->numVertData; - int numEdges = source->numEdgeData; - int numTessFaces = 0; - int numLoops = source->numLoopData; - int numPolys = source->numPolyData; - - /* NOTE: Don't copy tessellation faces if not requested explicitly. */ - - /* ensure these are created if they are made on demand */ - source->getVertDataArray(source, CD_ORIGINDEX); - source->getEdgeDataArray(source, CD_ORIGINDEX); - source->getPolyDataArray(source, CD_ORIGINDEX); - - /* this initializes dm, and copies all non mvert/medge/mface layers */ - DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys); - dm->deformedOnly = source->deformedOnly; - dm->cd_flag = source->cd_flag; - - CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts); - CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges); - - /* now add mvert/medge/mface layers */ - cddm->mvert = source->dupVertArray(source); - cddm->medge = source->dupEdgeArray(source); - - CustomData_add_layer(&dm->vertData, CD_MVERT, CD_ASSIGN, cddm->mvert, numVerts); - CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_ASSIGN, cddm->medge, numEdges); - - DM_DupPolys(source, dm); - - cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP); - cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); - - return dm; -} diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 69fe4c17657..98b1e3d0039 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -169,10 +169,10 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) } } -static ID *collection_owner_get(ID *id) +static ID **collection_owner_pointer_get(ID *id) { if ((id->flag & LIB_EMBEDDED_DATA) == 0) { - return id; + return NULL; } BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0); @@ -182,7 +182,7 @@ static ID *collection_owner_get(ID *id) BLI_assert(GS(master_collection->owner_id->name) == ID_SCE); BLI_assert(((Scene *)master_collection->owner_id)->master_collection == master_collection); - return master_collection->owner_id; + return &master_collection->owner_id; } void BKE_collection_blend_write_nolib(BlendWriter *writer, Collection *collection) @@ -234,8 +234,13 @@ void BKE_collection_compat_blend_read_data(BlendDataReader *reader, SceneCollect void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collection, ID *owner_id) { /* Special case for this pointer, do not rely on regular `lib_link` process here. Avoids needs - * for do_versioning, and ensures coherence of data in any case. */ - BLI_assert((collection->id.flag & LIB_EMBEDDED_DATA) != 0 || owner_id == NULL); + * for do_versioning, and ensures coherence of data in any case. + * + * NOTE: Old versions are very often 'broken' here, just fix it silently in these cases. + */ + if (BLO_read_fileversion_get(reader) > 300) { + BLI_assert((collection->id.flag & LIB_EMBEDDED_DATA) != 0 || owner_id == NULL); + } BLI_assert(owner_id == NULL || owner_id->lib == collection->id.lib); if (owner_id != NULL && (collection->id.flag & LIB_EMBEDDED_DATA) == 0) { /* This is unfortunate, but currently a lot of existing files (including startup ones) have @@ -244,11 +249,13 @@ void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collect * NOTE: Using do_version is not a solution here, since this code will be called before any * do_version takes place. Keeping it here also ensures future (or unknown existing) similar * bugs won't go easily unnoticed. */ - CLOG_WARN(&LOG, - "Fixing root node tree '%s' owned by '%s' missing EMBEDDED tag, please consider " - "re-saving your (startup) file", - collection->id.name, - owner_id->name); + if (BLO_read_fileversion_get(reader) > 300) { + CLOG_WARN(&LOG, + "Fixing root node tree '%s' owned by '%s' missing EMBEDDED tag, please consider " + "re-saving your (startup) file", + collection->id.name, + owner_id->name); + } collection->id.flag |= LIB_EMBEDDED_DATA; } collection->owner_id = owner_id; @@ -393,7 +400,7 @@ IDTypeInfo IDType_ID_GR = { .foreach_id = collection_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = collection_owner_get, + .owner_pointer_get = collection_owner_pointer_get, .blend_write = collection_blend_write, .blend_read_data = collection_blend_read_data, diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 01c6aea8024..0bacd657981 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -1245,10 +1245,10 @@ static void add_collision_object(ListBase *relations, } ListBase *BKE_collision_relations_create(Depsgraph *depsgraph, - const Scene *scene, Collection *collection, unsigned int modifier_type) { + const Scene *scene = DEG_get_input_scene(depsgraph); ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); Base *base = BKE_collection_or_layer_objects(scene, view_layer, collection); const bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); diff --git a/source/blender/blenkernel/intern/compute_contexts.cc b/source/blender/blenkernel/intern/compute_contexts.cc new file mode 100644 index 00000000000..026706d363e --- /dev/null +++ b/source/blender/blenkernel/intern/compute_contexts.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_compute_contexts.hh" + +namespace blender::bke { + +ModifierComputeContext::ModifierComputeContext(const ComputeContext *parent, + std::string modifier_name) + : ComputeContext(s_static_type, parent), modifier_name_(std::move(modifier_name)) +{ + hash_.mix_in(s_static_type, strlen(s_static_type)); + hash_.mix_in(modifier_name_.data(), modifier_name_.size()); +} + +void ModifierComputeContext::print_current_in_line(std::ostream &stream) const +{ + stream << "Modifier: " << modifier_name_; +} + +NodeGroupComputeContext::NodeGroupComputeContext(const ComputeContext *parent, + std::string node_name) + : ComputeContext(s_static_type, parent), node_name_(std::move(node_name)) +{ + hash_.mix_in(s_static_type, strlen(s_static_type)); + hash_.mix_in(node_name_.data(), node_name_.size()); +} + +StringRefNull NodeGroupComputeContext::node_name() const +{ + return node_name_; +} + +void NodeGroupComputeContext::print_current_in_line(std::ostream &stream) const +{ + stream << "Node: " << node_name_; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc index 102bda0f2b6..72204f6624e 100644 --- a/source/blender/blenkernel/intern/cryptomatte.cc +++ b/source/blender/blenkernel/intern/cryptomatte.cc @@ -41,7 +41,9 @@ struct CryptomatteSession { CryptomatteSession() = default; CryptomatteSession(const Main *bmain); CryptomatteSession(StampData *stamp_data); + CryptomatteSession(const ViewLayer *view_layer); CryptomatteSession(const Scene *scene); + void init(const ViewLayer *view_layer); blender::bke::cryptomatte::CryptomatteLayer &add_layer(std::string layer_name); std::optional<std::string> operator[](float encoded_hash) const; @@ -54,13 +56,15 @@ struct CryptomatteSession { CryptomatteSession::CryptomatteSession(const Main *bmain) { if (!BLI_listbase_is_empty(&bmain->objects)) { - blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer("CryptoObject"); + blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer( + RE_PASSNAME_CRYPTOMATTE_OBJECT); LISTBASE_FOREACH (ID *, id, &bmain->objects) { objects.add_ID(*id); } } if (!BLI_listbase_is_empty(&bmain->materials)) { - blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer("CryptoMaterial"); + blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer( + RE_PASSNAME_CRYPTOMATTE_MATERIAL); LISTBASE_FOREACH (ID *, id, &bmain->materials) { materials.add_ID(*id); } @@ -83,24 +87,34 @@ CryptomatteSession::CryptomatteSession(StampData *stamp_data) false); } +CryptomatteSession::CryptomatteSession(const ViewLayer *view_layer) +{ + init(view_layer); +} + CryptomatteSession::CryptomatteSession(const Scene *scene) { - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>( - view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL); - if (cryptoflags == 0) { - cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL); - } + LISTBASE_FOREACH (const ViewLayer *, view_layer, &scene->view_layers) { + init(view_layer); + } +} - if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) { - add_layer(blender::StringRefNull(view_layer->name) + ".CryptoObject"); - } - if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) { - add_layer(blender::StringRefNull(view_layer->name) + ".CryptoAsset"); - } - if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) { - add_layer(blender::StringRefNull(view_layer->name) + ".CryptoMaterial"); - } +void CryptomatteSession::init(const ViewLayer *view_layer) +{ + eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>( + view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL); + if (cryptoflags == 0) { + cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL); + } + + if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) { + add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_OBJECT); + } + if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) { + add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_ASSET); + } + if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) { + add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_MATERIAL); } } @@ -142,6 +156,12 @@ struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *s return session; } +struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer(const struct ViewLayer *view_layer) +{ + CryptomatteSession *session = new CryptomatteSession(view_layer); + return session; +} + void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name) { session->add_layer(layer_name); @@ -485,11 +505,6 @@ CryptomatteHash::CryptomatteHash(uint32_t hash) : hash(hash) { } -CryptomatteHash::CryptomatteHash(const char *name, const int name_len) -{ - hash = BLI_hash_mm3((const unsigned char *)name, name_len, 0); -} - CryptomatteHash CryptomatteHash::from_hex_encoded(blender::StringRef hex_encoded) { CryptomatteHash result(0); @@ -504,21 +519,6 @@ std::string CryptomatteHash::hex_encoded() const return encoded.str(); } -float CryptomatteHash::float_encoded() const -{ - uint32_t mantissa = hash & ((1 << 23) - 1); - uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); - exponent = MAX2(exponent, (uint32_t)1); - exponent = MIN2(exponent, (uint32_t)254); - exponent = exponent << 23; - uint32_t sign = (hash >> 31); - sign = sign << 31; - uint32_t float_bits = sign | exponent | mantissa; - float f; - memcpy(&f, &float_bits, sizeof(uint32_t)); - return f; -} - std::unique_ptr<CryptomatteLayer> CryptomatteLayer::read_from_manifest( blender::StringRefNull manifest) { @@ -625,4 +625,9 @@ const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get( return session.layer_names; } +CryptomatteLayer *BKE_cryptomatte_layer_get(CryptomatteSession &session, StringRef layer_name) +{ + return session.layers.lookup_ptr(layer_name); +} + } // namespace blender::bke::cryptomatte diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 40b64aa8dc8..aebdb8cc690 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -321,7 +321,7 @@ IDTypeInfo IDType_ID_CU_LEGACY = { /* foreach_id */ curve_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ nullptr, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ curve_blend_write, /* blend_read_data */ curve_blend_read_data, diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc index 952d59edcf9..dac88948036 100644 --- a/source/blender/blenkernel/intern/curve_catmull_rom.cc +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -17,16 +17,14 @@ int calculate_evaluated_num(const int points_num, const bool cyclic, const int r } /* 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) +void calculate_basis(const float parameter, float r_weights[4]) { 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); + r_weights[0] = -t * s * s; + r_weights[1] = 2.0f + t * t * (3.0f * t - 5.0f); + r_weights[2] = 2.0f + s * s * (3.0f * s - 5.0f); + r_weights[3] = -s * t * t; } template<typename T> @@ -35,7 +33,7 @@ static void evaluate_segment(const T &a, const T &b, const T &c, const T &d, Mut 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); + dst[i] = interpolate<T>(a, b, c, d, i * step); } } diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 8be7cec1b04..b9fea2a27b8 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -711,7 +711,7 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, Set<AttributeIDRef> main_attributes_set; - MutableAttributeAccessor mesh_attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write(); main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id)) { diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index c6e7bb72f53..e729fed050b 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -216,7 +216,7 @@ IDTypeInfo IDType_ID_CV = { /* foreach_id */ curves_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ nullptr, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ curves_blend_write, /* blend_read_data */ curves_blend_read_data, diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 35b209179d3..06789e34ad4 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -963,11 +963,11 @@ void CurvesGeometry::ensure_can_interpolate_to_evaluated() const void CurvesGeometry::resize(const int points_num, const int curves_num) { if (points_num != this->point_num) { - CustomData_realloc(&this->point_data, points_num); + CustomData_realloc(&this->point_data, this->points_num(), points_num); this->point_num = points_num; } if (curves_num != this->curve_num) { - CustomData_realloc(&this->curve_data, curves_num); + CustomData_realloc(&this->curve_data, this->curves_num(), curves_num); this->curve_num = curves_num; this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } @@ -1380,69 +1380,49 @@ static void reverse_swap_curve_point_data(const CurvesGeometry &curves, }); } -static bool layer_matches_name_and_type(const CustomDataLayer &layer, - const StringRef name, - const eCustomDataType 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()); + Set<StringRef> bezier_handle_names{{ATTR_HANDLE_POSITION_LEFT, + ATTR_HANDLE_POSITION_RIGHT, + ATTR_HANDLE_TYPE_LEFT, + ATTR_HANDLE_TYPE_RIGHT}}; - /* 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; + MutableAttributeAccessor attributes = this->attributes_for_write(); - 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; + attributes.for_all([&](const AttributeIDRef &id, AttributeMetaData meta_data) { + if (meta_data.domain != ATTR_DOMAIN_POINT) { + return true; } - 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; + if (id.is_named() && bezier_handle_names.contains(id.name())) { + return true; } - const eCustomDataType data_type = static_cast<eCustomDataType>(layer.type); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id); + attribute_math::convert_to_static_type(attribute.span.type(), [&](auto dummy) { using T = decltype(dummy); - reverse_curve_point_data<T>( - *this, curves_to_reverse, {static_cast<T *>(layer.data), this->points_num()}); + reverse_curve_point_data<T>(*this, curves_to_reverse, attribute.span.typed<T>()); }); - } + attribute.finish(); + return true; + }); /* 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 (attributes.contains(ATTR_HANDLE_POSITION_LEFT) && + attributes.contains(ATTR_HANDLE_POSITION_RIGHT)) { + reverse_swap_curve_point_data(*this, + curves_to_reverse, + this->handle_positions_left_for_write(), + this->handle_positions_right_for_write()); } - if (!types_left.is_empty() && !types_right.is_empty()) { - reverse_swap_curve_point_data(*this, curves_to_reverse, types_left, types_right); + if (attributes.contains(ATTR_HANDLE_TYPE_LEFT) && attributes.contains(ATTR_HANDLE_TYPE_RIGHT)) { + reverse_swap_curve_point_data(*this, + curves_to_reverse, + this->handle_types_left_for_write(), + this->handle_types_right_for_write()); } this->tag_topology_changed(); @@ -1450,21 +1430,20 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse) void CurvesGeometry::remove_attributes_based_on_types() { - const int points_num = this->points_num(); - const int curves_num = this->curves_num(); + MutableAttributeAccessor attributes = this->attributes_for_write(); if (!this->has_curve_with_type(CURVE_TYPE_BEZIER)) { - CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_LEFT.c_str(), points_num); - CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_RIGHT.c_str(), points_num); - CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_LEFT.c_str(), points_num); - CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_RIGHT.c_str(), points_num); + attributes.remove(ATTR_HANDLE_TYPE_LEFT); + attributes.remove(ATTR_HANDLE_TYPE_RIGHT); + attributes.remove(ATTR_HANDLE_POSITION_LEFT); + attributes.remove(ATTR_HANDLE_POSITION_RIGHT); } if (!this->has_curve_with_type(CURVE_TYPE_NURBS)) { - CustomData_free_layer_named(&this->point_data, ATTR_NURBS_WEIGHT.c_str(), points_num); - CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_ORDER.c_str(), curves_num); - CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_KNOTS_MODE.c_str(), curves_num); + attributes.remove(ATTR_NURBS_WEIGHT); + attributes.remove(ATTR_NURBS_ORDER); + attributes.remove(ATTR_NURBS_KNOTS_MODE); } if (!this->has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_NURBS})) { - CustomData_free_layer_named(&this->curve_data, ATTR_RESOLUTION.c_str(), curves_num); + attributes.remove(ATTR_RESOLUTION); } } diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 12425a67e70..82a1a2aa8f6 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1867,7 +1867,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 28: CD_SHAPEKEY */ {sizeof(float[3]), "", 0, N_("ShapeKey"), nullptr, nullptr, layerInterp_shapekey}, /* 29: CD_BWEIGHT */ - {sizeof(float), "", 0, N_("BevelWeight"), nullptr, nullptr, layerInterp_bweight}, + {sizeof(MFloatProperty), "MFloatProperty", 1, nullptr, nullptr, nullptr, layerInterp_bweight}, /* 30: CD_CREASE */ /* NOTE: we do not interpolate crease data as it should be either inherited for subdivided * edges, or for vertex creases, only present on the original vertex. */ @@ -2108,23 +2108,23 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { }; const CustomData_MeshMasks CD_MASK_BAREMESH = { - /* vmask */ CD_MASK_MVERT | CD_MASK_BWEIGHT, - /* emask */ CD_MASK_MEDGE | CD_MASK_BWEIGHT, + /* vmask */ CD_MASK_MVERT, + /* emask */ CD_MASK_MEDGE, /* fmask */ 0, /* pmask */ CD_MASK_MPOLY | CD_MASK_FACEMAP, /* lmask */ CD_MASK_MLOOP, }; const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = { - /* vmask */ CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX, - /* emask */ CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX, + /* vmask */ CD_MASK_MVERT | CD_MASK_ORIGINDEX, + /* emask */ CD_MASK_MEDGE | CD_MASK_ORIGINDEX, /* fmask */ 0, /* pmask */ CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX, /* lmask */ CD_MASK_MLOOP, }; const CustomData_MeshMasks CD_MASK_MESH = { /* vmask */ (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK | - CD_MASK_PROP_ALL | CD_MASK_CREASE), - /* emask */ (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), + CD_MASK_PROP_ALL | CD_MASK_CREASE | CD_MASK_BWEIGHT), + /* emask */ (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_BWEIGHT), /* fmask */ 0, /* pmask */ (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | @@ -2136,8 +2136,8 @@ const CustomData_MeshMasks CD_MASK_MESH = { const CustomData_MeshMasks CD_MASK_DERIVEDMESH = { /* vmask */ (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL | - CD_MASK_CREASE), - /* emask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), + CD_MASK_CREASE | CD_MASK_BWEIGHT), + /* emask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_BWEIGHT | CD_MASK_PROP_ALL), /* fmask */ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT), /* pmask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL | @@ -2353,8 +2353,16 @@ bool CustomData_merge(const CustomData *source, changed = true; if (layer->anonymous_id != nullptr) { - BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id); newlayer->anonymous_id = layer->anonymous_id; + if (alloctype == CD_ASSIGN) { + layer->anonymous_id = nullptr; + } + else { + BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id); + } + } + if (alloctype == CD_ASSIGN) { + layer->data = nullptr; } } } @@ -2400,19 +2408,37 @@ bool CustomData_merge_mesh_to_bmesh(const CustomData *source, return result; } -void CustomData_realloc(CustomData *data, const int totelem) +void CustomData_realloc(CustomData *data, const int old_size, const int new_size) { - BLI_assert(totelem >= 0); + BLI_assert(new_size >= 0); for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; - const LayerTypeInfo *typeInfo; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + + const int64_t old_size_in_bytes = int64_t(old_size) * typeInfo->size; + const int64_t new_size_in_bytes = int64_t(new_size) * typeInfo->size; if (layer->flag & CD_FLAG_NOFREE) { - continue; + const void *old_data = layer->data; + layer->data = MEM_malloc_arrayN(new_size, typeInfo->size, __func__); + if (typeInfo->copy) { + typeInfo->copy(old_data, layer->data, std::min(old_size, new_size)); + } + else { + std::memcpy(layer->data, old_data, std::min(old_size_in_bytes, new_size_in_bytes)); + } + layer->flag &= ~CD_FLAG_NOFREE; + } + else { + layer->data = MEM_reallocN(layer->data, new_size_in_bytes); + } + + if (new_size > old_size) { + /* Initialize new values for non-trivial types. */ + if (typeInfo->construct) { + const int new_elements_num = new_size - old_size; + typeInfo->construct(POINTER_OFFSET(layer->data, old_size_in_bytes), new_elements_num); + } } - typeInfo = layerType_getInfo(layer->type); - /* Use calloc to avoid the need to manually initialize new data in layers. - * Useful for types like #MDeformVert which contain a pointer. */ - layer->data = MEM_recallocN(layer->data, (size_t)totelem * typeInfo->size); } } @@ -5167,7 +5193,7 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, else { const LayerTypeInfo *type_info = layerType_getInfo(data_type); - /* NOTE: we can use 'fake' CDLayers, like e.g. for crease, bweight, etc. :/. */ + /* NOTE: we can use 'fake' CDLayers for crease :/. */ data_size = (size_t)type_info->size; data_step = laymap->elem_size ? laymap->elem_size : data_size; data_offset = laymap->data_offset; @@ -5404,6 +5430,9 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int else if (layer->type == CD_GRID_PAINT_MASK) { blend_read_paint_mask(reader, count, static_cast<GridPaintMask *>(layer->data)); } + else if (layer->type == CD_MDEFORMVERT) { + BKE_defvert_blend_read(reader, count, static_cast<MDeformVert *>(layer->data)); + } i++; } } diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 6fbdade08f8..6c7715c625e 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -192,7 +192,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) case DT_TYPE_SKIN: return CD_MVERT_SKIN; case DT_TYPE_BWEIGHT_VERT: - return CD_FAKE_BWEIGHT; + return CD_BWEIGHT; case DT_TYPE_SHARP_EDGE: return CD_FAKE_SHARP; @@ -201,7 +201,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) case DT_TYPE_CREASE: return CD_FAKE_CREASE; case DT_TYPE_BWEIGHT_EDGE: - return CD_FAKE_BWEIGHT; + return CD_BWEIGHT; case DT_TYPE_FREESTYLE_EDGE: return CD_FREESTYLE_EDGE; @@ -928,38 +928,6 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map, } return true; } - if (cddata_type == CD_FAKE_BWEIGHT) { - const size_t elem_size = sizeof(*((MVert *)NULL)); - const size_t data_size = sizeof(((MVert *)NULL)->bweight); - const size_t data_offset = offsetof(MVert, bweight); - const uint64_t data_flag = 0; - - if (!(me_src->cd_flag & ME_CDFLAG_VERT_BWEIGHT)) { - if (use_delete) { - me_dst->cd_flag &= ~ME_CDFLAG_VERT_BWEIGHT; - } - return true; - } - me_dst->cd_flag |= ME_CDFLAG_VERT_BWEIGHT; - if (r_map) { - data_transfer_layersmapping_add_item(r_map, - cddata_type, - mix_mode, - mix_factor, - mix_weights, - BKE_mesh_verts(me_src), - BKE_mesh_verts_for_write(me_dst), - me_src->totvert, - me_dst->totvert, - elem_size, - data_size, - data_offset, - data_flag, - data_transfer_interp_char, - interp_data); - } - return true; - } if (cddata_type == CD_FAKE_MDEFORMVERT) { bool ret; @@ -1045,38 +1013,7 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map, } return true; } - if (cddata_type == CD_FAKE_BWEIGHT) { - const size_t elem_size = sizeof(*((MEdge *)NULL)); - const size_t data_size = sizeof(((MEdge *)NULL)->bweight); - const size_t data_offset = offsetof(MEdge, bweight); - const uint64_t data_flag = 0; - if (!(me_src->cd_flag & ME_CDFLAG_EDGE_BWEIGHT)) { - if (use_delete) { - me_dst->cd_flag &= ~ME_CDFLAG_EDGE_BWEIGHT; - } - return true; - } - me_dst->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; - if (r_map) { - data_transfer_layersmapping_add_item(r_map, - cddata_type, - mix_mode, - mix_factor, - mix_weights, - BKE_mesh_edges(me_src), - BKE_mesh_edges_for_write(me_dst), - me_src->totedge, - me_dst->totedge, - elem_size, - data_size, - data_offset, - data_flag, - data_transfer_interp_char, - interp_data); - } - return true; - } if (r_map && ELEM(cddata_type, CD_FAKE_SHARP, CD_FAKE_SEAM)) { const size_t elem_size = sizeof(*((MEdge *)NULL)); const size_t data_size = sizeof(((MEdge *)NULL)->flag); diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index f928079f3ea..7940d65b1bb 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -94,10 +94,10 @@ bDeformGroup *BKE_defgroup_duplicate(const bDeformGroup *ingroup) void BKE_defvert_copy_subset(MDeformVert *dvert_dst, const MDeformVert *dvert_src, const bool *vgroup_subset, - const int vgroup_tot) + const int vgroup_num) { int defgroup; - for (defgroup = 0; defgroup < vgroup_tot; defgroup++) { + for (defgroup = 0; defgroup < vgroup_num; defgroup++) { if (vgroup_subset[defgroup]) { BKE_defvert_copy_index(dvert_dst, defgroup, dvert_src, defgroup); } @@ -107,12 +107,12 @@ void BKE_defvert_copy_subset(MDeformVert *dvert_dst, void BKE_defvert_mirror_subset(MDeformVert *dvert_dst, const MDeformVert *dvert_src, const bool *vgroup_subset, - const int vgroup_tot, + const int vgroup_num, const int *flip_map, - const int flip_map_len) + const int flip_map_num) { int defgroup; - for (defgroup = 0; defgroup < vgroup_tot && defgroup < flip_map_len; defgroup++) { + for (defgroup = 0; defgroup < vgroup_num && defgroup < flip_map_num; defgroup++) { if (vgroup_subset[defgroup] && (dvert_dst != dvert_src || flip_map[defgroup] != defgroup)) { BKE_defvert_copy_index(dvert_dst, flip_map[defgroup], dvert_src, defgroup); } @@ -189,13 +189,13 @@ void BKE_defvert_sync(MDeformVert *dvert_dst, const MDeformVert *dvert_src, cons void BKE_defvert_sync_mapped(MDeformVert *dvert_dst, const MDeformVert *dvert_src, const int *flip_map, - const int flip_map_len, + const int flip_map_num, const bool use_ensure) { if (dvert_src->totweight && dvert_dst->totweight) { MDeformWeight *dw_src = dvert_src->dw; for (int i = 0; i < dvert_src->totweight; i++, dw_src++) { - if (dw_src->def_nr < flip_map_len) { + if (dw_src->def_nr < flip_map_num) { MDeformWeight *dw_dst; if (use_ensure) { dw_dst = BKE_defvert_ensure_index(dvert_dst, flip_map[dw_src->def_nr]); @@ -226,14 +226,14 @@ void BKE_defvert_remap(MDeformVert *dvert, const int *map, const int map_len) void BKE_defvert_normalize_subset(MDeformVert *dvert, const bool *vgroup_subset, - const int vgroup_tot) + const int vgroup_num) { if (dvert->totweight == 0) { /* nothing */ } else if (dvert->totweight == 1) { MDeformWeight *dw = dvert->dw; - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { dw->weight = 1.0f; } } @@ -241,7 +241,7 @@ void BKE_defvert_normalize_subset(MDeformVert *dvert, MDeformWeight *dw = dvert->dw; float tot_weight = 0.0f; for (int i = dvert->totweight; i != 0; i--, dw++) { - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { tot_weight += dw->weight; } } @@ -250,7 +250,7 @@ void BKE_defvert_normalize_subset(MDeformVert *dvert, float scalar = 1.0f / tot_weight; dw = dvert->dw; for (int i = dvert->totweight; i != 0; i--, dw++) { - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { dw->weight *= scalar; /* in case of division errors with very low weights */ @@ -292,7 +292,7 @@ void BKE_defvert_normalize(MDeformVert *dvert) void BKE_defvert_normalize_lock_single(MDeformVert *dvert, const bool *vgroup_subset, - const int vgroup_tot, + const int vgroup_num, const uint def_nr_lock) { if (dvert->totweight == 0) { @@ -300,7 +300,7 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert, } else if (dvert->totweight == 1) { MDeformWeight *dw = dvert->dw; - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { if (def_nr_lock != dw->def_nr) { dw->weight = 1.0f; } @@ -314,7 +314,7 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert, float lock_iweight = 1.0f; for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { if (dw->def_nr != def_nr_lock) { tot_weight += dw->weight; } @@ -331,7 +331,7 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert, float scalar = (1.0f / tot_weight) * lock_iweight; for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { if (dw != dw_lock) { dw->weight *= scalar; @@ -346,17 +346,17 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert, void BKE_defvert_normalize_lock_map(MDeformVert *dvert, const bool *vgroup_subset, - const int vgroup_tot, + const int vgroup_num, const bool *lock_flags, - const int defbase_tot) + const int defbase_num) { if (dvert->totweight == 0) { /* nothing */ } else if (dvert->totweight == 1) { MDeformWeight *dw = dvert->dw; - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { - if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < defbase_num) && (lock_flags[dw->def_nr] == false)) { dw->weight = 1.0f; } } @@ -368,8 +368,8 @@ void BKE_defvert_normalize_lock_map(MDeformVert *dvert, float lock_iweight = 0.0f; for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { - if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < defbase_num) && (lock_flags[dw->def_nr] == false)) { tot_weight += dw->weight; } else { @@ -386,8 +386,8 @@ void BKE_defvert_normalize_lock_map(MDeformVert *dvert, float scalar = (1.0f / tot_weight) * lock_iweight; for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { - if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) { + if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) { + if ((dw->def_nr < defbase_num) && (lock_flags[dw->def_nr] == false)) { dw->weight *= scalar; /* in case of division errors with very low weights */ @@ -399,13 +399,13 @@ void BKE_defvert_normalize_lock_map(MDeformVert *dvert, } } -void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, const int flip_map_len) +void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, const int flip_map_num) { MDeformWeight *dw; int i; for (dw = dvert->dw, i = 0; i < dvert->totweight; dw++, i++) { - if (dw->def_nr < flip_map_len) { + if (dw->def_nr < flip_map_num) { if (flip_map[dw->def_nr] >= 0) { dw->def_nr = flip_map[dw->def_nr]; } @@ -413,7 +413,7 @@ void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, const int flip_ma } } -void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int flip_map_len) +void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int flip_map_num) { MDeformWeight *dw, *dw_cpy; float weight; @@ -421,7 +421,7 @@ void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int /* copy weights */ for (dw = dvert->dw, i = 0; i < totweight; dw++, i++) { - if (dw->def_nr < flip_map_len) { + if (dw->def_nr < flip_map_num) { if (flip_map[dw->def_nr] >= 0) { /* error checkers complain of this but we'll never get NULL return */ dw_cpy = BKE_defvert_ensure_index(dvert, flip_map[dw->def_nr]); @@ -572,20 +572,25 @@ void BKE_object_defgroup_active_index_set(Object *ob, const int new_index) *index = new_index; } -int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const bool use_default) +static int *object_defgroup_unlocked_flip_map_ex(const Object *ob, + const bool use_default, + const bool use_only_unlocked, + int *r_flip_map_num) { const ListBase *defbase = BKE_object_defgroup_list(ob); - int defbase_tot = *flip_map_len = BLI_listbase_count(defbase); + const int defbase_num = BLI_listbase_count(defbase); + *r_flip_map_num = defbase_num; - if (defbase_tot == 0) { + if (defbase_num == 0) { return NULL; } bDeformGroup *dg; char name_flip[sizeof(dg->name)]; - int i, flip_num, *map = MEM_mallocN(defbase_tot * sizeof(int), __func__); + int i, flip_num; + int *map = MEM_mallocN(defbase_num * sizeof(int), __func__); - for (i = 0; i < defbase_tot; i++) { + for (i = 0; i < defbase_num; i++) { map[i] = -1; } @@ -597,11 +602,15 @@ int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const boo map[i] = i; } + if (use_only_unlocked && (dg->flag & DG_LOCK_WEIGHT)) { + continue; + } + BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip)); if (!STREQ(name_flip, dg->name)) { flip_num = BKE_object_defgroup_name_index(ob, name_flip); - if (flip_num >= 0) { + if (flip_num != -1) { map[i] = flip_num; map[flip_num] = i; /* save an extra lookup */ } @@ -611,23 +620,36 @@ int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const boo return map; } +int *BKE_object_defgroup_flip_map(const Object *ob, const bool use_default, int *r_flip_map_num) +{ + return object_defgroup_unlocked_flip_map_ex(ob, use_default, false, r_flip_map_num); +} + +int *BKE_object_defgroup_flip_map_unlocked(const Object *ob, + const bool use_default, + int *r_flip_map_num) +{ + return object_defgroup_unlocked_flip_map_ex(ob, use_default, true, r_flip_map_num); +} + int *BKE_object_defgroup_flip_map_single(const Object *ob, - int *flip_map_len, const bool use_default, - int defgroup) + const int defgroup, + int *r_flip_map_num) { const ListBase *defbase = BKE_object_defgroup_list(ob); - int defbase_tot = *flip_map_len = BLI_listbase_count(defbase); + const int defbase_num = BLI_listbase_count(defbase); + *r_flip_map_num = defbase_num; - if (defbase_tot == 0) { + if (defbase_num == 0) { return NULL; } bDeformGroup *dg; char name_flip[sizeof(dg->name)]; - int i, flip_num, *map = MEM_mallocN(defbase_tot * sizeof(int), __func__); + int i, flip_num, *map = MEM_mallocN(defbase_num * sizeof(int), __func__); - for (i = 0; i < defbase_tot; i++) { + for (i = 0; i < defbase_num; i++) { map[i] = use_default ? i : -1; } @@ -776,7 +798,7 @@ MDeformWeight *BKE_defvert_ensure_index(MDeformVert *dvert, const int defgroup) return dw_new; } -void BKE_defvert_add_index_notest(MDeformVert *dvert, int defgroup, const float weight) +void BKE_defvert_add_index_notest(MDeformVert *dvert, const int defgroup, const float weight) { /* TODO: merge with #BKE_defvert_ensure_index! */ @@ -870,7 +892,7 @@ bool BKE_defvert_is_weight_zero(const struct MDeformVert *dvert, const int defgr } float BKE_defvert_total_selected_weight(const struct MDeformVert *dv, - int defbase_tot, + int defbase_num, const bool *defbase_sel) { float total = 0.0f; @@ -881,7 +903,7 @@ float BKE_defvert_total_selected_weight(const struct MDeformVert *dv, } for (int i = dv->totweight; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot) { + if (dw->def_nr < defbase_num) { if (defbase_sel[dw->def_nr]) { total += dw->weight; } @@ -892,17 +914,17 @@ float BKE_defvert_total_selected_weight(const struct MDeformVert *dv, } float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv, - int defbase_tot, + const int defbase_num, const bool *defbase_sel, - int defbase_tot_sel, - bool is_normalized) + const int defbase_sel_num, + const bool is_normalized) { - float total = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_sel); + float total = BKE_defvert_total_selected_weight(dv, defbase_num, defbase_sel); /* in multipaint, get the average if auto normalize is inactive * get the sum if it is active */ if (!is_normalized) { - total /= defbase_tot_sel; + total /= defbase_sel_num; } return total; @@ -936,19 +958,19 @@ float BKE_defvert_calc_lock_relative_weight(float weight, return weight / (1.0f - locked_weight); } -float BKE_defvert_lock_relative_weight(float weight, +float BKE_defvert_lock_relative_weight(const float weight, const struct MDeformVert *dv, - int defbase_tot, + const int defbase_num, const bool *defbase_locked, const bool *defbase_unlocked) { - float unlocked = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_unlocked); + float unlocked = BKE_defvert_total_selected_weight(dv, defbase_num, defbase_unlocked); if (unlocked > 0.0f) { return weight / unlocked; } - float locked = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_locked); + float locked = BKE_defvert_total_selected_weight(dv, defbase_num, defbase_locked); return BKE_defvert_calc_lock_relative_weight(weight, locked, unlocked); } @@ -1010,12 +1032,12 @@ void BKE_defvert_array_free(MDeformVert *dvert, int totvert) void BKE_defvert_extract_vgroup_to_vertweights(const MDeformVert *dvert, const int defgroup, - const int num_verts, + const int verts_num, const bool invert_vgroup, float *r_weights) { if (dvert && defgroup != -1) { - int i = num_verts; + int i = verts_num; while (i--) { const float w = BKE_defvert_find_weight(&dvert[i], defgroup); @@ -1023,24 +1045,24 @@ void BKE_defvert_extract_vgroup_to_vertweights(const MDeformVert *dvert, } } else { - copy_vn_fl(r_weights, num_verts, invert_vgroup ? 1.0f : 0.0f); + copy_vn_fl(r_weights, verts_num, invert_vgroup ? 1.0f : 0.0f); } } void BKE_defvert_extract_vgroup_to_edgeweights(const MDeformVert *dvert, const int defgroup, - const int num_verts, + const int verts_num, const MEdge *edges, - const int num_edges, + const int edges_num, const bool invert_vgroup, float *r_weights) { if (dvert && defgroup != -1) { - int i = num_edges; - float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); + int i = edges_num; + float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)verts_num, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, invert_vgroup, tmp_weights); + dvert, defgroup, verts_num, invert_vgroup, tmp_weights); while (i--) { const MEdge *me = &edges[i]; @@ -1051,24 +1073,24 @@ void BKE_defvert_extract_vgroup_to_edgeweights(const MDeformVert *dvert, MEM_freeN(tmp_weights); } else { - copy_vn_fl(r_weights, num_edges, 0.0f); + copy_vn_fl(r_weights, edges_num, 0.0f); } } void BKE_defvert_extract_vgroup_to_loopweights(const MDeformVert *dvert, const int defgroup, - const int num_verts, + const int verts_num, const MLoop *loops, - const int num_loops, + const int loops_num, const bool invert_vgroup, float *r_weights) { if (dvert && defgroup != -1) { - int i = num_loops; - float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); + int i = loops_num; + float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)verts_num, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, invert_vgroup, tmp_weights); + dvert, defgroup, verts_num, invert_vgroup, tmp_weights); while (i--) { const MLoop *ml = &loops[i]; @@ -1079,26 +1101,26 @@ void BKE_defvert_extract_vgroup_to_loopweights(const MDeformVert *dvert, MEM_freeN(tmp_weights); } else { - copy_vn_fl(r_weights, num_loops, 0.0f); + copy_vn_fl(r_weights, loops_num, 0.0f); } } void BKE_defvert_extract_vgroup_to_polyweights(const MDeformVert *dvert, const int defgroup, - const int num_verts, + const int verts_num, const MLoop *loops, - const int UNUSED(num_loops), + const int UNUSED(loops_num), const MPoly *polys, - const int num_polys, + const int polys_num, const bool invert_vgroup, float *r_weights) { if (dvert && defgroup != -1) { - int i = num_polys; - float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); + int i = polys_num; + float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)verts_num, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, invert_vgroup, tmp_weights); + dvert, defgroup, verts_num, invert_vgroup, tmp_weights); while (i--) { const MPoly *mp = &polys[i]; @@ -1115,7 +1137,7 @@ void BKE_defvert_extract_vgroup_to_polyweights(const MDeformVert *dvert, MEM_freeN(tmp_weights); } else { - copy_vn_fl(r_weights, num_polys, 0.0f); + copy_vn_fl(r_weights, polys_num, 0.0f); } } @@ -1207,7 +1229,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, const ListBase *src_list = BKE_object_defgroup_list(ob_src); ListBase *dst_defbase = BKE_object_defgroup_list_mutable(ob_dst); - int tot_dst = BLI_listbase_count(dst_defbase); + const int tot_dst = BLI_listbase_count(dst_defbase); const size_t elem_size = sizeof(*((MDeformVert *)NULL)); diff --git a/source/blender/blenkernel/intern/fcurve_test.cc b/source/blender/blenkernel/intern/fcurve_test.cc index 1912e3a9d8d..285c6a0af4d 100644 --- a/source/blender/blenkernel/intern/fcurve_test.cc +++ b/source/blender/blenkernel/intern/fcurve_test.cc @@ -7,7 +7,6 @@ #include "BKE_fcurve.h" #include "ED_keyframing.h" -#include "ED_types.h" /* For SELECT. */ #include "DNA_anim_types.h" diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 1a994266df7..715c7d6c743 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -149,7 +149,7 @@ VArray<float3> mesh_normals_varray(const Mesh &mesh, * array and copy the face normal for each of its corners. In this case using the mesh * component's generic domain interpolation is fine, the data will still be normalized, * since the face normal is just copied to every corner. */ - return mesh_attributes(mesh).adapt_domain( + return mesh.attributes().adapt_domain( VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly}), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); @@ -1324,18 +1324,19 @@ static const AttributeAccessorFunctions &get_mesh_accessor_functions_ref() return fn; } -AttributeAccessor mesh_attributes(const Mesh &mesh) +} // namespace blender::bke + +blender::bke::AttributeAccessor Mesh::attributes() const { - return AttributeAccessor(&mesh, get_mesh_accessor_functions_ref()); + return blender::bke::AttributeAccessor(this, blender::bke::get_mesh_accessor_functions_ref()); } -MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh) +blender::bke::MutableAttributeAccessor Mesh::attributes_for_write() { - return MutableAttributeAccessor(&mesh, get_mesh_accessor_functions_ref()); + return blender::bke::MutableAttributeAccessor(this, + blender::bke::get_mesh_accessor_functions_ref()); } -} // namespace blender::bke - std::optional<blender::bke::AttributeAccessor> MeshComponent::attributes() const { return blender::bke::AttributeAccessor(mesh_, blender::bke::get_mesh_accessor_functions_ref()); diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 238854c987e..6980b561bc3 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -201,18 +201,20 @@ static const AttributeAccessorFunctions &get_pointcloud_accessor_functions_ref() return fn; } -AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud) +} // namespace blender::bke + +blender::bke::AttributeAccessor PointCloud::attributes() const { - return AttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref()); + return blender::bke::AttributeAccessor(this, + blender::bke::get_pointcloud_accessor_functions_ref()); } -MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud) +blender::bke::MutableAttributeAccessor PointCloud::attributes_for_write() { - return MutableAttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref()); + return blender::bke::MutableAttributeAccessor( + this, blender::bke::get_pointcloud_accessor_functions_ref()); } -} // namespace blender::bke - std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes() const { return blender::bke::AttributeAccessor(pointcloud_, diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc index a52ffb6496b..56e9e9dcdff 100644 --- a/source/blender/blenkernel/intern/geometry_fields.cc +++ b/source/blender/blenkernel/intern/geometry_fields.cc @@ -18,7 +18,7 @@ namespace blender::bke { MeshFieldContext::MeshFieldContext(const Mesh &mesh, const eAttrDomain domain) : mesh_(mesh), domain_(domain) { - BLI_assert(mesh_attributes(mesh).domain_supported(domain_)); + BLI_assert(mesh.attributes().domain_supported(domain_)); } CurvesFieldContext::CurvesFieldContext(const CurvesGeometry &curves, const eAttrDomain domain) @@ -94,13 +94,13 @@ GeometryFieldContext::GeometryFieldContext(const InstancesComponent &instances) std::optional<AttributeAccessor> GeometryFieldContext::attributes() const { if (const Mesh *mesh = this->mesh()) { - return mesh_attributes(*mesh); + return mesh->attributes(); } if (const CurvesGeometry *curves = this->curves()) { return curves->attributes(); } if (const PointCloud *pointcloud = this->pointcloud()) { - return pointcloud_attributes(*pointcloud); + return pointcloud->attributes(); } if (const InstancesComponent *instances = this->instances()) { return instances->attributes(); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 81bca5fc911..f6082d886d9 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -314,7 +314,7 @@ IDTypeInfo IDType_ID_GD = { .foreach_id = greasepencil_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = greasepencil_blend_write, .blend_read_data = greasepencil_blend_read_data, diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 02f0a8398b0..4d0db4d5386 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -2715,7 +2715,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain, gpl_fill, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW); int i; - const VArray<int> mesh_material_indices = mesh_attributes(*me_eval).lookup_or_default<int>( + const VArray<int> mesh_material_indices = me_eval->attributes().lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); for (i = 0; i < mpoly_len; i++) { const MPoly *mp = &polys[i]; diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index ae24383e5b9..75cf10f88aa 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -442,7 +442,7 @@ constexpr IDTypeInfo get_type_info() info.foreach_id = nullptr; info.foreach_cache = image_foreach_cache; info.foreach_path = image_foreach_path; - info.owner_get = nullptr; + info.owner_pointer_get = nullptr; info.blend_write = image_blend_write; info.blend_read_data = image_blend_read_data; diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index e65a94d5301..6f62ee123cb 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -175,12 +175,12 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath)); } else { - BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", DATA_("untitled")); + BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", DATA_("untitled"), nullptr); BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); } } else { - BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2); + BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", ima->id.name + 2, nullptr); BLI_path_make_safe(opts->filepath); BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); } diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index abd6505456e..22c0007cbc0 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -178,7 +178,7 @@ IDTypeInfo IDType_ID_IP = { .foreach_id = NULL, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = NULL, .blend_read_data = ipo_blend_read_data, diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 8e7690d41d6..2ba81c54872 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -91,14 +91,14 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK); } -static ID *shapekey_owner_get(ID *id) +static ID **shapekey_owner_pointer_get(ID *id) { Key *key = (Key *)id; BLI_assert(key->from != NULL); BLI_assert(BKE_key_from_id(key->from) == key); - return key->from; + return &key->from; } static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -215,7 +215,7 @@ IDTypeInfo IDType_ID_KE = { .foreach_path = NULL, /* A bit weird, due to shape-keys not being strictly speaking embedded data... But they also * share a lot with those (non linkable, only ever used by one owner ID, etc.). */ - .owner_get = shapekey_owner_get, + .owner_pointer_get = shapekey_owner_pointer_get, .blend_write = shapekey_blend_write, .blend_read_data = shapekey_blend_read_data, @@ -1938,6 +1938,16 @@ KeyBlock *BKE_keyblock_find_name(Key *key, const char name[]) return BLI_findstring(&key->block, name, offsetof(KeyBlock, name)); } +KeyBlock *BKE_keyblock_find_uid(Key *key, const int uid) +{ + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { + if (kb->uid == uid) { + return kb; + } + } + return NULL; +} + void BKE_keyblock_copy_settings(KeyBlock *kb_dst, const KeyBlock *kb_src) { kb_dst->pos = kb_src->pos; diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 6fb67711ae9..e0959182fa4 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -190,7 +190,7 @@ IDTypeInfo IDType_ID_LT = { .foreach_id = lattice_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = lattice_blend_write, .blend_read_data = lattice_blend_read_data, diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index ba638d96224..adb145fd34b 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -56,7 +56,8 @@ static CLG_LogRef LOG = {"bke.layercollection"}; /* Set of flags which are dependent on a collection settings. */ -static const short g_base_collection_flags = (BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER | +static const short g_base_collection_flags = (BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | + BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT | BASE_SELECTABLE | BASE_ENABLED_VIEWPORT | BASE_ENABLED_RENDER | BASE_HOLDOUT | BASE_INDIRECT_ONLY); @@ -1017,9 +1018,10 @@ static void layer_collection_objects_sync(ViewLayer *view_layer, } if ((collection_restrict & COLLECTION_HIDE_VIEWPORT) == 0) { - base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_VISIBLE_DEPSGRAPH); + base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | + BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT); if ((layer_restrict & LAYER_COLLECTION_HIDE) == 0) { - base->flag_from_collection |= BASE_VISIBLE_VIEWLAYER; + base->flag_from_collection |= BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT; } if (((collection_restrict & COLLECTION_HIDE_SELECT) == 0)) { base->flag_from_collection |= BASE_SELECTABLE; @@ -1484,7 +1486,8 @@ bool BKE_layer_collection_has_selected_objects(const Scene *scene, LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) { Base *base = BKE_view_layer_base_find(view_layer, cob->ob); - if (base && (base->flag & BASE_SELECTED) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { + if (base && (base->flag & BASE_SELECTED) && + (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT)) { return true; } } @@ -1541,12 +1544,12 @@ void BKE_base_set_visible(Scene *scene, ViewLayer *view_layer, Base *base, bool bool BKE_base_is_visible(const View3D *v3d, const Base *base) { - if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) { + if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0) { return false; } if (v3d == NULL) { - return base->flag & BASE_VISIBLE_VIEWLAYER; + return base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT; } if ((v3d->localvd) && ((v3d->local_view_uuid & base->local_view_bits) == 0)) { @@ -1561,7 +1564,7 @@ bool BKE_base_is_visible(const View3D *v3d, const Base *base) return (v3d->local_collections_uuid & base->local_collections_bits) != 0; } - return base->flag & BASE_VISIBLE_VIEWLAYER; + return base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT; } bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *ob) @@ -1587,7 +1590,7 @@ bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *o /* If not using local collection the object may still be in a hidden collection. */ if ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0) { - return (ob->base_flag & BASE_VISIBLE_VIEWLAYER) != 0; + return (ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) != 0; } return true; @@ -2053,12 +2056,13 @@ static void objects_iterator_end(BLI_Iterator *iter) void BKE_view_layer_selected_objects_iterator_begin(BLI_Iterator *iter, void *data_in) { - objects_iterator_begin(iter, data_in, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED); + objects_iterator_begin( + iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_objects_iterator_next(BLI_Iterator *iter) { - objects_iterator_next(iter, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED); + objects_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_objects_iterator_end(BLI_Iterator *iter) @@ -2095,7 +2099,8 @@ void BKE_view_layer_visible_objects_iterator_end(BLI_Iterator *iter) void BKE_view_layer_selected_editable_objects_iterator_begin(BLI_Iterator *iter, void *data_in) { - objects_iterator_begin(iter, data_in, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED); + objects_iterator_begin( + iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); if (iter->valid) { if (BKE_object_is_libdata((Object *)iter->current) == false) { /* First object is valid (selectable and not libdata) -> all good. */ @@ -2112,7 +2117,7 @@ void BKE_view_layer_selected_editable_objects_iterator_next(BLI_Iterator *iter) /* Search while there are objects and the one we have is not editable (editable = not libdata). */ do { - objects_iterator_next(iter, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED); + objects_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } while (iter->valid && BKE_object_is_libdata((Object *)iter->current) != false); } @@ -2129,12 +2134,13 @@ void BKE_view_layer_selected_editable_objects_iterator_end(BLI_Iterator *iter) void BKE_view_layer_selected_bases_iterator_begin(BLI_Iterator *iter, void *data_in) { - objects_iterator_begin(iter, data_in, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED); + objects_iterator_begin( + iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_bases_iterator_next(BLI_Iterator *iter) { - object_bases_iterator_next(iter, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED); + object_bases_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_bases_iterator_end(BLI_Iterator *iter) @@ -2261,7 +2267,8 @@ void BKE_base_eval_flags(Base *base) * can change these again, but for tools we always want the viewport * visibility to be in sync regardless if depsgraph was evaluated. */ if (!(base->flag & BASE_ENABLED_VIEWPORT) || (base->flag & BASE_HIDDEN)) { - base->flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER | BASE_SELECTABLE); + base->flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | + BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT | BASE_SELECTABLE); } /* Deselect unselectable objects. */ diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 5a394a05d86..8faa260ab8b 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -93,7 +93,7 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = { .foreach_id = NULL, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = NULL, .blend_read_data = NULL, @@ -1965,6 +1965,18 @@ bool BKE_id_can_be_asset(const ID *id) BKE_idtype_idcode_is_linkable(GS(id->name)); } +ID *BKE_id_owner_get(ID *id) +{ + const IDTypeInfo *idtype = BKE_idtype_get_info_from_id(id); + if (idtype->owner_pointer_get != NULL) { + ID **owner_id_pointer = idtype->owner_pointer_get(id); + if (owner_id_pointer != NULL) { + return *owner_id_pointer; + } + } + return NULL; +} + bool BKE_id_is_editable(const Main *bmain, const ID *id) { return !(ID_IS_LINKED(id) || BKE_lib_override_library_is_system_defined(bmain, id)); diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 8e9f73ee125..b05ae775be6 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -97,21 +97,17 @@ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main * /* const ID * /*owner_id_hint*/, const ID **r_owner_id) { - if (r_owner_id != nullptr) { - *r_owner_id = id; - } if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) { - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); - if (id_type->owner_get != nullptr) { - /* The #IDTypeInfo::owner_get callback should not modify the arguments, so casting away const - * is okay. */ - const ID *owner_id = id_type->owner_get(const_cast<ID *>(id)); - if (r_owner_id != nullptr) { - *r_owner_id = owner_id; - } - return owner_id->override_library; + const ID *owner_id = BKE_id_owner_get(const_cast<ID *>(id)); + BLI_assert_msg(owner_id != nullptr, "Liboverride-embedded ID with no owner"); + if (r_owner_id != nullptr) { + *r_owner_id = owner_id; } - BLI_assert_msg(0, "IDTypeInfo of liboverride-embedded ID with no owner getter"); + return owner_id->override_library; + } + + if (r_owner_id != nullptr) { + *r_owner_id = id; } return id->override_library; } @@ -2214,9 +2210,9 @@ static bool lib_override_resync_id_lib_level_is_valid(ID *id, static ID *lib_override_library_main_resync_root_get(Main * /*bmain*/, ID *id) { if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); - if (id_type->owner_get != nullptr) { - id = id_type->owner_get(id); + ID *id_owner = BKE_id_owner_get(id); + if (id_owner != nullptr) { + id = id_owner; } BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index e51f3c524fa..50843b18d18 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -711,9 +711,8 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, ID *id_from = id_from_item->id_pointer.from; if ((id_from->flag & LIB_EMBEDDED_DATA) != 0) { /* Directly 'by-pass' to actual real ID owner. */ - const IDTypeInfo *type_info_from = BKE_idtype_get_info_from_id(id_from); - BLI_assert(type_info_from->owner_get != NULL); - id_from = type_info_from->owner_get(id_from); + id_from = BKE_id_owner_get(id_from); + BLI_assert(id_from != NULL); } lib_query_unused_ids_tag_recurse( diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 9424b615031..516fb9b75b6 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -97,7 +97,7 @@ IDTypeInfo IDType_ID_LI = { .foreach_id = library_foreach_id, .foreach_cache = NULL, .foreach_path = library_foreach_path, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = NULL, .blend_read_data = library_blend_read_data, diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index de7224e5bf0..42af11294c9 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -189,7 +189,7 @@ IDTypeInfo IDType_ID_LA = { .foreach_id = light_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = light_blend_write, .blend_read_data = light_blend_read_data, diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 9e731b1f878..a2151d6c6f1 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -85,7 +85,7 @@ IDTypeInfo IDType_ID_LP = { .foreach_id = lightprobe_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = lightprobe_blend_write, .blend_read_data = lightprobe_blend_read_data, diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 64dc48c0154..cba1bc414c1 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -749,7 +749,7 @@ IDTypeInfo IDType_ID_LS = { .foreach_id = linestyle_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = linestyle_blend_write, .blend_read_data = linestyle_blend_read_data, diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 42e65a95404..25268785d42 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -249,7 +249,7 @@ IDTypeInfo IDType_ID_MSK = { .foreach_id = mask_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = mask_blend_write, .blend_read_data = mask_blend_read_data, diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index e50eb9b0755..59a51436b7b 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -256,7 +256,7 @@ IDTypeInfo IDType_ID_MA = { .foreach_id = material_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = material_blend_write, .blend_read_data = material_blend_read_data, diff --git a/source/blender/blenkernel/intern/mball.cc b/source/blender/blenkernel/intern/mball.cc index 46f6e731354..91797f8ed2f 100644 --- a/source/blender/blenkernel/intern/mball.cc +++ b/source/blender/blenkernel/intern/mball.cc @@ -181,7 +181,7 @@ IDTypeInfo IDType_ID_MB = { /* foreach_id */ metaball_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ nullptr, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ metaball_blend_write, /* blend_read_data */ metaball_blend_read_data, diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 3917c020759..49963c333ec 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -1447,7 +1447,6 @@ Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob) MVert *mvert = CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CONSTRUCT, NULL, mesh->totvert); for (int i = 0; i < mesh->totvert; i++) { copy_v3_v3(mvert[i].co, process.co[i]); - mvert->bweight = 0; mvert->flag = 0; } MEM_freeN(process.co); diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 1bf068fcd45..6bf25da5ae7 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -249,17 +249,18 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address else { Set<std::string> names_to_skip; if (!BLO_write_is_undo(writer)) { - BKE_mesh_legacy_convert_hide_layers_to_flags(mesh); BKE_mesh_legacy_convert_material_indices_to_mpoly(mesh); + BKE_mesh_legacy_bevel_weight_from_layers(mesh); /* When converting to the old mesh format, don't save redundant attributes. */ - names_to_skip.add_multiple_new({".hide_vert", ".hide_edge", ".hide_poly"}); + names_to_skip.add_multiple_new({".hide_vert", ".hide_edge", ".hide_poly", "material_index"}); /* Set deprecated mesh data pointers for forward compatibility. */ mesh->mvert = const_cast<MVert *>(mesh->verts().data()); mesh->medge = const_cast<MEdge *>(mesh->edges().data()); mesh->mpoly = const_cast<MPoly *>(mesh->polys().data()); mesh->mloop = const_cast<MLoop *>(mesh->loops().data()); + mesh->dvert = const_cast<MDeformVert *>(mesh->deform_verts().data()); } CustomData_blend_write_prepare(mesh->vdata, vert_layers, names_to_skip); @@ -314,9 +315,6 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &mesh->adt); BKE_animdata_blend_read_data(reader, mesh->adt); - /* Normally BKE_defvert_blend_read should be called in CustomData_blend_read, - * but for backwards compatibility in do_versions to work we do it here. */ - BKE_defvert_blend_read(reader, mesh->totvert, mesh->dvert); BLO_read_list(reader, &mesh->vertex_group_names); CustomData_blend_read(reader, &mesh->vdata, mesh->totvert); @@ -324,6 +322,11 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) CustomData_blend_read(reader, &mesh->fdata, mesh->totface); CustomData_blend_read(reader, &mesh->ldata, mesh->totloop); CustomData_blend_read(reader, &mesh->pdata, mesh->totpoly); + if (mesh->deform_verts().is_empty()) { + /* Vertex group data was also an owning pointer in old Blender versions. + * Don't read them again if they were read as part of #CustomData. */ + BKE_defvert_blend_read(reader, mesh->totvert, mesh->dvert); + } mesh->texflag &= ~ME_AUTOSPACE_EVALUATED; mesh->edit_mesh = nullptr; @@ -346,6 +349,7 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) if (!BLO_read_data_is_undo(reader)) { BKE_mesh_legacy_convert_flags_to_hide_layers(mesh); BKE_mesh_legacy_convert_mpoly_to_material_indices(mesh); + BKE_mesh_legacy_bevel_weight_to_layers(mesh); } /* We don't expect to load normals from files, since they are derived data. */ @@ -400,7 +404,7 @@ IDTypeInfo IDType_ID_ME = { /* foreach_id */ mesh_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ mesh_foreach_path, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ mesh_blend_write, /* blend_read_data */ mesh_blend_read_data, @@ -879,11 +883,12 @@ static void mesh_clear_geometry(Mesh *mesh) mesh->totpoly = 0; mesh->act_face = -1; mesh->totselect = 0; + + BLI_freelistN(&mesh->vertex_group_names); } void BKE_mesh_clear_geometry(Mesh *mesh) { - BKE_animdata_free(&mesh->id, false); BKE_mesh_runtime_clear_cache(mesh); mesh_clear_geometry(mesh); } @@ -973,6 +978,7 @@ void BKE_mesh_copy_parameters(Mesh *me_dst, const Mesh *me_src) copy_v3_v3(me_dst->size, me_src->size); me_dst->vertex_group_active_index = me_src->vertex_group_active_index; + me_dst->attributes_active_index = me_src->attributes_active_index; } void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src) @@ -1341,7 +1347,7 @@ void BKE_mesh_material_index_remove(Mesh *me, short index) { using namespace blender; using namespace blender::bke; - MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); + MutableAttributeAccessor attributes = me->attributes_for_write(); AttributeWriter<int> material_indices = attributes.lookup_for_write<int>("material_index"); if (!material_indices) { return; @@ -1366,7 +1372,7 @@ bool BKE_mesh_material_index_used(Mesh *me, short index) { using namespace blender; using namespace blender::bke; - const AttributeAccessor attributes = mesh_attributes(*me); + const AttributeAccessor attributes = me->attributes(); const VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); if (material_indices.is_single()) { @@ -1380,7 +1386,7 @@ void BKE_mesh_material_index_clear(Mesh *me) { using namespace blender; using namespace blender::bke; - MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); + MutableAttributeAccessor attributes = me->attributes_for_write(); attributes.remove("material_index"); BKE_mesh_tessface_clear(me); @@ -1409,20 +1415,16 @@ void BKE_mesh_material_remap(Mesh *me, const uint *remap, uint remap_len) } } else { - MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); - AttributeWriter<int> material_indices = attributes.lookup_for_write<int>("material_index"); + MutableAttributeAccessor attributes = me->attributes_for_write(); + SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>( + "material_index", ATTR_DOMAIN_FACE); if (!material_indices) { return; } - if (material_indices.domain != ATTR_DOMAIN_FACE) { - BLI_assert_unreachable(); - return; - } - MutableVArraySpan<int> indices_span(material_indices.varray); - for (const int i : indices_span.index_range()) { - MAT_NR_REMAP(indices_span[i]); + for (const int i : material_indices.span.index_range()) { + MAT_NR_REMAP(material_indices.span[i]); } - indices_span.save(); + material_indices.span.save(); material_indices.finish(); } @@ -1610,14 +1612,14 @@ void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh) const Span<MEdge> edges = mesh->edges(); for (const MVert &vert : verts) { - if (vert.bweight != 0) { + if (vert.bweight_legacy != 0) { mesh->cd_flag |= ME_CDFLAG_VERT_BWEIGHT; break; } } for (const MEdge &edge : edges) { - if (edge.bweight != 0) { + if (edge.bweight_legacy != 0) { mesh->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; if (mesh->cd_flag & ME_CDFLAG_EDGE_CREASE) { break; @@ -1761,7 +1763,7 @@ void BKE_mesh_count_selected_items(const Mesh *mesh, int r_count[3]) void BKE_mesh_vert_coords_get(const Mesh *mesh, float (*vert_coords)[3]) { - blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*mesh); + blender::bke::AttributeAccessor attributes = mesh->attributes(); VArray<float3> positions = attributes.lookup_or_default( "position", ATTR_DOMAIN_POINT, float3(0)); positions.materialize({(float3 *)vert_coords, mesh->totvert}); @@ -2089,11 +2091,11 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals) const bool do_edges = (num_new_edges > 0); /* Reallocate all vert and edge related data. */ + CustomData_realloc(&mesh->vdata, mesh->totvert, mesh->totvert + num_new_verts); mesh->totvert += num_new_verts; - CustomData_realloc(&mesh->vdata, mesh->totvert); if (do_edges) { + CustomData_realloc(&mesh->edata, mesh->totedge, mesh->totedge + num_new_edges); mesh->totedge += num_new_edges; - CustomData_realloc(&mesh->edata, mesh->totedge); } /* Update normals manually to avoid recalculation after this operation. */ diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index e37de1ae513..7a04e45fe00 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -381,7 +381,6 @@ static void copy_vert_attributes(Mesh *dest_mesh, int mv_index, int index_in_orig_me) { - mv->bweight = orig_mv->bweight; mv->flag = orig_mv->flag; /* For all layers in the orig mesh, copy the layer information. */ @@ -415,7 +414,7 @@ static void copy_poly_attributes(Mesh *dest_mesh, Span<short> material_remap, MutableSpan<int> dst_material_indices) { - const VArray<int> src_material_indices = bke::mesh_attributes(*orig_me).lookup_or_default<int>( + const VArray<int> src_material_indices = orig_me->attributes().lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); const int src_index = src_material_indices[index_in_orig_me]; if (material_remap.size() > 0 && material_remap.index_range().contains(src_index)) { @@ -450,7 +449,6 @@ static void copy_edge_attributes(Mesh *dest_mesh, int medge_index, int index_in_orig_me) { - medge->bweight = orig_medge->bweight; medge->crease = orig_medge->crease; medge->flag = orig_medge->flag; CustomData *target_cd = &dest_mesh->edata; @@ -609,7 +607,7 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh, get_poly2d_cos(orig_me, orig_mp, cos_2d, mim.to_target_transform[orig_me_index], axis_mat); } CustomData *target_cd = &dest_mesh->ldata; - const Span<MVert> dst_vertices = dest_mesh->verts(); + const Span<MVert> dst_verts = dest_mesh->verts(); const Span<MLoop> dst_loops = dest_mesh->loops(); for (int i = 0; i < mp->totloop; ++i) { int loop_index = mp->loopstart + i; @@ -620,7 +618,7 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh, * The coordinate needs to be projected into 2d, just like the interpolating polygon's * coordinates were. The `dest_mesh` coordinates are already in object 0 local space. */ float co[2]; - mul_v2_m3v3(co, axis_mat, dst_vertices[dst_loops[loop_index].v].co); + mul_v2_m3v3(co, axis_mat, dst_verts[dst_loops[loop_index].v].co); interp_weights_poly_v2(weights.data(), cos_2d, orig_mp->totloop, co); } for (int source_layer_i = 0; source_layer_i < source_cd->totlayer; ++source_layer_i) { @@ -723,10 +721,10 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim) merge_vertex_loop_poly_customdata_layers(result, mim); /* Set the vertex coordinate values and other data. */ - MutableSpan<MVert> vertices = result->verts_for_write(); + MutableSpan<MVert> verts = result->verts_for_write(); for (int vi : im->vert_index_range()) { const Vert *v = im->vert(vi); - MVert *mv = &vertices[vi]; + MVert *mv = &verts[vi]; copy_v3fl_v3db(mv->co, v->co); if (v->orig != NO_INDEX) { const Mesh *orig_me; @@ -739,8 +737,8 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim) /* Set the loopstart and totloop for each output poly, * and set the vertices in the appropriate loops. */ bke::SpanAttributeWriter<int> dst_material_indices = - bke::mesh_attributes_for_write(*result).lookup_or_add_for_write_only_span<int>( - "material_index", ATTR_DOMAIN_FACE); + result->attributes_for_write().lookup_or_add_for_write_only_span<int>("material_index", + ATTR_DOMAIN_FACE); int cur_loop_index = 0; MutableSpan<MLoop> dst_loops = result->loops_for_write(); MutableSpan<MPoly> dst_polys = result->polys_for_write(); diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 9f8326454d2..e56df0e3fe3 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -54,9 +54,11 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::StringRefNull; /* Define for cases when you want extra validation of mesh * after certain modifications. @@ -103,8 +105,9 @@ static void make_edges_mdata_extend(Mesh &mesh) #endif if (totedge_new) { - CustomData_realloc(&mesh.edata, totedge + totedge_new); - + /* The only layer should be edges, so no other layers need to be initialized. */ + BLI_assert(mesh.edata.totlayer == 1); + CustomData_realloc(&mesh.edata, totedge, totedge + totedge_new); mesh.totedge += totedge_new; MutableSpan<MEdge> edges = mesh.edges_for_write(); MEdge *medge = &edges[totedge]; @@ -116,7 +119,7 @@ static void make_edges_mdata_extend(Mesh &mesh) BLI_edgehashIterator_getKey(ehi, &medge->v1, &medge->v2); BLI_edgehashIterator_setValue(ehi, POINTER_FROM_UINT(e_index)); - medge->crease = medge->bweight = 0; + medge->crease = 0; medge->flag = ME_EDGEDRAW | ME_EDGERENDER; } BLI_edgehashIterator_free(ehi); @@ -193,7 +196,7 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba MEdge *medge = edges.data(); MPoly *mpoly = polys.data(); MLoop *mloop = loops.data(); - MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>( "material_index", ATTR_DOMAIN_FACE); MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_add_layer_named( @@ -632,16 +635,17 @@ void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) using namespace blender; BLI_assert(me != nullptr); - + /* The pointcloud should only contain the position attribute, otherwise more attributes would + * need to be initialized below. */ + BLI_assert(pointcloud->attributes().all_ids().size() == 1); + CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint, me->totvert); pointcloud->totpoint = me->totvert; - CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); /* Copy over all attributes. */ CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert); - bke::AttributeAccessor mesh_attributes = bke::mesh_attributes(*me); - bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + bke::AttributeAccessor mesh_attributes = me->attributes(); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); const VArray<float3> mesh_positions = mesh_attributes.lookup_or_default<float3>( "position", ATTR_DOMAIN_POINT, float3(0)); @@ -1082,10 +1086,10 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, mesh_in_bmain->smoothresh = mesh->smoothresh; mesh->mat = nullptr; - BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr); /* Anonymous attributes shouldn't exist on original data. */ - blender::bke::mesh_attributes_for_write(*mesh_in_bmain).remove_anonymous(); + mesh_in_bmain->attributes_for_write().remove_anonymous(); /* User-count is required because so far mesh was in a limbo, where library management does * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ @@ -1236,239 +1240,113 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, return result; } -/* This is a Mesh-based copy of the same function in DerivedMesh.cc */ -static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int actshape_uid) +static KeyBlock *keyblock_ensure_from_uid(Key &key, const int uid, const StringRefNull name) { - KeyBlock *kb; - int i, j, tot; - - if (!mesh_dst->key) { - return; + if (KeyBlock *kb = BKE_keyblock_find_uid(&key, uid)) { + return kb; } + KeyBlock *kb = BKE_keyblock_add(&key, name.c_str()); + kb->uid = uid; + return kb; +} - tot = CustomData_number_of_layers(&mesh_src->vdata, CD_SHAPEKEY); - for (i = 0; i < tot; i++) { - CustomDataLayer *layer = - &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)]; - float(*kbcos)[3]; - - for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { - if (kb->uid == layer->uid) { - break; - } - } +static int find_object_active_key_uid(const Key &key, const Object &object) +{ + const int active_kb_index = object.shapenr - 1; + const KeyBlock *kb = (const KeyBlock *)BLI_findlink(&key.block, active_kb_index); + if (!kb) { + CLOG_ERROR(&LOG, "Could not find object's active shapekey %d", active_kb_index); + return -1; + } + return kb->uid; +} - if (!kb) { - kb = BKE_keyblock_add(mesh_dst->key, layer->name); - kb->uid = layer->uid; - } +static void move_shapekey_layers_to_keyblocks(Mesh &mesh, Key &key_dst, const int actshape_uid) +{ + using namespace blender::bke; + for (const int i : IndexRange(CustomData_number_of_layers(&mesh.vdata, CD_SHAPEKEY))) { + const int layer_index = CustomData_get_layer_index_n(&mesh.vdata, CD_SHAPEKEY, i); + CustomDataLayer &layer = mesh.vdata.layers[layer_index]; - if (kb->data) { - MEM_freeN(kb->data); - } + KeyBlock *kb = keyblock_ensure_from_uid(key_dst, layer.uid, layer.name); + MEM_SAFE_FREE(kb->data); - const float(*cos)[3] = (const float(*)[3])CustomData_get_layer_n( - &mesh_src->vdata, CD_SHAPEKEY, i); - kb->totelem = mesh_src->totvert; + kb->totelem = mesh.totvert; - kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__); if (kb->uid == actshape_uid) { - const Span<MVert> verts = mesh_src->verts(); - for (j = 0; j < mesh_src->totvert; j++, kbcos++) { - copy_v3_v3(*kbcos, verts[j].co); - } + kb->data = MEM_malloc_arrayN(kb->totelem, sizeof(float3), __func__); + MutableSpan<float3> kb_coords(static_cast<float3 *>(kb->data), kb->totelem); + mesh.attributes().lookup<float3>("position").materialize(kb_coords); } else { - for (j = 0; j < kb->totelem; j++, cos++, kbcos++) { - copy_v3_v3(*kbcos, *cos); - } + kb->data = layer.data; + layer.data = nullptr; } } - for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { - if (kb->totelem != mesh_src->totvert) { - if (kb->data) { - MEM_freeN(kb->data); - } - - kb->totelem = mesh_src->totvert; - kb->data = MEM_calloc_arrayN(kb->totelem, sizeof(float[3]), __func__); - CLOG_ERROR(&LOG, "lost a shapekey layer: '%s'! (bmesh internal error)", kb->name); + LISTBASE_FOREACH (KeyBlock *, kb, &key_dst.block) { + if (kb->totelem != mesh.totvert) { + MEM_SAFE_FREE(kb->data); } + kb->totelem = mesh.totvert; + kb->data = MEM_cnew_array<float3>(kb->totelem, __func__); + CLOG_ERROR(&LOG, "Data for shape key '%s' on mesh missing from evaluated mesh ", kb->name); } } -void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, - Mesh *mesh_dst, - Object *ob, - const CustomData_MeshMasks *mask, - bool take_ownership) +void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob) { using namespace blender::bke; BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN); - - /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */ - /* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh); - * check whether it is still true with Mesh */ - Mesh tmp = blender::dna::shallow_copy(*mesh_dst); - int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; - bool did_shapekeys = false; - eCDAllocType alloctype = CD_DUPLICATE; - - if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) { - bool has_any_referenced_layers = CustomData_has_referenced(&mesh_src->vdata) || - CustomData_has_referenced(&mesh_src->edata) || - CustomData_has_referenced(&mesh_src->ldata) || - CustomData_has_referenced(&mesh_src->fdata) || - CustomData_has_referenced(&mesh_src->pdata); - if (!has_any_referenced_layers) { - alloctype = CD_ASSIGN; - } - } - CustomData_reset(&tmp.vdata); - CustomData_reset(&tmp.edata); - CustomData_reset(&tmp.fdata); - CustomData_reset(&tmp.ldata); - CustomData_reset(&tmp.pdata); - - totvert = tmp.totvert = mesh_src->totvert; - totedge = tmp.totedge = mesh_src->totedge; - totloop = tmp.totloop = mesh_src->totloop; - totpoly = tmp.totpoly = mesh_src->totpoly; - tmp.totface = 0; - - CustomData_copy(&mesh_src->vdata, &tmp.vdata, mask->vmask, alloctype, totvert); - CustomData_copy(&mesh_src->edata, &tmp.edata, mask->emask, alloctype, totedge); - CustomData_copy(&mesh_src->ldata, &tmp.ldata, mask->lmask, alloctype, totloop); - CustomData_copy(&mesh_src->pdata, &tmp.pdata, mask->pmask, alloctype, totpoly); - tmp.cd_flag = mesh_src->cd_flag; - tmp.runtime.deformed_only = mesh_src->runtime.deformed_only; - - /* Clear the normals completely, since the new vertex / polygon count might be different. */ - BKE_mesh_clear_derived_normals(&tmp); - - if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) { - KeyBlock *kb; - int uid; - - if (ob) { - kb = (KeyBlock *)BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1); - if (kb) { - uid = kb->uid; - } - else { - CLOG_ERROR(&LOG, "could not find active shapekey %d!", ob->shapenr - 1); - - uid = INT_MAX; - } - } - else { - /* if no object, set to INT_MAX so we don't mess up any shapekey layers */ - uid = INT_MAX; - } - - shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid); - did_shapekeys = true; - } - - /* copy texture space */ if (ob) { - BKE_mesh_texspace_copy_from_object(&tmp, ob); - } - - /* not all DerivedMeshes store their verts/edges/faces in CustomData, so - * we set them here in case they are missing */ - /* TODO(Sybren): we could probably replace CD_ASSIGN with alloctype and - * always directly pass mesh_src->mxxx, instead of using a ternary operator. */ - if (!CustomData_has_layer(&tmp.vdata, CD_MVERT)) { - CustomData_add_layer(&tmp.vdata, - CD_MVERT, - CD_ASSIGN, - (alloctype == CD_ASSIGN) ? mesh_src->verts_for_write().data() : - MEM_dupallocN(mesh_src->verts().data()), - totvert); - } - if (!CustomData_has_layer(&tmp.edata, CD_MEDGE)) { - CustomData_add_layer(&tmp.edata, - CD_MEDGE, - CD_ASSIGN, - (alloctype == CD_ASSIGN) ? mesh_src->edges_for_write().data() : - MEM_dupallocN(mesh_src->edges().data()), - totedge); - } - if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) { - CustomData_add_layer(&tmp.ldata, - CD_MLOOP, - CD_ASSIGN, - (alloctype == CD_ASSIGN) ? mesh_src->loops_for_write().data() : - MEM_dupallocN(mesh_src->loops().data()), - tmp.totloop); - CustomData_add_layer(&tmp.pdata, - CD_MPOLY, - CD_ASSIGN, - (alloctype == CD_ASSIGN) ? mesh_src->polys_for_write().data() : - MEM_dupallocN(mesh_src->polys().data()), - tmp.totpoly); + BLI_assert(mesh_dst == ob->data); } - /* object had got displacement layer, should copy this layer to save sculpted data */ - /* NOTE(nazgul): maybe some other layers should be copied? */ - if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) { - if (totloop == mesh_dst->totloop) { - MDisps *mdisps = (MDisps *)CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS); - CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop); - if (alloctype == CD_ASSIGN) { - /* Assign nullptr to prevent double-free. */ - CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, nullptr); - } - } - } + BKE_mesh_clear_geometry(mesh_dst); - CustomData_free(&mesh_dst->vdata, mesh_dst->totvert); - CustomData_free(&mesh_dst->edata, mesh_dst->totedge); - CustomData_free(&mesh_dst->fdata, mesh_dst->totface); - CustomData_free(&mesh_dst->ldata, mesh_dst->totloop); - CustomData_free(&mesh_dst->pdata, mesh_dst->totpoly); - - /* ok, this should now use new CD shapekey data, - * which should be fed through the modifier - * stack */ - if (tmp.totvert != mesh_dst->totvert && !did_shapekeys && mesh_dst->key) { - CLOG_ERROR(&LOG, "YEEK! this should be recoded! Shape key loss!: ID '%s'", tmp.id.name); - if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) { - id_us_min(&tmp.key->id); - } - tmp.key = nullptr; - } - - /* Clear selection history */ - MEM_SAFE_FREE(tmp.mselect); - tmp.totselect = 0; - tmp.texflag &= ~ME_AUTOSPACE_EVALUATED; + /* Make sure referenced layers have a single user so assigning them to the mesh in main doesn't + * share them. "Referenced" layers are not expected to be shared between original meshes. */ + CustomData_duplicate_referenced_layers(&mesh_src->vdata, mesh_src->totvert); + CustomData_duplicate_referenced_layers(&mesh_src->edata, mesh_src->totedge); + CustomData_duplicate_referenced_layers(&mesh_src->pdata, mesh_src->totpoly); + CustomData_duplicate_referenced_layers(&mesh_src->ldata, mesh_src->totloop); - /* Clear any run-time data. - * Even though this mesh won't typically have run-time data, the Python API can for e.g. - * create loop-triangle cache here, which is confusing when left in the mesh, see: T81136. */ - BKE_mesh_runtime_clear_geometry(&tmp); + mesh_dst->totvert = mesh_src->totvert; + mesh_dst->totedge = mesh_src->totedge; + mesh_dst->totpoly = mesh_src->totpoly; + mesh_dst->totloop = mesh_src->totloop; - /* skip the listbase */ - MEMCPY_STRUCT_AFTER(mesh_dst, &tmp, id.prev); + /* Using #CD_MASK_MESH ensures that only data that should exist in Main meshes is moved. */ + const CustomData_MeshMasks mask = CD_MASK_MESH; + CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, CD_ASSIGN, mesh_src->totvert); + CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, CD_ASSIGN, mesh_src->totedge); + CustomData_copy(&mesh_src->pdata, &mesh_dst->pdata, mask.pmask, CD_ASSIGN, mesh_src->totpoly); + CustomData_copy(&mesh_src->ldata, &mesh_dst->ldata, mask.lmask, CD_ASSIGN, mesh_src->totloop); BLI_freelistN(&mesh_dst->vertex_group_names); - BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names); - mesh_dst->vertex_group_active_index = mesh_src->vertex_group_active_index; - - if (take_ownership) { - if (alloctype == CD_ASSIGN) { - CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask->vmask); - CustomData_free_typemask(&mesh_src->edata, mesh_src->totedge, ~mask->emask); - CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask->lmask); - CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask->pmask); + mesh_dst->vertex_group_names = mesh_src->vertex_group_names; + BLI_listbase_clear(&mesh_src->vertex_group_names); + + BKE_mesh_copy_parameters(mesh_dst, mesh_src); + mesh_dst->cd_flag = mesh_src->cd_flag; + + /* For original meshes, shape key data is stored in the #Key data-block, so it + * must be moved from the storage in #CustomData layers used for evaluation. */ + if (Key *key_dst = mesh_dst->key) { + if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) { + /* If no object, set to -1 so we don't mess up any shapekey layers. */ + const int uid_active = ob ? find_object_active_key_uid(*key_dst, *ob) : -1; + move_shapekey_layers_to_keyblocks(*mesh_src, *key_dst, uid_active); + } + else if (mesh_src->totvert != mesh_dst->totvert) { + CLOG_ERROR(&LOG, "Mesh in Main '%s' lost shape keys", mesh_src->id.name); + if (mesh_src->key) { + id_us_min(&mesh_src->key->id); + } } - BKE_id_free(nullptr, mesh_src); } - BKE_mesh_assert_normals_dirty_or_calculated(mesh_dst); + BKE_id_free(nullptr, mesh_src); } void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb) diff --git a/source/blender/blenkernel/intern/mesh_debug.cc b/source/blender/blenkernel/intern/mesh_debug.cc index 1826a77d6f4..8a9ce901923 100644 --- a/source/blender/blenkernel/intern/mesh_debug.cc +++ b/source/blender/blenkernel/intern/mesh_debug.cc @@ -30,12 +30,6 @@ static void mesh_debug_info_from_cd_flag(const Mesh *me, DynStr *dynstr) { BLI_dynstr_append(dynstr, "'cd_flag': {"); - if (me->cd_flag & ME_CDFLAG_VERT_BWEIGHT) { - BLI_dynstr_append(dynstr, "'VERT_BWEIGHT', "); - } - if (me->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) { - BLI_dynstr_append(dynstr, "'EDGE_BWEIGHT', "); - } if (me->cd_flag & ME_CDFLAG_EDGE_CREASE) { BLI_dynstr_append(dynstr, "'EDGE_CREASE', "); } diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index f2fe2e0b141..938d7e42aa3 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -717,7 +717,7 @@ void BKE_mesh_polygon_flip(const MPoly *mpoly, MLoop *mloop, CustomData *ldata) BKE_mesh_polygon_flip_ex(mpoly, mloop, ldata, nullptr, mdisp, true); } -void BKE_mesh_polygons_flip(const MPoly *mpoly, MLoop *mloop, CustomData *ldata, int totpoly) +void BKE_mesh_polys_flip(const MPoly *mpoly, MLoop *mloop, CustomData *ldata, int totpoly) { MDisps *mdisp = (MDisps *)CustomData_get_layer(ldata, CD_MDISPS); const MPoly *mp; @@ -736,7 +736,7 @@ void BKE_mesh_flush_hidden_from_verts(Mesh *me) { using namespace blender; using namespace blender::bke; - MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); + MutableAttributeAccessor attributes = me->attributes_for_write(); const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( ".hide_vert", ATTR_DOMAIN_POINT, false); @@ -776,7 +776,7 @@ void BKE_mesh_flush_hidden_from_polys(Mesh *me) { using namespace blender; using namespace blender::bke; - MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); + MutableAttributeAccessor attributes = me->attributes_for_write(); const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( ".hide_poly", ATTR_DOMAIN_FACE, false); @@ -907,7 +907,7 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts, void BKE_mesh_flush_select_from_verts(Mesh *me) { - const blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*me); + const blender::bke::AttributeAccessor attributes = me->attributes(); mesh_flush_select_from_verts( me->verts(), me->loops(), diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index fe5add864ea..41dcb3501cc 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -76,13 +76,13 @@ class FairingContext { virtual ~FairingContext() = default; - void fair_vertices(bool *affected, - const eMeshFairingDepth depth, - VertexWeight *vertex_weight, - LoopWeight *loop_weight) + void fair_verts(bool *affected, + const eMeshFairingDepth depth, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) { - fair_vertices_ex(affected, (int)depth, vertex_weight, loop_weight); + fair_verts_ex(affected, (int)depth, vertex_weight, loop_weight); } protected: @@ -143,28 +143,28 @@ class FairingContext { loop_weight); } - void fair_vertices_ex(const bool *affected, - const int order, - VertexWeight *vertex_weight, - LoopWeight *loop_weight) + void fair_verts_ex(const bool *affected, + const int order, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) { Map<int, int> vert_col_map; - int num_affected_vertices = 0; + int affected_verts_num = 0; for (int i = 0; i < totvert_; i++) { if (!affected[i]) { continue; } - vert_col_map.add(i, num_affected_vertices); - num_affected_vertices++; + vert_col_map.add(i, affected_verts_num); + affected_verts_num++; } /* Early return, nothing to do. */ - if (ELEM(num_affected_vertices, 0, totvert_)) { + if (ELEM(affected_verts_num, 0, totvert_)) { return; } /* Setup fairing matrices */ - LinearSolver *solver = EIG_linear_solver_new(num_affected_vertices, num_affected_vertices, 3); + LinearSolver *solver = EIG_linear_solver_new(affected_verts_num, affected_verts_num, 3); for (auto item : vert_col_map.items()) { const int v = item.key; const int col = item.value; @@ -450,42 +450,40 @@ class UniformLoopWeight : public LoopWeight { } }; -static void prefair_and_fair_vertices(FairingContext *fairing_context, - bool *affected_vertices, - const eMeshFairingDepth depth) +static void prefair_and_fair_verts(FairingContext *fairing_context, + bool *affected_verts, + const eMeshFairingDepth depth) { /* Prefair. */ UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context); UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight(); - fairing_context->fair_vertices( - affected_vertices, depth, uniform_vertex_weights, uniform_loop_weights); + fairing_context->fair_verts(affected_verts, depth, uniform_vertex_weights, uniform_loop_weights); delete uniform_vertex_weights; /* Fair. */ VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context); /* TODO: Implement cotangent loop weights. */ - fairing_context->fair_vertices( - affected_vertices, depth, voronoi_vertex_weights, uniform_loop_weights); + fairing_context->fair_verts(affected_verts, depth, voronoi_vertex_weights, uniform_loop_weights); delete uniform_loop_weights; delete voronoi_vertex_weights; } -void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh, - struct MVert *deform_mverts, - bool *affect_vertices, - const eMeshFairingDepth depth) +void BKE_mesh_prefair_and_fair_verts(struct Mesh *mesh, + struct MVert *deform_mverts, + bool *affect_verts, + const eMeshFairingDepth depth) { MeshFairingContext *fairing_context = new MeshFairingContext(mesh, deform_mverts); - prefair_and_fair_vertices(fairing_context, affect_vertices, depth); + prefair_and_fair_verts(fairing_context, affect_verts, depth); delete fairing_context; } -void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm, - bool *affect_vertices, - const eMeshFairingDepth depth) +void BKE_bmesh_prefair_and_fair_verts(struct BMesh *bm, + bool *affect_verts, + const eMeshFairingDepth depth) { BMeshFairingContext *fairing_context = new BMeshFairingContext(bm); - prefair_and_fair_vertices(fairing_context, affect_vertices, depth); + prefair_and_fair_verts(fairing_context, affect_verts, depth); delete fairing_context; } diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 39b1ffb7cf4..627c0057a28 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -918,6 +918,67 @@ void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Bevel Weight Conversion + * \{ */ + +void BKE_mesh_legacy_bevel_weight_from_layers(Mesh *mesh) +{ + using namespace blender; + MutableSpan<MVert> verts = mesh->verts_for_write(); + if (const float *weights = static_cast<const float *>( + CustomData_get_layer(&mesh->vdata, CD_BWEIGHT))) { + mesh->cd_flag |= ME_CDFLAG_VERT_BWEIGHT; + for (const int i : verts.index_range()) { + verts[i].bweight_legacy = std::clamp(weights[i], 0.0f, 1.0f) * 255.0f; + } + } + else { + mesh->cd_flag &= ~ME_CDFLAG_VERT_BWEIGHT; + for (const int i : verts.index_range()) { + verts[i].bweight_legacy = 0; + } + } + MutableSpan<MEdge> edges = mesh->edges_for_write(); + if (const float *weights = static_cast<const float *>( + CustomData_get_layer(&mesh->edata, CD_BWEIGHT))) { + mesh->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; + for (const int i : edges.index_range()) { + edges[i].bweight_legacy = std::clamp(weights[i], 0.0f, 1.0f) * 255.0f; + } + } + else { + mesh->cd_flag &= ~ME_CDFLAG_EDGE_BWEIGHT; + for (const int i : edges.index_range()) { + edges[i].bweight_legacy = 0; + } + } +} + +void BKE_mesh_legacy_bevel_weight_to_layers(Mesh *mesh) +{ + using namespace blender; + const Span<MVert> verts = mesh->verts(); + if (mesh->cd_flag & ME_CDFLAG_VERT_BWEIGHT) { + float *weights = static_cast<float *>( + CustomData_add_layer(&mesh->vdata, CD_BWEIGHT, CD_CONSTRUCT, nullptr, verts.size())); + for (const int i : verts.index_range()) { + weights[i] = verts[i].bweight_legacy / 255.0f; + } + } + + const Span<MEdge> edges = mesh->edges(); + if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) { + float *weights = static_cast<float *>( + CustomData_add_layer(&mesh->edata, CD_BWEIGHT, CD_CONSTRUCT, nullptr, edges.size())); + for (const int i : edges.index_range()) { + weights[i] = edges[i].bweight_legacy / 255.0f; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Hide Attribute and Legacy Flag Conversion * \{ */ @@ -925,7 +986,7 @@ void BKE_mesh_legacy_convert_hide_layers_to_flags(Mesh *mesh) { using namespace blender; using namespace blender::bke; - const AttributeAccessor attributes = mesh_attributes(*mesh); + const AttributeAccessor attributes = mesh->attributes(); MutableSpan<MVert> verts = mesh->verts_for_write(); const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( @@ -959,7 +1020,7 @@ void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh) { using namespace blender; using namespace blender::bke; - MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); const Span<MVert> verts = mesh->verts(); if (std::any_of( @@ -1010,13 +1071,13 @@ void BKE_mesh_legacy_convert_material_indices_to_mpoly(Mesh *mesh) { using namespace blender; using namespace blender::bke; - const AttributeAccessor attributes = mesh_attributes(*mesh); + const AttributeAccessor attributes = mesh->attributes(); MutableSpan<MPoly> polys = mesh->polys_for_write(); const VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { for (const int i : range) { - polys[i].mat_nr = material_indices[i]; + polys[i].mat_nr_legacy = material_indices[i]; } }); } @@ -1025,15 +1086,15 @@ void BKE_mesh_legacy_convert_mpoly_to_material_indices(Mesh *mesh) { using namespace blender; using namespace blender::bke; - MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); const Span<MPoly> polys = mesh->polys(); if (std::any_of( - polys.begin(), polys.end(), [](const MPoly &poly) { return poly.mat_nr != 0; })) { + polys.begin(), polys.end(), [](const MPoly &poly) { return poly.mat_nr_legacy != 0; })) { SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>( "material_index", ATTR_DOMAIN_FACE); threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { for (const int i : range) { - material_indices.span[i] = polys[i].mat_nr; + material_indices.span[i] = polys[i].mat_nr_legacy; } }); material_indices.finish(); diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c index dc1ebf4c6e0..9c0e3c1bf59 100644 --- a/source/blender/blenkernel/intern/mesh_merge.c +++ b/source/blender/blenkernel/intern/mesh_merge.c @@ -354,11 +354,11 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, ml = src_loops + mp->loopstart; /* check faces with all vertices merged */ - bool all_vertices_merged = true; + bool all_verts_merged = true; for (j = 0; j < mp->totloop; j++, ml++) { if (vtargetmap[ml->v] == -1) { - all_vertices_merged = false; + all_verts_merged = false; /* This will be used to check for poly using several time the same vert. */ BLI_BITMAP_DISABLE(vert_tag, ml->v); } @@ -368,7 +368,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, } } - if (UNLIKELY(all_vertices_merged)) { + if (UNLIKELY(all_verts_merged)) { if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_MAPPED) { /* In this mode, all vertices merged is enough to dump face */ continue; diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index 2a64f6628f2..261bc3d150b 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.c +++ b/source/blender/blenkernel/intern/mesh_mirror.c @@ -208,8 +208,8 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, maxLoops); CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, maxPolys); - /* Subsurf for eg won't have mesh data in the custom-data arrays. - * now add mvert/medge/mpoly layers. */ + /* Subdivision-surface for eg won't have mesh data in the custom-data arrays. + * Now add #MVert/#MEdge/#MPoly layers. */ if (!CustomData_has_layer(&mesh->vdata, CD_MVERT)) { memcpy(BKE_mesh_verts_for_write(result), BKE_mesh_verts(mesh), sizeof(MVert) * mesh->totvert); } @@ -450,7 +450,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, MDeformVert *dvert = BKE_mesh_deform_verts_for_write(result) + maxVerts; int *flip_map = NULL, flip_map_len = 0; - flip_map = BKE_object_defgroup_flip_map(ob, &flip_map_len, false); + flip_map = BKE_object_defgroup_flip_map(ob, false, &flip_map_len); if (flip_map) { for (i = 0; i < maxVerts; dvert++, i++) { diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index a88ff4e9d90..21dd39586ec 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -2021,18 +2021,18 @@ void BKE_mesh_normals_loop_custom_set(const MVert *mverts, false); } -void BKE_mesh_normals_loop_custom_from_vertices_set(const MVert *mverts, - const float (*vert_normals)[3], - float (*r_custom_vertnors)[3], - const int numVerts, - MEdge *medges, - const int numEdges, - const MLoop *mloops, - const int numLoops, - const MPoly *mpolys, - const float (*polynors)[3], - const int numPolys, - short (*r_clnors_data)[2]) +void BKE_mesh_normals_loop_custom_from_verts_set(const MVert *mverts, + const float (*vert_normals)[3], + float (*r_custom_vertnors)[3], + const int numVerts, + MEdge *medges, + const int numEdges, + const MLoop *mloops, + const int numLoops, + const MPoly *mpolys, + const float (*polynors)[3], + const int numPolys, + short (*r_clnors_data)[2]) { mesh_normals_loop_custom_set(mverts, vert_normals, @@ -2087,7 +2087,7 @@ void BKE_mesh_set_custom_normals(Mesh *mesh, float (*r_custom_loopnors)[3]) mesh_set_custom_normals(mesh, r_custom_loopnors, false); } -void BKE_mesh_set_custom_normals_from_vertices(Mesh *mesh, float (*r_custom_vertnors)[3]) +void BKE_mesh_set_custom_normals_from_verts(Mesh *mesh, float (*r_custom_vertnors)[3]) { mesh_set_custom_normals(mesh, r_custom_vertnors, true); } diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index eb14028f49a..a77879fb573 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -124,6 +124,7 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh, /* Construct the new output mesh */ Mesh *mesh = BKE_mesh_new_nomain(qrd.out_totverts, 0, 0, qrd.out_totfaces * 4, qrd.out_totfaces); + BKE_mesh_copy_parameters(mesh, input_mesh); MutableSpan<MVert> mesh_verts = mesh->verts_for_write(); MutableSpan<MPoly> polys = mesh->polys_for_write(); MutableSpan<MLoop> loops = mesh->loops_for_write(); @@ -273,7 +274,9 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh, { #ifdef WITH_OPENVDB openvdb::FloatGrid::Ptr level_set = remesh_voxel_level_set_create(mesh, voxel_size); - return remesh_voxel_volume_to_mesh(level_set, isovalue, adaptivity, false); + Mesh *result = remesh_voxel_volume_to_mesh(level_set, isovalue, adaptivity, false); + BKE_mesh_copy_parameters(result, mesh); + return result; #else UNUSED_VARS(mesh, voxel_size, adaptivity, isovalue); return nullptr; diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index 3c1cdf84b3d..8f9af5e9258 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -68,7 +68,7 @@ struct BKEMeshToTangent { } const MPoly *mpolys; /* faces */ - const MLoop *mloops; /* faces's vertices */ + const MLoop *mloops; /* faces vertices */ const MVert *mverts; /* vertices */ const MLoopUV *luvs; /* texture coordinates */ const float (*lnors)[3]; /* loops' normals */ diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index 50577969a83..47de7245ccc 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -243,7 +243,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, (void)0 blender::bke::AttributeWriter<int> material_indices = - blender::bke::mesh_attributes_for_write(*mesh).lookup_for_write<int>("material_index"); + mesh->attributes_for_write().lookup_for_write<int>("material_index"); blender::MutableVArraySpan<int> material_indices_span(material_indices.varray); MVert *mv = mverts; @@ -1152,7 +1152,7 @@ bool BKE_mesh_validate_material_indices(Mesh *me) bool is_valid = true; blender::bke::AttributeWriter<int> material_indices = - blender::bke::mesh_attributes_for_write(*me).lookup_for_write<int>("material_index"); + me->attributes_for_write().lookup_for_write<int>("material_index"); blender::MutableVArraySpan<int> material_indices_span(material_indices.varray); for (const int i : material_indices_span.index_range()) { if (material_indices_span[i] < 0 || material_indices_span[i] > mat_nr_max) { diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 19ee2ba6605..46e05b39076 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -343,7 +343,7 @@ IDTypeInfo IDType_ID_MC = { .foreach_id = movie_clip_foreach_id, .foreach_cache = movie_clip_foreach_cache, .foreach_path = movie_clip_foreach_path, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = movieclip_blend_write, .blend_read_data = movieclip_blend_read_data, diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index 97c85ae526c..e887f543dc5 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.c +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -354,10 +354,9 @@ static GridCoord *vertex_grid_coord_with_grid_index(const Vertex *vertex, const /* Get grid coordinates which correspond to corners of the given face. * All the grid coordinates will be from the same grid index. */ -static void grid_coords_from_face_vertices( - const MultiresReshapeSmoothContext *reshape_smooth_context, - const Face *face, - const GridCoord *grid_coords[]) +static void grid_coords_from_face_verts(const MultiresReshapeSmoothContext *reshape_smooth_context, + const Face *face, + const GridCoord *grid_coords[]) { BLI_assert(face->num_corners == 4); @@ -417,7 +416,7 @@ static void foreach_toplevel_grid_coord_task(void *__restrict userdata_v, const Face *face = &reshape_smooth_context->geometry.faces[face_index]; const GridCoord *face_grid_coords[4]; - grid_coords_from_face_vertices(reshape_smooth_context, face, face_grid_coords); + grid_coords_from_face_verts(reshape_smooth_context, face, face_grid_coords); for (int y = 0; y < inner_grid_size; ++y) { const float ptex_v = (float)y * inner_grid_size_1_inv; diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c index 7884d6718af..353fbec6933 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.c +++ b/source/blender/blenkernel/intern/multires_unsubdivide.c @@ -161,7 +161,7 @@ static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v) */ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex) { - bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); + bool *visited_verts = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); GSQueue *queue; queue = BLI_gsqueue_new(sizeof(BMVert *)); @@ -177,7 +177,7 @@ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex int neighbor_vertex_index = BM_elem_index_get(neighbor_v); if (neighbor_v != initial_vertex && is_vertex_diagonal(neighbor_v, initial_vertex)) { BLI_gsqueue_push(queue, &neighbor_v); - visited_vertices[neighbor_vertex_index] = true; + visited_verts[neighbor_vertex_index] = true; BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true); } } @@ -211,10 +211,10 @@ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex BM_ITER_ELEM (f, &iter, diagonal_v, BM_FACES_OF_VERT) { BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) { int neighbor_vertex_index = BM_elem_index_get(neighbor_v); - if (!visited_vertices[neighbor_vertex_index] && neighbor_v != diagonal_v && + if (!visited_verts[neighbor_vertex_index] && neighbor_v != diagonal_v && is_vertex_diagonal(neighbor_v, diagonal_v)) { BLI_gsqueue_push(queue, &neighbor_v); - visited_vertices[neighbor_vertex_index] = true; + visited_verts[neighbor_vertex_index] = true; BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true); } } @@ -224,7 +224,7 @@ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex } BLI_gsqueue_free(queue); - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); } /** @@ -352,14 +352,14 @@ static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, i */ static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id) { - bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); + bool *visited_verts = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); int current_id = 0; for (int i = 0; i < bm->totvert; i++) { - if (!visited_vertices[i]) { + if (!visited_verts[i]) { GSQueue *queue; queue = BLI_gsqueue_new(sizeof(BMVert *)); - visited_vertices[i] = true; + visited_verts[i] = true; elem_id[i] = current_id; BMVert *iv = BM_vert_at_index(bm, i); BLI_gsqueue_push(queue, &iv); @@ -372,8 +372,8 @@ static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id) BM_ITER_ELEM (ed, &iter, current_v, BM_EDGES_OF_VERT) { neighbor_v = BM_edge_other_vert(ed, current_v); const int neighbor_index = BM_elem_index_get(neighbor_v); - if (!visited_vertices[neighbor_index]) { - visited_vertices[neighbor_index] = true; + if (!visited_verts[neighbor_index]) { + visited_verts[neighbor_index] = true; elem_id[neighbor_index] = current_id; BLI_gsqueue_push(queue, &neighbor_v); } @@ -383,7 +383,7 @@ static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id) BLI_gsqueue_free(queue); } } - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); return current_id; } @@ -1252,7 +1252,7 @@ int multiresModifier_rebuild_subdiv(struct Depsgraph *depsgraph, } /* Copy the new base mesh to the original mesh. */ - BKE_mesh_nomain_to_mesh(unsubdiv_context.base_mesh, object->data, object, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(unsubdiv_context.base_mesh, object->data, object); Mesh *base_mesh = object->data; multires_create_grids_in_unsubdivided_base_mesh(&unsubdiv_context, base_mesh); diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 9457c20eb7d..da508ff865c 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1241,7 +1241,7 @@ static NlaStrip *nlastrip_find_active(ListBase /* NlaStrip */ *strips) float BKE_nlastrip_compute_frame_from_previous_strip(NlaStrip *strip) { - float limit_prev = MINFRAMEF; + float limit_prev = MINAFRAMEF; /* Find the previous end frame, with a special case if the previous strip was a transition : */ if (strip->prev) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index ab26ccc5d3f..b82cf30416a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -71,6 +71,7 @@ #include "NOD_composite.h" #include "NOD_function.h" #include "NOD_geometry.h" +#include "NOD_geometry_nodes_lazy_function.hh" #include "NOD_node_declaration.hh" #include "NOD_shader.h" #include "NOD_socket.h" @@ -401,10 +402,10 @@ static void node_foreach_path(ID *id, BPathForeachPathData *bpath_data) } } -static ID *node_owner_get(ID *id) +static ID **node_owner_pointer_get(ID *id) { if ((id->flag & LIB_EMBEDDED_DATA) == 0) { - return id; + return NULL; } /* TODO: Sort this NO_MAIN or not for embedded node trees. See T86119. */ // BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0); @@ -413,7 +414,7 @@ static ID *node_owner_get(ID *id) BLI_assert(ntree->owner_id != NULL); BLI_assert(ntreeFromID(ntree->owner_id) == ntree); - return ntree->owner_id; + return &ntree->owner_id; } static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock) @@ -652,8 +653,13 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) { /* Special case for this pointer, do not rely on regular `lib_link` process here. Avoids needs - * for do_versioning, and ensures coherence of data in any case. */ - BLI_assert((ntree->id.flag & LIB_EMBEDDED_DATA) != 0 || owner_id == nullptr); + * for do_versioning, and ensures coherence of data in any case. + * + * NOTE: Old versions are very often 'broken' here, just fix it silently in these cases. + */ + if (BLO_read_fileversion_get(reader) > 300) { + BLI_assert((ntree->id.flag & LIB_EMBEDDED_DATA) != 0 || owner_id == nullptr); + } BLI_assert(owner_id == NULL || owner_id->lib == ntree->id.lib); if (owner_id != nullptr && (ntree->id.flag & LIB_EMBEDDED_DATA) == 0) { /* This is unfortunate, but currently a lot of existing files (including startup ones) have @@ -662,11 +668,13 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) * NOTE: Using do_version is not a solution here, since this code will be called before any * do_version takes place. Keeping it here also ensures future (or unknown existing) similar * bugs won't go easily unnoticed. */ - CLOG_WARN(&LOG, - "Fixing root node tree '%s' owned by '%s' missing EMBEDDED tag, please consider " - "re-saving your (startup) file", - ntree->id.name, - owner_id->name); + if (BLO_read_fileversion_get(reader) > 300) { + CLOG_WARN(&LOG, + "Fixing root node tree '%s' owned by '%s' missing EMBEDDED tag, please consider " + "re-saving your (startup) file", + ntree->id.name, + owner_id->name); + } ntree->id.flag |= LIB_EMBEDDED_DATA; } ntree->owner_id = owner_id; @@ -1036,7 +1044,7 @@ IDTypeInfo IDType_ID_NT = { /* foreach_id */ node_foreach_id, /* foreach_cache */ node_foreach_cache, /* foreach_path */ node_foreach_path, - /* owner_get */ node_owner_get, + /* owner_pointer_get */ node_owner_pointer_get, /* blend_write */ ntree_blend_write, /* blend_read_data */ ntree_blend_read_data, @@ -1924,6 +1932,9 @@ static void node_socket_free(bNodeSocket *sock, const bool do_id_user) } MEM_freeN(sock->default_value); } + if (sock->default_attribute_name) { + MEM_freeN(sock->default_attribute_name); + } MEM_delete(sock->runtime); } @@ -3008,6 +3019,9 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree), } MEM_freeN(sock->default_value); } + if (sock->default_attribute_name) { + MEM_freeN(sock->default_attribute_name); + } MEM_delete(sock->runtime); } diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 0c78c0f09d1..00b78284791 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -10,8 +10,22 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" +#include "NOD_geometry_nodes_lazy_function.hh" + namespace blender::bke::node_tree_runtime { +void handle_node_tree_output_changed(bNodeTree &tree_cow) +{ + if (tree_cow.type == NTREE_GEOMETRY) { + /* Rebuild geometry nodes lazy function graph. */ + { + std::lock_guard lock{tree_cow.runtime->geometry_nodes_lazy_function_graph_info_mutex}; + tree_cow.runtime->geometry_nodes_lazy_function_graph_info.reset(); + } + blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow); + } +} + static void double_checked_lock(std::mutex &mutex, bool &data_is_dirty, FunctionRef<void()> fn) { if (!data_is_dirty) { @@ -36,11 +50,15 @@ static void update_node_vector(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; tree_runtime.nodes.clear(); + tree_runtime.group_nodes.clear(); tree_runtime.has_undefined_nodes_or_sockets = false; LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { node->runtime->index_in_tree = tree_runtime.nodes.append_and_get_index(node); node->runtime->owner_tree = const_cast<bNodeTree *>(&ntree); tree_runtime.has_undefined_nodes_or_sockets |= node->typeinfo == &NodeTypeUndefined; + if (node->is_group()) { + tree_runtime.group_nodes.append(node); + } } } @@ -258,6 +276,7 @@ static void toposort_from_start_node(const ToposortDirection direction, Stack<Item, 64> nodes_to_check; nodes_to_check.push({&start_node}); + node_states[start_node.runtime->index_in_tree].is_in_stack = true; while (!nodes_to_check.is_empty()) { Item &item = nodes_to_check.peek(); bNode &node = *item.node; diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 611db6d274e..cbe5ea425fb 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1239,7 +1239,7 @@ IDTypeInfo IDType_ID_OB = { /* foreach_id */ object_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ object_foreach_path, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ object_blend_write, /* blend_read_data */ object_blend_read_data, @@ -2040,7 +2040,7 @@ bool BKE_object_is_mode_compat(const struct Object *ob, eObjectMode object_mode) int BKE_object_visibility(const Object *ob, const int dag_eval_mode) { - if ((ob->base_flag & BASE_VISIBLE_DEPSGRAPH) == 0) { + if ((ob->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0) { return 0; } @@ -2259,7 +2259,7 @@ Object *BKE_object_add_only_object(Main *bmain, int type, const char *name) } static Object *object_add_common( - Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name) + Main *bmain, const Scene *scene, ViewLayer *view_layer, int type, const char *name) { Object *ob = BKE_object_add_only_object(bmain, type, name); ob->data = BKE_object_obdata_add_from_type(bmain, type, name); diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 5656a9f6c92..91170060fee 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -407,10 +407,10 @@ void BKE_object_eval_eval_base_flags(Depsgraph *depsgraph, * assumed viewport visibility. Select-ability does not matter here. */ if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) { if (base->flag & BASE_ENABLED_RENDER) { - base->flag |= BASE_VISIBLE_DEPSGRAPH; + base->flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT; } else { - base->flag &= ~BASE_VISIBLE_DEPSGRAPH; + base->flag &= ~BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT; } } diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 7c96c463339..901b42ac0b2 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -526,21 +526,27 @@ static void unpack_generate_paths(const char *name, BLI_strncpy(tempdir, "//", sizeof(tempdir)); } - switch (id_type) { - case ID_VF: - BLI_snprintf(r_relpath, relpathlen, "//fonts/%s", tempname); - break; - case ID_SO: - BLI_snprintf(r_relpath, relpathlen, "//sounds/%s", tempname); - break; - case ID_IM: - BLI_snprintf(r_relpath, relpathlen, "//textures/%s", tempname); - break; - case ID_VO: - BLI_snprintf(r_relpath, relpathlen, "//volumes/%s", tempname); - break; - default: - break; + { + const char *dir_name = NULL; + switch (id_type) { + case ID_VF: + dir_name = "fonts"; + break; + case ID_SO: + dir_name = "sounds"; + break; + case ID_IM: + dir_name = "textures"; + break; + case ID_VO: + dir_name = "volumes"; + break; + default: + break; + } + if (dir_name) { + BLI_path_join(r_relpath, relpathlen, "//", dir_name, tempname, NULL); + } } { diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 67eaed67eb0..7ad9fd3a7c3 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -140,7 +140,7 @@ IDTypeInfo IDType_ID_PAL = { /* foreach_id */ nullptr, /* foreach_cache */ nullptr, /* foreach_path */ nullptr, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ palette_blend_write, /* blend_read_data */ palette_blend_read_data, @@ -208,7 +208,7 @@ IDTypeInfo IDType_ID_PC = { /* foreach_id */ nullptr, /* foreach_cache */ nullptr, /* foreach_path */ nullptr, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ paint_curve_blend_write, /* blend_read_data */ paint_curve_blend_read_data, @@ -1085,10 +1085,10 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) Sculpt *data = MEM_cnew<Sculpt>(__func__); paint = &data->paint; - /* Turn on X plane mirror symmetry by default */ + /* Turn on X plane mirror symmetry by default. */ paint->symmetry_flags |= PAINT_SYMM_X; - /* Make sure at least dyntopo subdivision is enabled */ + /* Make sure at least dyntopo subdivision is enabled. */ data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE; } else if ((GpPaint **)r_paint == &ts->gp_paint) { @@ -1143,7 +1143,7 @@ void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3] brush = BKE_brush_first_search(bmain, ob_mode); if (!brush) { brush = BKE_brush_add(bmain, "Brush", ob_mode); - id_us_min(&brush->id); /* fake user only */ + id_us_min(&brush->id); /* Fake user only. */ } BKE_paint_brush_set(paint, brush); } @@ -1244,18 +1244,17 @@ void BKE_paint_blend_read_lib(BlendLibReader *reader, Scene *sce, Paint *p) } } -bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_vert, const MLoop *mloop) +bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_poly) { - if (!hide_vert) { + if (!hide_poly) { return false; } - return ((hide_vert[mloop[lt->tri[0]].v]) || (hide_vert[mloop[lt->tri[1]].v]) || - (hide_vert[mloop[lt->tri[2]].v])); + return hide_poly[lt->poly]; } bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y) { - /* skip face if any of its corners are hidden */ + /* Skip face if any of its corners are hidden. */ return (BLI_BITMAP_TEST(grid_hidden, y * gridsize + x) || BLI_BITMAP_TEST(grid_hidden, y * gridsize + x + 1) || BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x + 1) || @@ -1285,7 +1284,7 @@ float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y return gpm->data[(y * factor) * gridsize + (x * factor)]; } -/* threshold to move before updating the brush rotation */ +/* Threshold to move before updating the brush rotation. */ #define RAKE_THRESHHOLD 20 void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, float rotation) @@ -1328,8 +1327,8 @@ bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, paint_update_brush_rake_rotation(ups, brush, rotation); ok = true; } - /* make sure we reset here to the last rotation to avoid accumulating - * values in case a random rotation is also added */ + /* Make sure we reset here to the last rotation to avoid accumulating + * values in case a random rotation is also added. */ else { paint_update_brush_rake_rotation(ups, brush, ups->last_rake_angle); ok = false; @@ -1508,7 +1507,7 @@ void BKE_sculptsession_free(Object *ob) } if (ss->boundary_preview) { - MEM_SAFE_FREE(ss->boundary_preview->vertices); + MEM_SAFE_FREE(ss->boundary_preview->verts); MEM_SAFE_FREE(ss->boundary_preview->edges); MEM_SAFE_FREE(ss->boundary_preview->distance); MEM_SAFE_FREE(ss->boundary_preview->edit_info); @@ -1525,20 +1524,29 @@ void BKE_sculptsession_free(Object *ob) } } -MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob) +static MultiresModifierData *sculpt_multires_modifier_get(const Scene *scene, + Object *ob, + const bool auto_create_mdisps) { Mesh *me = (Mesh *)ob->data; ModifierData *md; VirtualModifierData virtualModifierData; if (ob->sculpt && ob->sculpt->bm) { - /* can't combine multires and dynamic topology */ + /* Can't combine multires and dynamic topology. */ return nullptr; } + bool need_mdisps = false; + if (!CustomData_get_layer(&me->ldata, CD_MDISPS)) { - /* multires can't work without displacement layer */ - return nullptr; + if (!auto_create_mdisps) { + /* Multires can't work without displacement layer. */ + return nullptr; + } + else { + need_mdisps = true; + } } /* Weight paint operates on original vertices, and needs to treat multires as regular modifier @@ -1556,6 +1564,10 @@ MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob) } if (mmd->sculptlvl > 0 && !(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) { + if (need_mdisps) { + CustomData_add_layer(&me->ldata, CD_MDISPS, CD_SET_DEFAULT, nullptr, me->totloop); + } + return mmd; } @@ -1566,6 +1578,11 @@ MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob) return nullptr; } +MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob) +{ + return sculpt_multires_modifier_get(scene, ob, false); +} + /* Checks if there are any supported deformation modifiers active */ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) { @@ -1577,14 +1594,14 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) return false; } - /* non-locked shape keys could be handled in the same way as deformed mesh */ + /* Non-locked shape keys could be handled in the same way as deformed mesh. */ if ((ob->shapeflag & OB_SHAPE_LOCK) == 0 && me->key && ob->shapenr) { return true; } md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); - /* exception for shape keys because we can edit those */ + /* Exception for shape keys because we can edit those. */ for (; md; md = md->next) { const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { @@ -1611,22 +1628,15 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) return false; } -/** - * \param need_mask: So that the evaluated mesh that is returned has mask data. - */ -static void sculpt_update_object(Depsgraph *depsgraph, - Object *ob, - Object *ob_eval, - bool need_pmap, - bool need_mask, - bool is_paint_tool) +static void sculpt_update_object( + Depsgraph *depsgraph, Object *ob, Object *ob_eval, bool need_pmap, bool is_paint_tool) { Scene *scene = DEG_get_input_scene(depsgraph); Sculpt *sd = scene->toolsettings->sculpt; SculptSession *ss = ob->sculpt; Mesh *me = BKE_object_get_original_mesh(ob); Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + MultiresModifierData *mmd = sculpt_multires_modifier_get(scene, ob, true); const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0; BLI_assert(me_eval != nullptr); @@ -1641,15 +1651,6 @@ static void sculpt_update_object(Depsgraph *depsgraph, ss->scene = scene; - if (need_mask) { - if (mmd == nullptr) { - BLI_assert(CustomData_has_layer(&me->vdata, CD_PAINT_MASK)); - } - else { - BLI_assert(CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)); - } - } - ss->shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr; /* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path, @@ -1705,7 +1706,6 @@ static void sculpt_update_object(Depsgraph *depsgraph, /* Sculpt Face Sets. */ if (use_face_sets) { - BLI_assert(CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)); ss->face_sets = static_cast<int *>(CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS)); } else { @@ -1838,24 +1838,7 @@ static void sculpt_update_object(Depsgraph *depsgraph, } } -static void sculpt_face_sets_ensure(Mesh *mesh) -{ - if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) { - return; - } - - int *new_face_sets = static_cast<int *>(CustomData_add_layer( - &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly)); - - /* Initialize the new Face Set data-layer with a default valid visible ID and set the default - * color to render it white. */ - for (int i = 0; i < mesh->totpoly; i++) { - new_face_sets[i] = 1; - } - mesh->face_sets_color_default = 1; -} - -void BKE_sculpt_update_object_before_eval(const Scene *scene, Object *ob_eval) +void BKE_sculpt_update_object_before_eval(Object *ob_eval) { /* Update before mesh evaluation in the dependency graph. */ SculptSession *ss = ob_eval->sculpt; @@ -1885,16 +1868,6 @@ void BKE_sculpt_update_object_before_eval(const Scene *scene, Object *ob_eval) MEM_freeN(nodes); } } - - if (ss) { - Object *ob_orig = DEG_get_original_object(ob_eval); - Mesh *mesh = BKE_object_get_original_mesh(ob_orig); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob_orig); - - /* Ensure attribute layout is still correct. */ - sculpt_face_sets_ensure(mesh); - BKE_sculpt_mask_layers_ensure(ob_orig, mmd); - } } void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval) @@ -1903,7 +1876,7 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval) * other data when modifiers change the mesh. */ Object *ob_orig = DEG_get_original_object(ob_eval); - sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false, false); + sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false); } void BKE_sculpt_color_layer_create_if_needed(Object *object) @@ -1941,13 +1914,53 @@ void BKE_sculpt_color_layer_create_if_needed(Object *object) } void BKE_sculpt_update_object_for_edit( - Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool is_paint_tool) + Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool /*need_mask*/, bool is_paint_tool) { BLI_assert(ob_orig == DEG_get_original_object(ob_orig)); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_orig); - sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, need_mask, is_paint_tool); + sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool); +} + +int *BKE_sculpt_face_sets_ensure(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) { + return static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)); + } + + const AttributeAccessor attributes = mesh->attributes_for_write(); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + + MutableSpan<int> face_sets = { + static_cast<int *>(CustomData_add_layer( + &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly)), + mesh->totpoly}; + + /* Initialize the new face sets with a default valid visible ID and set the default + * color to render it white. */ + if (hide_poly.is_single() && !hide_poly.get_internal_single()) { + face_sets.fill(1); + } + else { + const int face_sets_default_visible_id = 1; + const int face_sets_default_hidden_id = -2; + + const VArraySpan<bool> hide_poly_span{hide_poly}; + for (const int i : face_sets.index_range()) { + /* Assign a new hidden ID to hidden faces. This way we get at initial split in two Face Sets + * between hidden and visible faces based on the previous mesh visibly from other mode that + * can be useful in some cases. */ + face_sets[i] = hide_poly_span[i] ? face_sets_default_hidden_id : + face_sets_default_visible_id; + } + } + + mesh->face_sets_color_default = 1; + return face_sets.data(); } int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) @@ -2011,7 +2024,7 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) ret |= SCULPT_MASK_LAYER_CALC_LOOP; } - /* create vertex paint mask layer if there isn't one already */ + /* Create vertex paint mask layer if there isn't one already. */ if (!paint_mask) { CustomData_add_layer(&me->vdata, CD_PAINT_MASK, CD_SET_DEFAULT, nullptr, me->totvert); ret |= SCULPT_MASK_LAYER_CALC_VERT; @@ -2035,7 +2048,7 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->constant_detail = 3.0f; } - /* Set sane default tiling offsets */ + /* Set sane default tiling offsets. */ if (!sd->paint.tile_offset[0]) { sd->paint.tile_offset[0] = 1.0f; } @@ -2053,8 +2066,7 @@ static bool check_sculpt_object_deformed(Object *object, const bool for_construc /* Active modifiers means extra deformation, which can't be handled correct * on birth of PBVH and sculpt "layer" levels, so use PBVH only for internal brush - * stuff and show final evaluated mesh so user would see actual object shape. - */ + * stuff and show final evaluated mesh so user would see actual object shape. */ deformed |= object->sculpt->deform_modifiers_active; if (for_construction) { @@ -2063,75 +2075,55 @@ static bool check_sculpt_object_deformed(Object *object, const bool for_construc else { /* As in case with modifiers, we can't synchronize deformation made against * PBVH and non-locked keyblock, so also use PBVH only for brushes and - * final DM to give final result to user. - */ + * final DM to give final result to user. */ deformed |= object->sculpt->shapekey_active && (object->shapeflag & OB_SHAPE_LOCK) == 0; } return deformed; } -void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh) +void BKE_sculpt_face_sets_update_from_base_mesh_visibility(Mesh *mesh) { - const int face_sets_default_visible_id = 1; - const int face_sets_default_hidden_id = -(face_sets_default_visible_id + 1); - - bool initialize_new_face_sets = false; - - if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) { - /* Make everything visible. */ - int *current_face_sets = static_cast<int *>( - CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)); - for (int i = 0; i < mesh->totpoly; i++) { - current_face_sets[i] = abs(current_face_sets[i]); - } - } - else { - initialize_new_face_sets = true; - int *new_face_sets = static_cast<int *>(CustomData_add_layer( - &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly)); - - /* Initialize the new Face Set data-layer with a default valid visible ID and set the default - * color to render it white. */ - for (int i = 0; i < mesh->totpoly; i++) { - new_face_sets[i] = face_sets_default_visible_id; - } - mesh->face_sets_color_default = face_sets_default_visible_id; + using namespace blender; + using namespace blender::bke; + if (!CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) { + return; } - int *face_sets = static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)); - const bool *hide_poly = (const bool *)CustomData_get_layer_named( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly"); + const AttributeAccessor attributes = mesh->attributes(); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (hide_poly.is_single() && !hide_poly.get_internal_single()) { + return; + } - for (int i = 0; i < mesh->totpoly; i++) { - if (!(hide_poly && hide_poly[i])) { - continue; - } + MutableSpan<int> face_sets{ + static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)), mesh->totpoly}; - if (initialize_new_face_sets) { - /* When initializing a new Face Set data-layer, assign a new hidden Face Set ID to hidden - * vertices. This way, we get at initial split in two Face Sets between hidden and - * visible vertices based on the previous mesh visibly from other mode that can be - * useful in some cases. */ - face_sets[i] = face_sets_default_hidden_id; - } - else { - /* Otherwise, set the already existing Face Set ID to hidden. */ - face_sets[i] = -abs(face_sets[i]); - } + for (const int i : hide_poly.index_range()) { + face_sets[i] = hide_poly[i] ? -std::abs(face_sets[i]) : std::abs(face_sets[i]); } } -void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) +static void set_hide_poly_from_face_sets(Mesh &mesh) { + using namespace blender; using namespace blender::bke; - const int *face_sets = static_cast<const int *>( - CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)); - if (!face_sets) { + if (!CustomData_has_layer(&mesh.pdata, CD_SCULPT_FACE_SETS)) { + return; + } + + const Span<int> face_sets{ + static_cast<const int *>(CustomData_get_layer(&mesh.pdata, CD_SCULPT_FACE_SETS)), + mesh.totpoly}; + + MutableAttributeAccessor attributes = mesh.attributes_for_write(); + if (std::all_of( + face_sets.begin(), face_sets.end(), [&](const int value) { return value > 0; })) { + attributes.remove(".hide_poly"); return; } - MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh); SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>( ".hide_poly", ATTR_DOMAIN_FACE); if (!hide_poly) { @@ -2141,7 +2133,11 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) hide_poly.span[i] = face_sets[i] < 0; } hide_poly.finish(); +} +void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) +{ + set_hide_poly_from_face_sets(*mesh); BKE_mesh_flush_hidden_from_polys(mesh); } @@ -2179,41 +2175,29 @@ void BKE_sculpt_sync_face_sets_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv void BKE_sculpt_sync_face_set_visibility(Mesh *mesh, SubdivCCG *subdiv_ccg) { - BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(mesh); + BKE_sculpt_face_sets_update_from_base_mesh_visibility(mesh); BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh); BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, subdiv_ccg); } -void BKE_sculpt_ensure_orig_mesh_data(Scene *scene, Object *object) +void BKE_sculpt_ensure_orig_mesh_data(Object *object) { Mesh *mesh = BKE_mesh_from_object(object); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, object); - BLI_assert(object->mode == OB_MODE_SCULPT); /* Copy the current mesh visibility to the Face Sets. */ - BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(mesh); - if (object->sculpt != nullptr) { - /* If a sculpt session is active, ensure we have its face-set data properly up-to-date. */ - object->sculpt->face_sets = static_cast<int *>( - CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)); - - /* NOTE: In theory we could add that on the fly when required by sculpt code. - * But this then requires proper update of depsgraph etc. For now we play safe, optimization is - * always possible later if it's worth it. */ - BKE_sculpt_mask_layers_ensure(object, mmd); - } + BKE_sculpt_face_sets_update_from_base_mesh_visibility(mesh); /* Tessfaces aren't used and will become invalid. */ BKE_mesh_tessface_clear(mesh); /* We always need to flush updates from depsgraph here, since at the very least - * `BKE_sculpt_face_sets_ensure_from_base_mesh_visibility()` will have updated some data layer of + * `BKE_sculpt_face_sets_update_from_base_mesh_visibility()` will have updated some data layer of * the mesh. * * All known potential sources of updates: * - Addition of, or changes to, the `CD_SCULPT_FACE_SETS` data layer - * (`BKE_sculpt_face_sets_ensure_from_base_mesh_visibility`). + * (`BKE_sculpt_face_sets_update_from_base_mesh_visibility`). * - Addition of a `CD_PAINT_MASK` data layer (`BKE_sculpt_mask_layers_ensure`). * - Object has any active modifier (modifier stack can be different in Sculpt mode). * - Multires: diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 33f4f650098..bbd462d5ae1 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -495,7 +495,7 @@ IDTypeInfo IDType_ID_PA = { .foreach_id = particle_settings_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = particle_settings_blend_write, .blend_read_data = particle_settings_blend_read_data, @@ -2123,8 +2123,8 @@ void psys_particle_on_dm(Mesh *mesh_final, const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh_final); if (from == PART_FROM_VERT) { - const MVert *vertices = BKE_mesh_verts(mesh_final); - copy_v3_v3(vec, vertices[mapindex].co); + const MVert *verts = BKE_mesh_verts(mesh_final); + copy_v3_v3(vec, verts[mapindex].co); if (nor) { copy_v3_v3(nor, vert_normals[mapindex]); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 1c6274ef35e..2e273e076d5 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -285,7 +285,7 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) } if (has_visible == false) { - if (!paint_is_face_hidden(lt, pbvh->hide_vert, pbvh->mloop)) { + if (!paint_is_face_hidden(lt, pbvh->hide_poly)) { has_visible = true; } } @@ -552,6 +552,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, pbvh->mesh = mesh; pbvh->header.type = PBVH_FACES; pbvh->mpoly = mpoly; + pbvh->hide_poly = (bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"); pbvh->material_indices = (const int *)CustomData_get_layer_named( &mesh->pdata, CD_PROP_INT32, "material_index"); pbvh->mloop = mloop; @@ -1313,11 +1314,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, } case PBVH_FACES: node->draw_buffers = GPU_pbvh_mesh_buffers_build( - pbvh->mesh, - pbvh->looptri, - CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS), - node->prim_indices, - node->totprim); + pbvh->mesh, pbvh->looptri, node->prim_indices, node->totprim); break; case PBVH_BMESH: node->draw_buffers = GPU_pbvh_bmesh_buffers_build(pbvh->flags & @@ -1831,7 +1828,7 @@ BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh) return pbvh->grid_hidden; } -int BKE_pbvh_get_grid_num_vertices(const PBVH *pbvh) +int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh) { BLI_assert(pbvh->header.type == PBVH_GRIDS); return pbvh->totgrid * pbvh->gridkey.grid_area; @@ -2293,7 +2290,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, const MLoopTri *lt = &pbvh->looptri[faces[i]]; const int *face_verts = node->face_vert_indices[i]; - if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) { + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) { continue; } @@ -2602,7 +2599,7 @@ static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh, const MLoopTri *lt = &pbvh->looptri[faces[i]]; const int *face_verts = node->face_vert_indices[i]; - if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) { + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) { continue; } @@ -3219,6 +3216,12 @@ const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh) return pbvh->hide_vert; } +const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->hide_poly; +} + bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh) { BLI_assert(pbvh->header.type == PBVH_FACES); @@ -3244,6 +3247,14 @@ void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets) pbvh->face_sets = face_sets; } +void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh) +{ + if (pbvh->header.type == PBVH_FACES) { + pbvh->hide_vert = CustomData_get_layer_named(&pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + pbvh->hide_poly = CustomData_get_layer_named(&pbvh->mesh->pdata, CD_PROP_BOOL, ".hide_poly"); + } +} + void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide) { pbvh->respect_hide = respect_hide; diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index b848327b7a9..8ab56839f9c 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -156,6 +156,7 @@ struct PBVH { bool *hide_vert; struct MVert *verts; const struct MPoly *mpoly; + bool *hide_poly; /** Material indices. Only valid for polygon meshes. */ const int *material_indices; const struct MLoop *mloop; diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 14ca3f58db9..b45e164b594 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -175,7 +175,7 @@ IDTypeInfo IDType_ID_PT = { /* foreach_id */ pointcloud_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ nullptr, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ pointcloud_blend_write, /* blend_read_data */ pointcloud_blend_read_data, @@ -189,13 +189,13 @@ IDTypeInfo IDType_ID_PT = { static void pointcloud_random(PointCloud *pointcloud) { + BLI_assert(pointcloud->totpoint == 0); pointcloud->totpoint = 400; - CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); + CustomData_realloc(&pointcloud->pdata, 0, pointcloud->totpoint); RNG *rng = BLI_rng_new(0); - blender::bke::MutableAttributeAccessor attributes = - blender::bke::pointcloud_attributes_for_write(*pointcloud); + blender::bke::MutableAttributeAccessor attributes = pointcloud->attributes_for_write(); blender::bke::SpanAttributeWriter positions = attributes.lookup_or_add_for_write_only_span<float3>(POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT); @@ -239,9 +239,6 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE)); pointcloud_init_data(&pointcloud->id); - - pointcloud->totpoint = totpoint; - CustomData_add_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, CD_SET_DEFAULT, @@ -249,8 +246,8 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) pointcloud->totpoint, POINTCLOUD_ATTR_RADIUS); + CustomData_realloc(&pointcloud->pdata, 0, totpoint); pointcloud->totpoint = totpoint; - CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); return pointcloud; } @@ -258,7 +255,7 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds( const PointCloud &pointcloud) { - blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(pointcloud); + blender::bke::AttributeAccessor attributes = pointcloud.attributes(); blender::VArraySpan<float3> positions = attributes.lookup_or_default<float3>( POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT, float3(0)); blender::VArray<float> radii = attributes.lookup_or_default<float>( diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 3416d79dc42..db950492f69 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1675,7 +1675,7 @@ constexpr IDTypeInfo get_type_info() info.foreach_id = scene_foreach_id; info.foreach_cache = scene_foreach_cache; info.foreach_path = scene_foreach_path; - info.owner_get = nullptr; + info.owner_pointer_get = nullptr; info.blend_write = scene_blend_write; info.blend_read_data = scene_blend_read_data; diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index f1eba64e401..40348824a13 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -292,7 +292,7 @@ IDTypeInfo IDType_ID_SCR = { .foreach_id = screen_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = screen_blend_write, /* Cannot be used yet, because #direct_link_screen has a return value. */ diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 90cbb083e77..9d4d6a4e350 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -149,7 +149,7 @@ IDTypeInfo IDType_ID_SIM = { /* foreach_id */ simulation_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ nullptr, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ simulation_blend_write, /* blend_read_data */ simulation_blend_read_data, diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 6ca598a3688..d1451353feb 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2632,7 +2632,7 @@ static void springs_from_mesh(Object *ob) BodyPoint *bp; int a; float scale = 1.0f; - const MVert *vertices = BKE_mesh_verts(me); + const MVert *verts = BKE_mesh_verts(me); sb = ob->soft; if (me && sb) { @@ -2643,7 +2643,7 @@ static void springs_from_mesh(Object *ob) if (me->totvert) { bp = ob->soft->bpoint; for (a = 0; a < me->totvert; a++, bp++) { - copy_v3_v3(bp->origS, vertices[a].co); + copy_v3_v3(bp->origS, verts[a].co); mul_m4_v3(ob->obmat, bp->origS); } } @@ -2755,15 +2755,15 @@ static void mesh_faces_to_scratch(Object *ob) MLoopTri *looptri, *lt; BodyFace *bodyface; int a; - const MVert *vertices = BKE_mesh_verts(me); - const MPoly *polygons = BKE_mesh_polys(me); + const MVert *verts = BKE_mesh_verts(me); + const MPoly *polys = BKE_mesh_polys(me); const MLoop *loops = BKE_mesh_loops(me); /* Allocate and copy faces. */ sb->scratch->totface = poly_to_tri_count(me->totpoly, me->totloop); looptri = lt = MEM_mallocN(sizeof(*looptri) * sb->scratch->totface, __func__); - BKE_mesh_recalc_looptri(loops, polygons, vertices, me->totloop, me->totpoly, looptri); + BKE_mesh_recalc_looptri(loops, polys, verts, me->totloop, me->totpoly, looptri); bodyface = sb->scratch->bodyface = MEM_mallocN(sizeof(BodyFace) * sb->scratch->totface, "SB_body_Faces"); diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index bb0e7a4dd6b..de1d0d3c30e 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -211,7 +211,7 @@ IDTypeInfo IDType_ID_SO = { .foreach_id = NULL, .foreach_cache = sound_foreach_cache, .foreach_path = sound_foreach_path, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = sound_blend_write, .blend_read_data = sound_blend_read_data, diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index a8b76954a6f..3d49176d00a 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -94,7 +94,7 @@ IDTypeInfo IDType_ID_SPK = { .foreach_id = speaker_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = speaker_blend_write, .blend_read_data = speaker_blend_read_data, diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index f17450ac3f4..64f998ea67f 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -1166,19 +1166,21 @@ static void studiolight_add_files_from_datafolder(const int folder_id, const char *subfolder, int flag) { - struct direntry *dirs; const char *folder = BKE_appdir_folder_id(folder_id, subfolder); - if (folder) { - const uint dirs_num = BLI_filelist_dir_contents(folder, &dirs); - int i; - for (i = 0; i < dirs_num; i++) { - if (dirs[i].type & S_IFREG) { - studiolight_add_file(dirs[i].path, flag); - } + if (!folder) { + return; + } + + struct direntry *dirs; + const uint dirs_num = BLI_filelist_dir_contents(folder, &dirs); + int i; + for (i = 0; i < dirs_num; i++) { + if (dirs[i].type & S_IFREG) { + studiolight_add_file(dirs[i].path, flag); } - BLI_filelist_free(dirs, dirs_num); - dirs = NULL; } + BLI_filelist_free(dirs, dirs_num); + dirs = NULL; } static int studiolight_flag_cmp_order(const StudioLight *sl) diff --git a/source/blender/blenkernel/intern/subdiv_mesh.cc b/source/blender/blenkernel/intern/subdiv_mesh.cc index 5a2af36e83c..44bdd6e6d06 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.cc +++ b/source/blender/blenkernel/intern/subdiv_mesh.cc @@ -5,6 +5,8 @@ * \ingroup bke */ +#include <mutex> + #include "atomic_ops.h" #include "DNA_key_types.h" @@ -18,6 +20,7 @@ #include "BKE_customdata.h" #include "BKE_key.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" #include "BKE_subdiv.h" #include "BKE_subdiv_eval.h" #include "BKE_subdiv_foreach.h" @@ -25,6 +28,8 @@ #include "MEM_guardedalloc.h" +using blender::Span; + /* -------------------------------------------------------------------- */ /** \name Subdivision Context * \{ */ @@ -58,6 +63,11 @@ struct SubdivMeshContext { /* Per-subdivided vertex counter of averaged values. */ int *accumulated_counters; bool have_displacement; + + /* Lazily initialize a map from vertices to connected edges. */ + std::mutex vert_to_edge_map_mutex; + int *vert_to_edge_buffer; + MeshElemMap *vert_to_edge_map; }; static void subdiv_mesh_ctx_cache_uv_layers(SubdivMeshContext *ctx) @@ -106,6 +116,8 @@ static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vert static void subdiv_mesh_context_free(SubdivMeshContext *ctx) { MEM_SAFE_FREE(ctx->accumulated_counters); + MEM_SAFE_FREE(ctx->vert_to_edge_buffer); + MEM_SAFE_FREE(ctx->vert_to_edge_map); } /** \} */ @@ -779,7 +791,6 @@ static void subdiv_copy_edge_data(SubdivMeshContext *ctx, const int subdiv_edge_index = subdiv_edge - ctx->subdiv_edges; if (coarse_edge == nullptr) { subdiv_edge->crease = 0; - subdiv_edge->bweight = 0; subdiv_edge->flag = 0; if (!ctx->settings->use_optimal_display) { subdiv_edge->flag |= ME_EDGERENDER; @@ -961,25 +972,30 @@ static void subdiv_mesh_vertex_loose(const SubdivForeachContext *foreach_context /* Get neighbor edges of the given one. * - neighbors[0] is an edge adjacent to edge->v1. * - neighbors[1] is an edge adjacent to edge->v2. */ -static void find_edge_neighbors(const Mesh *coarse_mesh, - const MEdge *edge, +static void find_edge_neighbors(const MEdge *coarse_edges, + const MeshElemMap *vert_to_edge_map, + const int edge_index, const MEdge *neighbors[2]) { - const blender::Span<MEdge> coarse_edges = coarse_mesh->edges(); + const MEdge *edge = &coarse_edges[edge_index]; neighbors[0] = nullptr; neighbors[1] = nullptr; int neighbor_counters[2] = {0, 0}; - for (int edge_index = 0; edge_index < coarse_mesh->totedge; edge_index++) { - const MEdge *current_edge = &coarse_edges[edge_index]; - if (current_edge == edge) { + for (const int i : Span(vert_to_edge_map[edge->v1].indices, vert_to_edge_map[edge->v1].count)) { + if (i == edge_index) { continue; } - if (ELEM(edge->v1, current_edge->v1, current_edge->v2)) { - neighbors[0] = current_edge; + if (ELEM(edge->v1, coarse_edges[i].v1, coarse_edges[i].v2)) { + neighbors[0] = &coarse_edges[i]; ++neighbor_counters[0]; } - if (ELEM(edge->v2, current_edge->v1, current_edge->v2)) { - neighbors[1] = current_edge; + } + for (const int i : Span(vert_to_edge_map[edge->v2].indices, vert_to_edge_map[edge->v2].count)) { + if (i == edge_index) { + continue; + } + if (ELEM(edge->v2, coarse_edges[i].v1, coarse_edges[i].v2)) { + neighbors[1] = &coarse_edges[i]; ++neighbor_counters[1]; } } @@ -994,12 +1010,11 @@ static void find_edge_neighbors(const Mesh *coarse_mesh, } } -static void points_for_loose_edges_interpolation_get(const Mesh *coarse_mesh, +static void points_for_loose_edges_interpolation_get(const MVert *coarse_mvert, const MEdge *coarse_edge, const MEdge *neighbors[2], float points_r[4][3]) { - const MVert *coarse_mvert = BKE_mesh_verts(coarse_mesh); /* Middle points corresponds to the edge. */ copy_v3_v3(points_r[1], coarse_mvert[coarse_edge->v1].co); copy_v3_v3(points_r[2], coarse_mvert[coarse_edge->v2].co); @@ -1031,24 +1046,26 @@ static void points_for_loose_edges_interpolation_get(const Mesh *coarse_mesh, } } -void BKE_subdiv_mesh_interpolate_position_on_edge(const Mesh *coarse_mesh, - const MEdge *coarse_edge, +void BKE_subdiv_mesh_interpolate_position_on_edge(const MVert *coarse_verts, + const MEdge *coarse_edges, + const MeshElemMap *vert_to_edge_map, + const int coarse_edge_index, const bool is_simple, const float u, float pos_r[3]) { + const MEdge *coarse_edge = &coarse_edges[coarse_edge_index]; if (is_simple) { - const MVert *coarse_mvert = BKE_mesh_verts(coarse_mesh); - const MVert *vert_1 = &coarse_mvert[coarse_edge->v1]; - const MVert *vert_2 = &coarse_mvert[coarse_edge->v2]; + const MVert *vert_1 = &coarse_verts[coarse_edge->v1]; + const MVert *vert_2 = &coarse_verts[coarse_edge->v2]; interp_v3_v3v3(pos_r, vert_1->co, vert_2->co, u); } else { /* Find neighbors of the coarse edge. */ const MEdge *neighbors[2]; - find_edge_neighbors(coarse_mesh, coarse_edge, neighbors); + find_edge_neighbors(coarse_edges, vert_to_edge_map, coarse_edge_index, neighbors); float points[4][3]; - points_for_loose_edges_interpolation_get(coarse_mesh, coarse_edge, neighbors, points); + points_for_loose_edges_interpolation_get(coarse_verts, coarse_edge, neighbors, points); float weights[4]; key_curve_position_weights(u, weights, KEY_BSPLINE); interp_v3_v3v3v3v3(pos_r, points[0], points[1], points[2], points[3], weights); @@ -1090,6 +1107,20 @@ static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach const Mesh *coarse_mesh = ctx->coarse_mesh; const MEdge *coarse_edge = &ctx->coarse_edges[coarse_edge_index]; const bool is_simple = ctx->subdiv->settings.is_simple; + + /* Lazily initialize a vertex to edge map to avoid quadratic runtime when subdividing loose + * edges. Do this here to avoid the cost in common cases when there are no loose edges at all. */ + if (ctx->vert_to_edge_map == NULL) { + std::lock_guard lock{ctx->vert_to_edge_map_mutex}; + if (ctx->vert_to_edge_map == NULL) { + BKE_mesh_vert_edge_map_create(&ctx->vert_to_edge_map, + &ctx->vert_to_edge_buffer, + ctx->coarse_edges, + coarse_mesh->totvert, + ctx->coarse_mesh->totedge); + } + } + /* Interpolate custom data when not an end point. * This data has already been copied from the original vertex by #subdiv_mesh_vertex_loose. */ if (!ELEM(u, 0.0, 1.0)) { @@ -1097,13 +1128,15 @@ static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach } /* Interpolate coordinate. */ MVert *subdiv_vertex = &ctx->subdiv_verts[subdiv_vertex_index]; - BKE_subdiv_mesh_interpolate_position_on_edge( - coarse_mesh, coarse_edge, is_simple, u, subdiv_vertex->co); + BKE_subdiv_mesh_interpolate_position_on_edge(ctx->coarse_verts, + ctx->coarse_edges, + ctx->vert_to_edge_map, + coarse_edge_index, + is_simple, + u, + subdiv_vertex->co); /* Reset flags and such. */ subdiv_vertex->flag = 0; - /* TODO(sergey): This matches old behavior, but we can as well interpolate - * it. Maybe even using vertex varying attributes. */ - subdiv_vertex->bweight = 0.0f; } /** \} */ diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 88c260be9ba..0e5f9f30243 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -879,7 +879,7 @@ static void ccgDM_getFinalVertNo(DerivedMesh *dm, int vertNum, float r_no[3]) BLI_INLINE void ccgDM_to_MVert(MVert *mv, const CCGKey *key, CCGElem *elem) { copy_v3_v3(mv->co, CCG_elem_co(key, elem)); - mv->flag = mv->bweight = 0; + mv->flag = 0; } static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert) @@ -949,7 +949,7 @@ BLI_INLINE void ccgDM_to_MEdge(MEdge *med, const int v1, const int v2, const sho { med->v1 = v1; med->v2 = v2; - med->crease = med->bweight = 0; + med->crease = 0; med->flag = flag; } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 9acf387b930..1444ba1e1c4 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -245,7 +245,7 @@ IDTypeInfo IDType_ID_TXT = { .foreach_id = NULL, .foreach_cache = NULL, .foreach_path = text_foreach_path, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = text_blend_write, .blend_read_data = text_blend_read_data, diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 8f64296da5a..5cdaa12f514 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -207,7 +207,7 @@ IDTypeInfo IDType_ID_TE = { .foreach_id = texture_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = texture_blend_write, .blend_read_data = texture_blend_read_data, diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index e016cf8db84..288493c9d65 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -171,7 +171,7 @@ IDTypeInfo IDType_ID_VF = { .foreach_id = NULL, .foreach_cache = NULL, .foreach_path = vfont_foreach_path, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = vfont_blend_write, .blend_read_data = vfont_blend_read_data, diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index d7762400f64..502d3ac6d40 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -661,7 +661,7 @@ IDTypeInfo IDType_ID_VO = { /* foreach_id */ volume_foreach_id, /* foreach_cache */ volume_foreach_cache, /* foreach_path */ volume_foreach_path, - /* owner_get */ nullptr, + /* owner_pointer_get */ nullptr, /* blend_write */ volume_blend_write, /* blend_read_data */ volume_blend_read_data, diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 88e7db1fe6c..f59be7f0111 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -193,7 +193,7 @@ IDTypeInfo IDType_ID_WS = { .foreach_id = workspace_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = workspace_blend_write, .blend_read_data = workspace_blend_read_data, diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 5220577afbd..c6c50ee068c 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -198,7 +198,7 @@ IDTypeInfo IDType_ID_WO = { .foreach_id = world_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = world_blend_write, .blend_read_data = world_blend_read_data, diff --git a/source/blender/blenlib/BLI_array_utils.hh b/source/blender/blenlib/BLI_array_utils.hh new file mode 100644 index 00000000000..dd65147a926 --- /dev/null +++ b/source/blender/blenlib/BLI_array_utils.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_generic_span.hh" +#include "BLI_generic_virtual_array.hh" +#include "BLI_index_mask.hh" +#include "BLI_task.hh" + +namespace blender::array_utils { + +/** + * Fill the destination span by copying masked values from the src array. Threaded based on + * grainsize. + */ +void copy(const GVArray &src, IndexMask selection, GMutableSpan dst, int64_t grain_size = 4096); + +/** + * Fill the destination span by copying values from the src array. Threaded based on + * grainsize. + */ +template<typename T> +inline void copy(const Span<T> src, + const IndexMask selection, + MutableSpan<T> dst, + const int64_t grain_size = 4096) +{ + threading::parallel_for(selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int64_t index : selection.slice(range)) { + dst[index] = src[index]; + } + }); +} + +} // namespace blender::array_utils diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 3cbd2483a31..2cec190f84a 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -196,7 +196,7 @@ class BitVector { /** Current size of the vector in bits. */ int64_t size_in_bits_; - /** Number of bits that fit into the vector until a reallocation has to occure. */ + /** Number of bits that fit into the vector until a reallocation has to occur. */ int64_t capacity_in_bits_; /** Used for allocations when the inline buffer is too small. */ diff --git a/source/blender/blenlib/BLI_compute_context.hh b/source/blender/blenlib/BLI_compute_context.hh new file mode 100644 index 00000000000..7422467e400 --- /dev/null +++ b/source/blender/blenlib/BLI_compute_context.hh @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * When logging computed values, we generally want to know where the value was computed. For + * example, geometry nodes logs socket values so that they can be displayed in the ui. For that we + * can combine the logged value with a `ComputeContext`, which identifies the place where the value + * was computed. + * + * This is not a trivial problem because e.g. just storing storing a pointer to the socket a value + * belongs to is not enough. That's because the same socket may correspond to many different values + * when the socket is used in a node group that is used multiple times. In this case, not only does + * the socket have to be stored but also the entire nested node group path that led to the + * evaluation of the socket. + * + * Storing the entire "context path" for every logged value is not feasible, because that path can + * become quite long. So that would need much more memory, more compute overhead and makes it + * complicated to compare if two contexts are the same. If the identifier for a compute context + * would have a variable size, it would also be much harder to create a map from context to values. + * + * The solution implemented below uses the following key ideas: + * - Every compute context can be hashed to a unique fixed size value (`ComputeContextHash`). While + * technically there could be hash collisions, the hashing algorithm has to be chosen to make + * that practically impossible. This way an entire context path, possibly consisting of many + * nested contexts, is represented by a single value that can be stored easily. + * - A nested compute context is build as singly linked list, where every compute context has a + * pointer to the parent compute context. Note that a link in the other direction is not possible + * because the same parent compute context may be used by many different children which possibly + * run on different threads. + */ + +#include "BLI_array.hh" +#include "BLI_linear_allocator.hh" +#include "BLI_stack.hh" +#include "BLI_string_ref.hh" + +namespace blender { + +/** + * A hash that uniquely identifies a specific (non-fixed-size) compute context. The hash has to + * have enough bits to make collisions practically impossible. + */ +struct ComputeContextHash { + static constexpr int64_t HashSizeInBytes = 16; + uint64_t v1 = 0; + uint64_t v2 = 0; + + uint64_t hash() const + { + return v1; + } + + friend bool operator==(const ComputeContextHash &a, const ComputeContextHash &b) + { + return a.v1 == b.v1 && a.v2 == b.v2; + } + + void mix_in(const void *data, int64_t len); + + friend std::ostream &operator<<(std::ostream &stream, const ComputeContextHash &hash); +}; + +static_assert(sizeof(ComputeContextHash) == ComputeContextHash::HashSizeInBytes); + +/** + * Identifies the context in which a computation happens. This context can be used to identify + * values logged during the computation. For more details, see the comment at the top of the file. + * + * This class should be subclassed to implement specific contexts. + */ +class ComputeContext { + private: + /** + * Only used for debugging currently. + */ + const char *static_type_; + /** + * Pointer to the context that this context is child of. That allows nesting compute contexts. + */ + const ComputeContext *parent_ = nullptr; + + protected: + /** + * The hash that uniquely identifies this context. It's a combined hash of this context as well + * as all the parent contexts. + */ + ComputeContextHash hash_; + + public: + ComputeContext(const char *static_type, const ComputeContext *parent) + : static_type_(static_type), parent_(parent) + { + if (parent != nullptr) { + hash_ = parent_->hash_; + } + } + virtual ~ComputeContext() = default; + + const ComputeContextHash &hash() const + { + return hash_; + } + + const char *static_type() const + { + return static_type_; + } + + const ComputeContext *parent() const + { + return parent_; + } + + /** + * Print the entire nested context stack. + */ + void print_stack(std::ostream &stream, StringRef name) const; + + /** + * Print information about this specific context. This has to be implemented by each subclass. + */ + virtual void print_current_in_line(std::ostream &stream) const = 0; + + friend std::ostream &operator<<(std::ostream &stream, const ComputeContext &compute_context); +}; + +/** + * Utility class to build a context stack in one place. This is typically used to get the hash that + * corresponds to a specific nested compute context, in order to look up corresponding logged + * values. + */ +class ComputeContextBuilder { + private: + LinearAllocator<> allocator_; + Stack<destruct_ptr<ComputeContext>> contexts_; + + public: + bool is_empty() const + { + return contexts_.is_empty(); + } + + const ComputeContext *current() const + { + if (contexts_.is_empty()) { + return nullptr; + } + return contexts_.peek().get(); + } + + const ComputeContextHash hash() const + { + BLI_assert(!contexts_.is_empty()); + return this->current()->hash(); + } + + template<typename T, typename... Args> void push(Args &&...args) + { + const ComputeContext *current = this->current(); + destruct_ptr<T> context = allocator_.construct<T>(current, std::forward<Args>(args)...); + contexts_.push(std::move(context)); + } + + void pop() + { + contexts_.pop(); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_float3x3.hh b/source/blender/blenlib/BLI_float3x3.hh index 6a9e7dd04f0..178973c155d 100644 --- a/source/blender/blenlib/BLI_float3x3.hh +++ b/source/blender/blenlib/BLI_float3x3.hh @@ -63,6 +63,15 @@ struct float3x3 { return result; } + static float3x3 from_scale(const float2 scale) + { + float3x3 result = zero(); + result.values[0][0] = scale.x; + result.values[1][1] = scale.y; + result.values[2][2] = 1.0f; + return result; + } + static float3x3 from_translation_rotation_scale(const float2 translation, float rotation, const float2 scale) @@ -190,6 +199,13 @@ struct float3x3 { return result; } + float2 scale_2d() const + { + float2 scale; + mat3_to_size_2d(scale, values); + return scale; + } + friend bool operator==(const float3x3 &a, const float3x3 &b) { return equals_m3m3(a.values, b.values); diff --git a/source/blender/blenlib/BLI_generic_span.hh b/source/blender/blenlib/BLI_generic_span.hh index 143ab235d2e..e7a08988c46 100644 --- a/source/blender/blenlib/BLI_generic_span.hh +++ b/source/blender/blenlib/BLI_generic_span.hh @@ -100,6 +100,34 @@ class GSpan { { return this->slice(range.start(), range.size()); } + + GSpan drop_front(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::max<int64_t>(0, size_ - n); + return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * n), new_size); + } + + GSpan drop_back(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::max<int64_t>(0, size_ - n); + return GSpan(*type_, data_, new_size); + } + + GSpan take_front(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::min<int64_t>(size_, n); + return GSpan(*type_, data_, new_size); + } + + GSpan take_back(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::min<int64_t>(size_, n); + return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * (size_ - new_size)), new_size); + } }; /** @@ -199,6 +227,35 @@ class GMutableSpan { return this->slice(range.start(), range.size()); } + GMutableSpan drop_front(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::max<int64_t>(0, size_ - n); + return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * n), new_size); + } + + GMutableSpan drop_back(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::max<int64_t>(0, size_ - n); + return GMutableSpan(*type_, data_, new_size); + } + + GMutableSpan take_front(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::min<int64_t>(size_, n); + return GMutableSpan(*type_, data_, new_size); + } + + GMutableSpan take_back(const int64_t n) const + { + BLI_assert(n >= 0); + const int64_t new_size = std::min<int64_t>(size_, n); + return GMutableSpan( + *type_, POINTER_OFFSET(data_, type_->size() * (size_ - new_size)), new_size); + } + /** * Copy all values from another span into this span. This invokes undefined behavior when the * destination contains uninitialized data and T is not trivially copy constructible. diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 467e6db4805..19943614881 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -410,6 +410,8 @@ float mat4_to_xy_scale(const float mat[4][4]); void size_to_mat3(float R[3][3], const float size[3]); void size_to_mat4(float R[4][4], const float size[3]); +/** Return 2D size assuming the given matrix is a 2D affine matrix. */ +void mat3_to_size_2d(float size[2], const float M[3][3]); void mat3_to_size(float size[3], const float M[3][3]); void mat4_to_size(float size[3], const float M[4][4]); diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh index 1fc5a797574..81b536e7d3c 100644 --- a/source/blender/blenlib/BLI_multi_value_map.hh +++ b/source/blender/blenlib/BLI_multi_value_map.hh @@ -115,6 +115,14 @@ template<typename Key, typename Value> class MultiValueMap { } /** + * Get the number of keys. + */ + int64_t size() const + { + return map_.size(); + } + + /** * NOTE: This signature will change when the implementation changes. */ typename MapType::ItemIterator items() const diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 06dd9ab0db9..136258e50f2 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -36,16 +36,6 @@ void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1); const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** - * Returns in `string` the concatenation of `dir` and `file` (also with `relabase` on the - * front if specified and `dir` begins with "//"). Normalizes all occurrences of path - * separators, including ensuring there is exactly one between the copies of `dir` and `file`, - * and between the copies of `relabase` and `dir`. - * - * \param relabase: Optional prefix to substitute for "//" on front of `dir`. - * \param string: Area to return result. - */ -void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file); -/** * Ensures that the parent directory of `name` exists. * * \return true on success (i.e. given path now exists on file-system), false otherwise. @@ -94,10 +84,18 @@ void BLI_join_dirfile(char *__restrict dst, * Join multiple strings into a path, ensuring only a single path separator between each, * and trailing slash is kept. * + * \param path: The first patch which has special treatment, + * allowing `//` prefix which is kept intact unlike double-slashes which are stripped + * from the bounds of all other paths passed in. + * Passing in the following paths all result in the same output (`//a/b/c`): + * - `"//", "a", "b", "c"`. + * - `"//", "/a/", "/b/", "/c"`. + * - `"//a", "b/c"`. + * * \note If you want a trailing slash, add `SEP_STR` as the last path argument, * duplicate slashes will be cleaned up. */ -size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path_first, ...) +size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path, ...) ATTR_NONNULL(1, 3) ATTR_SENTINEL(0); /** * Like Python's `os.path.basename()` diff --git a/source/blender/blenlib/BLI_probing_strategies.hh b/source/blender/blenlib/BLI_probing_strategies.hh index c6152e4d03d..2c001270495 100644 --- a/source/blender/blenlib/BLI_probing_strategies.hh +++ b/source/blender/blenlib/BLI_probing_strategies.hh @@ -2,6 +2,8 @@ #pragma once +#include <numeric> + /** \file * \ingroup bli * @@ -20,7 +22,7 @@ * clustering issues. However, more linear steps can also make things slower when the initial hash * produces many collisions. * - * Every probing strategy has to guarantee, that every possible uint64_t is returned eventually. + * Every probing strategy has to guarantee that every possible uint64_t is returned eventually. * This is necessary for correctness. If this is not the case, empty slots might not be found. * * The SLOT_PROBING_BEGIN and SLOT_PROBING_END macros can be used to implement a loop that iterates @@ -69,7 +71,7 @@ class LinearProbingStrategy { int64_t linear_steps() const { - return UINT32_MAX; + return std::numeric_limits<int64_t>::max(); } }; diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index d87c60e6099..470ffebcad4 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -23,7 +23,6 @@ set(INC_SYS ) set(SRC - intern/BLI_args.c intern/BLI_array.c intern/BLI_assert.c intern/BLI_color.cc @@ -48,11 +47,13 @@ set(SRC intern/array_store.c intern/array_store_utils.c intern/array_utils.c + intern/array_utils.cc intern/astar.c intern/bitmap.c intern/bitmap_draw_2d.c intern/boxpack_2d.c intern/buffer.c + intern/compute_context.cc intern/convexhull_2d.c intern/cpp_type.cc intern/delaunay_2d.cc @@ -159,12 +160,12 @@ set(SRC BLI_alloca.h BLI_allocator.hh BLI_any.hh - BLI_args.h BLI_array.h BLI_array.hh BLI_array_store.h BLI_array_store_utils.h BLI_array_utils.h + BLI_array_utils.hh BLI_asan.h BLI_assert.h BLI_astar.h @@ -180,6 +181,7 @@ set(SRC BLI_compiler_attrs.h BLI_compiler_compat.h BLI_compiler_typecheck.h + BLI_compute_context.hh BLI_console.h BLI_convexhull_2d.h BLI_cpp_type.hh @@ -353,6 +355,14 @@ set(LIB ${ZSTD_LIBRARIES} ) +if(NOT WITH_PYTHON_MODULE) + list(APPEND SRC + intern/BLI_args.c + + BLI_args.h + ) +endif() + if(WITH_MEM_VALGRIND) add_definitions(-DWITH_MEM_VALGRIND) endif() diff --git a/source/blender/blenlib/intern/array_utils.cc b/source/blender/blenlib/intern/array_utils.cc new file mode 100644 index 00000000000..d4266295944 --- /dev/null +++ b/source/blender/blenlib/intern/array_utils.cc @@ -0,0 +1,18 @@ +#include "BLI_array_utils.hh" +#include "BLI_task.hh" + +namespace blender::array_utils { + +void copy(const GVArray &src, + const IndexMask selection, + GMutableSpan dst, + const int64_t grain_size) +{ + BLI_assert(src.type() == dst.type()); + BLI_assert(src.size() == dst.size()); + threading::parallel_for(selection.index_range(), grain_size, [&](const IndexRange range) { + src.materialize_to_uninitialized(selection.slice(range), dst.data()); + }); +} + +} // namespace blender::array_utils diff --git a/source/blender/blenlib/intern/compute_context.cc b/source/blender/blenlib/intern/compute_context.cc new file mode 100644 index 00000000000..50a4a90a4a9 --- /dev/null +++ b/source/blender/blenlib/intern/compute_context.cc @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_compute_context.hh" +#include "BLI_hash_md5.h" + +namespace blender { + +void ComputeContextHash::mix_in(const void *data, int64_t len) +{ + DynamicStackBuffer<> buffer_owner(HashSizeInBytes + len, 8); + char *buffer = static_cast<char *>(buffer_owner.buffer()); + memcpy(buffer, this, HashSizeInBytes); + memcpy(buffer + HashSizeInBytes, data, len); + + BLI_hash_md5_buffer(buffer, HashSizeInBytes + len, this); +} + +std::ostream &operator<<(std::ostream &stream, const ComputeContextHash &hash) +{ + std::stringstream ss; + ss << "0x" << std::hex << hash.v1 << hash.v2; + stream << ss.str(); + return stream; +} + +void ComputeContext::print_stack(std::ostream &stream, StringRef name) const +{ + Stack<const ComputeContext *> stack; + for (const ComputeContext *current = this; current; current = current->parent_) { + stack.push(current); + } + stream << "Context Stack: " << name << "\n"; + while (!stack.is_empty()) { + const ComputeContext *current = stack.pop(); + stream << "-> "; + current->print_current_in_line(stream); + const ComputeContextHash ¤t_hash = current->hash_; + stream << " \t(hash: " << current_hash << ")\n"; + } +} + +std::ostream &operator<<(std::ostream &stream, const ComputeContext &compute_context) +{ + compute_context.print_stack(stream, ""); + return stream; +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/cpp_type.cc b/source/blender/blenlib/intern/cpp_type.cc index d6a087cf175..38de32d3ec8 100644 --- a/source/blender/blenlib/intern/cpp_type.cc +++ b/source/blender/blenlib/intern/cpp_type.cc @@ -26,3 +26,4 @@ BLI_CPP_TYPE_MAKE(ColorGeometry4f, blender::ColorGeometry4f, CPPTypeFlags::Basic BLI_CPP_TYPE_MAKE(ColorGeometry4b, blender::ColorGeometry4b, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(string, std::string, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(StringVector, blender::Vector<std::string>, CPPTypeFlags::None) diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index e96b12033a9..221ae84e74d 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -2127,6 +2127,12 @@ void size_to_mat4(float R[4][4], const float size[3]) R[3][3] = 1.0f; } +void mat3_to_size_2d(float size[2], const float M[3][3]) +{ + size[0] = len_v2(M[0]); + size[1] = len_v2(M[1]); +} + void mat3_to_size(float size[3], const float M[3][3]) { size[0] = len_v3(M[0]); diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 623dd572b11..c053c3907db 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1204,87 +1204,6 @@ bool BLI_make_existing_file(const char *name) return BLI_dir_create_recursive(di); } -void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file) -{ - int sl; - - if (string) { - /* ensure this is always set even if dir/file are NULL */ - string[0] = '\0'; - - if (ELEM(NULL, dir, file)) { - return; /* We don't want any NULLs */ - } - } - else { - return; /* string is NULL, probably shouldn't happen but return anyway */ - } - - /* Resolve relative references */ - if (relabase && dir[0] == '/' && dir[1] == '/') { - char *lslash; - - /* Get the file name, chop everything past the last slash (ie. the filename) */ - strcpy(string, relabase); - - lslash = (char *)BLI_path_slash_rfind(string); - if (lslash) { - *(lslash + 1) = 0; - } - - dir += 2; /* Skip over the relative reference */ - } -#ifdef WIN32 - else { - if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':') { - BLI_strncpy(string, dir, 3); - dir += 2; - } - else if (BLI_strnlen(dir, 3) >= 2 && BLI_path_is_unc(dir)) { - string[0] = 0; - } - else { /* no drive specified */ - /* first option: get the drive from the relabase if it has one */ - if (relabase && BLI_strnlen(relabase, 3) >= 2 && relabase[1] == ':') { - BLI_strncpy(string, relabase, 3); - string[2] = '\\'; - string[3] = '\0'; - } - else { /* we're out of luck here, guessing the first valid drive, usually c:\ */ - BLI_windows_get_default_root_dir(string); - } - - /* ignore leading slashes */ - while (ELEM(*dir, '/', '\\')) { - dir++; - } - } - } -#endif - - strcat(string, dir); - - /* Make sure string ends in one (and only one) slash */ - /* first trim all slashes from the end of the string */ - sl = strlen(string); - while ((sl > 0) && ELEM(string[sl - 1], '/', '\\')) { - string[sl - 1] = '\0'; - sl--; - } - /* since we've now removed all slashes, put back one slash at the end. */ - strcat(string, "/"); - - while (ELEM(*file, '/', '\\')) { - /* Trim slashes from the front of file */ - file++; - } - - strcat(string, file); - - /* Push all slashes to the system preferred direction */ - BLI_path_slash_native(string); -} - static bool path_extension_check_ex(const char *str, const size_t str_len, const char *ext, @@ -1586,8 +1505,8 @@ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *pat return ofs; } - /* remove trailing slashes, unless there are _only_ trailing slashes - * (allow "//" as the first argument). */ + /* Remove trailing slashes, unless there are *only* trailing slashes + * (allow `//` or `//some_path` as the first argument). */ bool has_trailing_slash = false; if (ofs != 0) { size_t len = ofs; diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 781b38f713a..f7249e491d7 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -154,12 +154,12 @@ void BLI_hostname_get(char *buffer, size_t bufsize) if (gethostname(buffer, bufsize - 1) < 0) { BLI_strncpy(buffer, "-unknown-", bufsize); } - /* When gethostname() truncates, it doesn't guarantee the trailing \0. */ + /* When `gethostname()` truncates, it doesn't guarantee the trailing `\0`. */ buffer[bufsize - 1] = '\0'; #else DWORD bufsize_inout = bufsize; if (!GetComputerName(buffer, &bufsize_inout)) { - strncpy(buffer, "-unknown-", bufsize); + BLI_strncpy(buffer, "-unknown-", bufsize); } #endif } diff --git a/source/blender/blenlib/tests/BLI_bit_vector_test.cc b/source/blender/blenlib/tests/BLI_bit_vector_test.cc index c477b464f0c..210f2be012d 100644 --- a/source/blender/blenlib/tests/BLI_bit_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_vector_test.cc @@ -1,4 +1,4 @@ -/* Apache License, Version 2.0 */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "BLI_bit_vector.hh" #include "BLI_exception_safety_test_utils.hh" diff --git a/source/blender/blenlib/tests/BLI_float3x3_test.cc b/source/blender/blenlib/tests/BLI_float3x3_test.cc index d22993ee69e..cd823b6e368 100644 --- a/source/blender/blenlib/tests/BLI_float3x3_test.cc +++ b/source/blender/blenlib/tests/BLI_float3x3_test.cc @@ -34,6 +34,15 @@ TEST(float3x3, Rotation) EXPECT_FLOAT_EQ(result[1], 1.0f); } +TEST(float3x3, Scale) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_scale(float2(2.0f, 3.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 2.0f); + EXPECT_FLOAT_EQ(result[1], 6.0f); +} + TEST(float3x3, TranslationRotationScale) { float2 point(1.0f, 2.0f); @@ -116,4 +125,11 @@ TEST(float3x3, Origin) EXPECT_FLOAT_EQ(result[1], 3.0f); } +TEST(float3x3, GetScale2D) +{ + float2 scale(2.0f, 3.0f); + float3x3 transformation = float3x3::from_scale(scale); + EXPECT_EQ(scale, transformation.scale_2d()); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index 4f6f4a5c413..54afc3d975d 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -298,6 +298,13 @@ TEST(path_util, JoinComplex) JOIN("1/2/3/", 100, "1", "////////", "", "2", "3\\"); } +TEST(path_util, JoinRelativePrefix) +{ + JOIN("//a/b/c", 100, "//a", "b", "c"); + JOIN("//a/b/c", 100, "//", "//a//", "//b//", "//c"); + JOIN("//a/b/c", 100, "//", "//", "a", "//", "b", "//", "c"); +} + #undef JOIN /* BLI_path_frame */ diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 536c3989aff..7e2f5e4b0ae 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -237,6 +237,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p); /* Misc. */ +int BLO_read_fileversion_get(BlendDataReader *reader); bool BLO_read_requires_endian_switch(BlendDataReader *reader); bool BLO_read_data_is_undo(BlendDataReader *reader); void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 863f978daaf..bf2017b80f4 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3065,9 +3065,13 @@ static BHead *read_data_into_datamap(FileData *fd, BHead *bhead, const char *all * With the code below we get the struct-name to help tracking down the leak. * This is kept disabled as the #malloc for the text always leaks memory. */ #if 0 - { - const short *sp = fd->filesdna->structs[bhead->SDNAnr]; - allocname = fd->filesdna->types[sp[0]]; + if (bhead->SDNAnr == 0) { + /* The data type here is unclear because #writedata sets SDNAnr to 0. */ + allocname = "likely raw data"; + } + else { + SDNA_Struct *sp = fd->filesdna->structs[bhead->SDNAnr]; + allocname = fd->filesdna->types[sp->type]; size_t allocname_size = strlen(allocname) + 1; char *allocname_buf = malloc(allocname_size); memcpy(allocname_buf, allocname, allocname_size); @@ -4972,6 +4976,11 @@ ID *BLO_read_get_new_id_address(BlendLibReader *reader, Library *lib, ID *id) return newlibadr(reader->fd, lib, id); } +int BLO_read_fileversion_get(BlendDataReader *reader) +{ + return reader->fd->fileversion; +} + bool BLO_read_requires_endian_switch(BlendDataReader *reader) { return (reader->fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0; diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 11d75e0d8b8..9e5ef41892a 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -996,9 +996,9 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) if ((key = blo_do_versions_newlibadr(fd, lib, me->key)) && key->refkey) { data = key->refkey->data; tot = MIN2(me->totvert, key->refkey->totelem); - MVert *vertices = BKE_mesh_verts_for_write(me); + MVert *verts = BKE_mesh_verts_for_write(me); for (a = 0; a < tot; a++, data += 3) { - copy_v3_v3(vertices[a].co, data); + copy_v3_v3(verts[a].co, data); } } } diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index fe9369fd652..94440916603 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -126,17 +126,6 @@ void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag) BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != nullptr); BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != nullptr); - if (cd_flag & ME_CDFLAG_VERT_BWEIGHT) { - if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { - BM_data_layer_add(bm, &bm->vdata, CD_BWEIGHT); - } - } - else { - if (CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { - BM_data_layer_free(bm, &bm->vdata, CD_BWEIGHT); - } - } - if (cd_flag & ME_CDFLAG_VERT_CREASE) { if (!CustomData_has_layer(&bm->vdata, CD_CREASE)) { BM_data_layer_add(bm, &bm->vdata, CD_CREASE); @@ -148,17 +137,6 @@ void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag) } } - if (cd_flag & ME_CDFLAG_EDGE_BWEIGHT) { - if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { - BM_data_layer_add(bm, &bm->edata, CD_BWEIGHT); - } - } - else { - if (CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { - BM_data_layer_free(bm, &bm->edata, CD_BWEIGHT); - } - } - if (cd_flag & ME_CDFLAG_EDGE_CREASE) { if (!CustomData_has_layer(&bm->edata, CD_CREASE)) { BM_data_layer_add(bm, &bm->edata, CD_CREASE); @@ -174,15 +152,9 @@ void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag) char BM_mesh_cd_flag_from_bmesh(BMesh *bm) { char cd_flag = 0; - if (CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { - cd_flag |= ME_CDFLAG_VERT_BWEIGHT; - } if (CustomData_has_layer(&bm->vdata, CD_CREASE)) { cd_flag |= ME_CDFLAG_VERT_CREASE; } - if (CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { - cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; - } if (CustomData_has_layer(&bm->edata, CD_CREASE)) { cd_flag |= ME_CDFLAG_EDGE_CREASE; } @@ -342,12 +314,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Only copy these values over if the source mesh is flagged to be using them. * Even if `bm` has these layers, they may have been added from another mesh, when `!is_new`. */ - const int cd_vert_bweight_offset = (me->cd_flag & ME_CDFLAG_VERT_BWEIGHT) ? - CustomData_get_offset(&bm->vdata, CD_BWEIGHT) : - -1; - const int cd_edge_bweight_offset = (me->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) ? - CustomData_get_offset(&bm->edata, CD_BWEIGHT) : - -1; const int cd_edge_crease_offset = (me->cd_flag & ME_CDFLAG_EDGE_CREASE) ? CustomData_get_offset(&bm->edata, CD_CREASE) : -1; @@ -391,10 +357,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Copy Custom Data */ CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true); - if (cd_vert_bweight_offset != -1) { - BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert[i].bweight / 255.0f); - } - /* Set shape key original index. */ if (cd_shape_keyindex_offset != -1) { BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i); @@ -433,9 +395,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Copy Custom Data */ CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true); - if (cd_edge_bweight_offset != -1) { - BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge[i].bweight / 255.0f); - } if (cd_edge_crease_offset != -1) { BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge[i].crease / 255.0f); } @@ -965,7 +924,7 @@ static void convert_bmesh_hide_flags_to_mesh_attributes(BMesh &bm, return; } - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE); write_fn_to_attribute<bool>( @@ -990,8 +949,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh BMIter iter; int i, j; - const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); @@ -1073,10 +1030,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh /* Copy over custom-data. */ CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); - if (cd_vert_bweight_offset != -1) { - mvert[i].bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(v, cd_vert_bweight_offset); - } - i++; BM_CHECK_ELEMENT(v); @@ -1103,9 +1056,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh if (cd_edge_crease_offset != -1) { medge[i].crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_crease_offset); } - if (cd_edge_bweight_offset != -1) { - medge[i].bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_bweight_offset); - } i++; BM_CHECK_ELEMENT(e); @@ -1154,11 +1104,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh if (need_material_index) { BM_mesh_elem_table_ensure(bm, BM_FACE); write_fn_to_attribute<int>( - blender::bke::mesh_attributes_for_write(*me), - "material_index", - ATTR_DOMAIN_FACE, - true, - [&](const int i) { return static_cast<int>(BM_face_at_index(bm, i)->mat_nr); }); + me->attributes_for_write(), "material_index", ATTR_DOMAIN_FACE, true, [&](const int i) { + return static_cast<int>(BM_face_at_index(bm, i)->mat_nr); + }); } /* Patch hook indices and vertex parents. */ @@ -1314,8 +1262,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * MLoop *mloop = loops.data(); unsigned int i, j; - const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); bool need_hide_vert = false; @@ -1341,14 +1287,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * need_hide_vert = true; } - if (cd_vert_bweight_offset != -1) { - mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset); - } - - if (cd_vert_bweight_offset != -1) { - mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset); - } - CustomData_from_bmesh_block(&bm->vdata, &me->vdata, eve->head.data, i); } bm->elem_index_dirty &= ~BM_VERT; @@ -1377,9 +1315,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * if (cd_edge_crease_offset != -1) { med->crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eed, cd_edge_crease_offset); } - if (cd_edge_bweight_offset != -1) { - med->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eed, cd_edge_bweight_offset); - } CustomData_from_bmesh_block(&bm->edata, &me->edata, eed->head.data, i); } @@ -1423,11 +1358,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * if (need_material_index) { BM_mesh_elem_table_ensure(bm, BM_FACE); write_fn_to_attribute<int>( - blender::bke::mesh_attributes_for_write(*me), - "material_index", - ATTR_DOMAIN_FACE, - true, - [&](const int i) { return static_cast<int>(BM_face_at_index(bm, i)->mat_nr); }); + me->attributes_for_write(), "material_index", ATTR_DOMAIN_FACE, true, [&](const int i) { + return static_cast<int>(BM_face_at_index(bm, i)->mat_nr); + }); } convert_bmesh_hide_flags_to_mesh_attributes( diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c index 309ef2cf21e..d88f3112a71 100644 --- a/source/blender/bmesh/operators/bmo_utils.c +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -470,7 +470,7 @@ void bmo_rotate_uvs_exec(BMesh *bm, BMOperator *op) BMLoop *lf; /* current face loops */ MLoopUV *f_luv; /* first face loop uv */ float p_uv[2]; /* previous uvs */ - float t_uv[2]; /* tmp uvs */ + float t_uv[2]; /* temp uvs */ int n = 0; BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { @@ -603,7 +603,7 @@ void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op) const size_t size = cd_loop_color_type == CD_PROP_COLOR ? sizeof(MPropCol) : sizeof(MLoopCol); void *p_col; /* previous color */ - void *t_col = alloca(size); /* tmp color */ + void *t_col = alloca(size); /* Temp color. */ BMO_ITER (fs, &fs_iter, op->slots_in, "faces", BM_FACE) { if (use_ccw == false) { /* same loops direction */ diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc index 4813e49cd11..1d613a030d7 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.cc +++ b/source/blender/compositor/nodes/COM_ScaleNode.cc @@ -25,7 +25,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_socket = this->get_output_socket(0); switch (bnode->custom1) { - case CMP_SCALE_RELATIVE: { + case CMP_NODE_SCALE_RELATIVE: { ScaleRelativeOperation *operation = new ScaleRelativeOperation(); converter.add_operation(operation); @@ -39,7 +39,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, break; } - case CMP_SCALE_SCENEPERCENT: { + case CMP_NODE_SCALE_RENDER_PERCENT: { SetValueOperation *scale_factor_operation = new SetValueOperation(); scale_factor_operation->set_value(context.get_render_percentage_as_factor()); converter.add_operation(scale_factor_operation); @@ -59,13 +59,14 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, break; } - case CMP_SCALE_RENDERPERCENT: { + case CMP_NODE_SCALE_RENDER_SIZE: { const RenderData *rd = context.get_render_data(); const float render_size_factor = context.get_render_percentage_as_factor(); ScaleFixedSizeOperation *operation = new ScaleFixedSizeOperation(); /* framing options */ - operation->set_is_aspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0); - operation->set_is_crop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0); + operation->set_is_aspect( + ELEM(bnode->custom2, CMP_NODE_SCALE_RENDER_SIZE_FIT, CMP_NODE_SCALE_RENDER_SIZE_CROP)); + operation->set_is_crop(bnode->custom2 == CMP_NODE_SCALE_RENDER_SIZE_CROP); operation->set_offset(bnode->custom3, bnode->custom4); operation->set_new_width(rd->xsch * render_size_factor); operation->set_new_height(rd->ysch * render_size_factor); @@ -79,7 +80,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter, break; } - case CMP_SCALE_ABSOLUTE: { + case CMP_NODE_SCALE_ABSOLUTE: { /* TODO: what is the use of this one.... perhaps some issues when the ui was updated... */ ScaleAbsoluteOperation *operation = new ScaleAbsoluteOperation(); converter.add_operation(operation); diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 11e51e81ef0..261426b31e2 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -12,7 +12,7 @@ extern "C" { namespace blender::compositor { /* - * An implementation of Enhanced Subpixel Morphological Antialiasing (SMAA) + * An implementation of Enhanced Sub-pixel Morphological Anti-aliasing (SMAA) * * The algorithm was proposed by: * Jorge Jimenez, Jose I. Echevarria, Tiago Sousa, Diego Gutierrez diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index aeadf8f255d..3bd5fa4ad14 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -156,7 +156,7 @@ void ViewerOperation::init_image() ibuf->y = display_height_; /* zero size can happen if no image buffers exist to define a sensible resolution */ if (ibuf->x > 0 && ibuf->y > 0) { - imb_addrectfloatImBuf(ibuf); + imb_addrectfloatImBuf(ibuf, 4); } ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; diff --git a/source/blender/compositor/realtime_compositor/intern/compile_state.cc b/source/blender/compositor/realtime_compositor/intern/compile_state.cc index 97c1e47e86e..5fa2fc9d544 100644 --- a/source/blender/compositor/realtime_compositor/intern/compile_state.cc +++ b/source/blender/compositor/realtime_compositor/intern/compile_state.cc @@ -149,6 +149,11 @@ Domain CompileState::compute_shader_node_domain(DNode node) continue; } + /* An input that skips realization can't be a domain input. */ + if (input_descriptor.skip_realization) { + continue; + } + /* Notice that the lower the domain priority value is, the higher the priority is, hence the * less than comparison. */ if (input_descriptor.domain_priority < current_domain_priority) { diff --git a/source/blender/compositor/realtime_compositor/intern/operation.cc b/source/blender/compositor/realtime_compositor/intern/operation.cc index fb02807d729..832196cc5ef 100644 --- a/source/blender/compositor/realtime_compositor/intern/operation.cc +++ b/source/blender/compositor/realtime_compositor/intern/operation.cc @@ -66,6 +66,11 @@ Domain Operation::compute_domain() continue; } + /* An input that skips realization can't be a domain input. */ + if (descriptor.skip_realization) { + continue; + } + /* Notice that the lower the domain priority value is, the higher the priority is, hence the * less than comparison. */ if (descriptor.domain_priority < current_domain_priority) { diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc index 2e1baec98a8..1a5823b8441 100644 --- a/source/blender/compositor/realtime_compositor/intern/utilities.cc +++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc @@ -116,6 +116,7 @@ InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket) } const SocketDeclarationPtr &socket_declaration = node_declaration->inputs()[socket->index()]; input_descriptor.domain_priority = socket_declaration->compositor_domain_priority(); + input_descriptor.skip_realization = socket_declaration->compositor_skip_realization(); input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value(); return input_descriptor; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index dc564d443c1..67f454b608b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1741,7 +1741,14 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) /* Animation, */ build_animdata(&ntree->id); /* Output update. */ - add_operation_node(&ntree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); + ID *id_cow = get_cow_id(&ntree->id); + add_operation_node(&ntree->id, + NodeType::NTREE_OUTPUT, + OperationCode::NTREE_OUTPUT, + [id_cow](::Depsgraph * /*depsgraph*/) { + bNodeTree *ntree_cow = reinterpret_cast<bNodeTree *>(id_cow); + bke::node_tree_runtime::handle_node_tree_output_changed(*ntree_cow); + }); /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); diff --git a/source/blender/depsgraph/intern/depsgraph_physics.cc b/source/blender/depsgraph/intern/depsgraph_physics.cc index de2a14df107..cf5cccac580 100644 --- a/source/blender/depsgraph/intern/depsgraph_physics.cc +++ b/source/blender/depsgraph/intern/depsgraph_physics.cc @@ -195,7 +195,7 @@ ListBase *build_collision_relations(Depsgraph *graph, ID *collection_id = object_id_safe(collection); return hash->lookup_or_add_cb(collection_id, [&]() { ::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(graph); - return BKE_collision_relations_create(depsgraph, graph->scene, collection, modifier_type); + return BKE_collision_relations_create(depsgraph, collection, modifier_type); }); } diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index 2d2ee5dc4b4..bcae5de1e1e 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -182,7 +182,7 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data) } /* Duplicated elements shouldn't care whether their original collection is visible or not. */ - temp_dupli_object->base_flag |= BASE_VISIBLE_DEPSGRAPH; + temp_dupli_object->base_flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT; int ob_visibility = BKE_object_visibility(temp_dupli_object, data->eval_mode); if ((ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) == 0) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 30ee626f0f8..09981eb32c5 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -371,10 +371,6 @@ void deg_graph_flush_updates(Depsgraph *graph) while (op_node != nullptr) { /* Tag operation as required for update. */ op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - /* Tag depsgraph visibility update when visibility operation is tagged for an update. */ - if (op_node->opcode == OperationCode::VISIBILITY) { - graph->need_update_nodes_visibility = true; - } /* Inform corresponding ID and component nodes about the change. */ ComponentNode *comp_node = op_node->owner; IDNode *id_node = comp_node->owner; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc index e35e992fc8b..a056ba1dfa7 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc @@ -34,14 +34,10 @@ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id); - bool is_enabled; - if (graph->mode == DAG_EVAL_VIEWPORT) { - is_enabled = (object->base_flag & BASE_ENABLED_VIEWPORT) && - ((object->base_flag & BASE_HIDDEN) == 0); - } - else { - is_enabled = (object->base_flag & BASE_ENABLED_RENDER); - }; + const int required_flags = (graph->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : + BASE_ENABLED_RENDER; + + const bool is_enabled = object->base_flag & required_flags; if (id_node->is_enabled_on_eval != is_enabled) { id_node->is_enabled_on_eval = is_enabled; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index ac7f1c5ff68..e6b532ed25a 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -135,6 +135,7 @@ set(SRC engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c engines/eevee_next/eevee_camera.cc + engines/eevee_next/eevee_cryptomatte.cc engines/eevee_next/eevee_depth_of_field.cc engines/eevee_next/eevee_engine.cc engines/eevee_next/eevee_film.cc @@ -387,6 +388,7 @@ set(GLSL_SRC engines/eevee/shaders/volumetric_frag.glsl engines/eevee/shaders/volumetric_geom.glsl engines/eevee/shaders/volumetric_vert.glsl + engines/eevee/shaders/volumetric_resolve_comp.glsl engines/eevee/shaders/volumetric_resolve_frag.glsl engines/eevee/shaders/volumetric_scatter_frag.glsl engines/eevee/shaders/volumetric_integration_frag.glsl @@ -395,6 +397,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_attributes_lib.glsl engines/eevee_next/shaders/eevee_camera_lib.glsl engines/eevee_next/shaders/eevee_colorspace_lib.glsl + engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl @@ -411,6 +414,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl engines/eevee_next/shaders/eevee_film_comp.glsl + engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl engines/eevee_next/shaders/eevee_film_frag.glsl engines/eevee_next/shaders/eevee_film_lib.glsl engines/eevee_next/shaders/eevee_geom_curves_vert.glsl diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 7f722ff1764..614ea0b0892 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -849,7 +849,7 @@ static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake) DRW_opengl_context_enable(); } - /* XXX Free the resources contained in the viewlayer data + /* XXX: Free the resources contained in the view-layer data * to be able to free the context before deleting the depsgraph. */ if (lbake->sldata) { EEVEE_view_layer_data_free(lbake->sldata); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 8d47d80987c..573c29b78a1 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -1015,7 +1015,7 @@ typedef struct EEVEE_PrivateData { struct GHash *material_hash; float background_alpha; /* TODO: find a better place for this. */ bool disable_ligthprobes; - /* Chosen lightcache: can come from Lookdev or the viewlayer. */ + /** Chosen light-cache: can come from Lookdev or the view-layer. */ struct LightCache *light_cache; /* For planar probes */ float planar_texel_size[2]; @@ -1261,6 +1261,7 @@ struct GPUShader *EEVEE_shaders_volumes_scatter_sh_get(void); struct GPUShader *EEVEE_shaders_volumes_scatter_with_lights_sh_get(void); struct GPUShader *EEVEE_shaders_volumes_integration_sh_get(void); struct GPUShader *EEVEE_shaders_volumes_resolve_sh_get(bool accum); +struct GPUShader *EEVEE_shaders_volumes_resolve_comp_sh_get(bool float_target); struct GPUShader *EEVEE_shaders_volumes_accum_sh_get(void); struct GPUShader *EEVEE_shaders_ggx_lut_sh_get(void); struct GPUShader *EEVEE_shaders_ggx_refraction_lut_sh_get(void); diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 04d1168a30d..a7290b3894e 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -133,6 +133,7 @@ static struct { struct GPUShader *scatter_with_lights_sh; struct GPUShader *volumetric_integration_sh; struct GPUShader *volumetric_resolve_sh[2]; + struct GPUShader *volumetric_resolve_comp_sh[2]; struct GPUShader *volumetric_accum_sh; /* Shader strings */ @@ -261,6 +262,7 @@ extern char datatoc_volumetric_frag_glsl[]; extern char datatoc_volumetric_geom_glsl[]; extern char datatoc_volumetric_integration_frag_glsl[]; extern char datatoc_volumetric_lib_glsl[]; +extern char datatoc_volumetric_resolve_comp_glsl[]; extern char datatoc_volumetric_resolve_frag_glsl[]; extern char datatoc_volumetric_scatter_frag_glsl[]; extern char datatoc_volumetric_vert_glsl[]; @@ -903,6 +905,20 @@ struct GPUShader *EEVEE_shaders_volumes_resolve_sh_get(bool accum) return e_data.volumetric_resolve_sh[index]; } +struct GPUShader *EEVEE_shaders_volumes_resolve_comp_sh_get(bool float_target) +{ + const int index = (float_target ? 1 : 0); + if (e_data.volumetric_resolve_comp_sh[index] == NULL) { + e_data.volumetric_resolve_comp_sh[index] = DRW_shader_create_compute_with_shaderlib( + datatoc_volumetric_resolve_comp_glsl, + e_data.lib, + float_target ? "#define TARGET_IMG_FLOAT\n" SHADER_DEFINES : SHADER_DEFINES, + __func__); + } + + return e_data.volumetric_resolve_comp_sh[index]; +} + struct GPUShader *EEVEE_shaders_volumes_accum_sh_get() { if (e_data.volumetric_accum_sh == NULL) { diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 2d96cffb4ba..b2e5a0abe94 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -396,18 +396,37 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) grp, NULL, USE_VOLUME_OPTI ? 1 : common_data->vol_tex_size[2]); DRW_PASS_CREATE(psl->volumetric_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); - grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(false), - psl->volumetric_resolve_ps); - DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter); - DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit); - DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + if (GPU_compute_shader_support() && GPU_shader_image_load_store_support()) { + const bool use_float_target = DRW_state_is_image_render(); + grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_comp_sh_get(use_float_target), + psl->volumetric_resolve_ps); + DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter); + DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit); + DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_image_ref(grp, "target_img", &txl->color); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + const float *size = DRW_viewport_size_get(); + DRW_shgroup_call_compute(grp, size[0], size[1], 1); + } + else { + grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(false), + psl->volumetric_resolve_ps); + DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter); + DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit); + DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } } } @@ -546,11 +565,16 @@ void EEVEE_volumes_resolve(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *veda } /* Apply for opaque geometry. */ - GPU_framebuffer_bind(fbl->main_color_fb); - DRW_draw_pass(psl->volumetric_resolve_ps); + if (GPU_compute_shader_support() && GPU_shader_image_load_store_support()) { + DRW_draw_pass(psl->volumetric_resolve_ps); + } + else { + GPU_framebuffer_bind(fbl->main_color_fb); + DRW_draw_pass(psl->volumetric_resolve_ps); - /* Restore. */ - GPU_framebuffer_bind(fbl->main_fb); + /* Restore. */ + GPU_framebuffer_bind(fbl->main_fb); + } } } diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_comp.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_comp.glsl new file mode 100644 index 00000000000..2b0139ff923 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_comp.glsl @@ -0,0 +1,38 @@ + +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ + +/* Step 4 : Apply final integration on top of the scene color. */ + +uniform sampler2D inSceneDepth; + +layout(local_size_x = 1, local_size_y = 1) in; + +#ifdef TARGET_IMG_FLOAT +layout(binding = 0, rgba32f) uniform image2D target_img; +#else +layout(binding = 0, rgba16f) uniform image2D target_img; +#endif + +void main() +{ + ivec2 co = ivec2(gl_GlobalInvocationID.xy); + vec2 uvs = co / vec2(textureSize(inSceneDepth, 0)); + float scene_depth = texture(inSceneDepth, uvs).r; + + vec3 transmittance, scattering; + volumetric_resolve(uvs, scene_depth, transmittance, scattering); + + /* Approximate volume alpha by using a monochromatic transmittance + * and adding it to the scene alpha. */ + float alpha = dot(transmittance, vec3(1.0 / 3.0)); + + vec4 color0 = vec4(scattering, 1.0 - alpha); + vec4 color1 = vec4(transmittance, alpha); + + vec4 color_in = imageLoad(target_img, co); + vec4 color_out = color0 + color1 * color_in; + imageStore(target_img, co, color_out); +} diff --git a/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc new file mode 100644 index 00000000000..340a587b1c1 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc @@ -0,0 +1,130 @@ +#include "BKE_cryptomatte.hh" + +#include "GPU_material.h" + +#include "eevee_cryptomatte.hh" +#include "eevee_instance.hh" +#include "eevee_renderbuffers.hh" + +namespace blender::eevee { + +void Cryptomatte::begin_sync() +{ + const eViewLayerEEVEEPassType enabled_passes = static_cast<eViewLayerEEVEEPassType>( + inst_.film.enabled_passes_get() & + (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET | + EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET)); + + session_.reset(); + object_layer_ = nullptr; + asset_layer_ = nullptr; + material_layer_ = nullptr; + + if (enabled_passes && !inst_.is_viewport()) { + session_.reset(BKE_cryptomatte_init_from_view_layer(inst_.view_layer)); + + for (const std::string &layer_name : + bke::cryptomatte::BKE_cryptomatte_layer_names_get(*session_)) { + StringRef layer_name_ref = layer_name; + bke::cryptomatte::CryptomatteLayer *layer = bke::cryptomatte::BKE_cryptomatte_layer_get( + *session_, layer_name); + if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_OBJECT)) { + object_layer_ = layer; + } + else if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_ASSET)) { + asset_layer_ = layer; + } + else if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_MATERIAL)) { + material_layer_ = layer; + } + } + } + + if (!(enabled_passes & + (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET))) { + cryptomatte_object_buf.resize(16); + } +} + +void Cryptomatte::sync_object(Object *ob, ResourceHandle res_handle) +{ + const eViewLayerEEVEEPassType enabled_passes = inst_.film.enabled_passes_get(); + if (!(enabled_passes & + (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET))) { + return; + } + + uint32_t resource_id = res_handle.resource_index(); + float2 object_hashes(0.0f, 0.0f); + + if (enabled_passes & EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT) { + object_hashes[0] = register_id(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, ob->id); + } + + if (enabled_passes & EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET) { + Object *asset = ob; + while (asset->parent) { + asset = asset->parent; + } + object_hashes[1] = register_id(EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, asset->id); + } + cryptomatte_object_buf.get_or_resize(resource_id) = object_hashes; +} + +void Cryptomatte::sync_material(const ::Material *material) +{ + /* Material crypto hashes are generated during shader codegen stage. We only need to register + * them to store inside the metadata. */ + if (material_layer_ && material) { + material_layer_->add_ID(material->id); + } +} + +void Cryptomatte::end_sync() +{ + cryptomatte_object_buf.push_update(); + + object_layer_ = nullptr; + asset_layer_ = nullptr; + material_layer_ = nullptr; +} + +float Cryptomatte::register_id(const eViewLayerEEVEEPassType layer, const ID &id) const +{ + BLI_assert(ELEM(layer, + EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, + EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, + EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL)); + + uint32_t cryptomatte_hash = 0; + if (session_) { + if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT) { + BLI_assert(object_layer_); + cryptomatte_hash = object_layer_->add_ID(id); + } + else if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET) { + BLI_assert(asset_layer_); + cryptomatte_hash = asset_layer_->add_ID(id); + } + else if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL) { + BLI_assert(material_layer_); + cryptomatte_hash = material_layer_->add_ID(id); + } + } + else { + const char *name = &id.name[2]; + const int name_len = BLI_strnlen(name, MAX_NAME - 2); + cryptomatte_hash = BKE_cryptomatte_hash(name, name_len); + } + + return BKE_cryptomatte_hash_to_float(cryptomatte_hash); +} + +void Cryptomatte::store_metadata(RenderResult *render_result) +{ + if (session_) { + BKE_cryptomatte_store_metadata(&*session_, render_result, inst_.view_layer); + } +} + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh new file mode 100644 index 00000000000..37f5edf4c6d --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Cryptomatte. + * + * During rasterization, cryptomatte hashes are stored into a single array texture. + * The film pass then resamples this texture using pixel filter weighting. + * Each cryptomatte layer can hold N samples. These are stored in sequential layers + * of the array texture. The samples are sorted and merged only for final rendering. + */ + +#pragma once + +#include "eevee_shader_shared.hh" + +#include "BKE_cryptomatte.hh" + +extern "C" { +struct Material; +struct CryptomatteSession; +} + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte + * \{ */ + +class Cryptomatte { + private: + class Instance &inst_; + + bke::cryptomatte::CryptomatteSessionPtr session_; + + /* Cached pointer to the cryptomatte layer instances. */ + bke::cryptomatte::CryptomatteLayer *object_layer_ = nullptr; + bke::cryptomatte::CryptomatteLayer *asset_layer_ = nullptr; + bke::cryptomatte::CryptomatteLayer *material_layer_ = nullptr; + + /** Contains per object hashes (object and asset hash). Indexed by resource ID. */ + CryptomatteObjectBuf cryptomatte_object_buf; + + public: + Cryptomatte(Instance &inst) : inst_(inst){}; + + void begin_sync(); + void sync_object(Object *ob, ResourceHandle res_handle); + void sync_material(const ::Material *material); + void end_sync(); + + template<typename T> void bind_resources(draw::detail::PassBase<T> *pass) + { + pass->bind_ssbo(CRYPTOMATTE_BUF_SLOT, &cryptomatte_object_buf); + } + + /* Register ID to use inside cryptomatte layer and returns associated hash as float. */ + float register_id(const eViewLayerEEVEEPassType layer, const ID &id) const; + void store_metadata(RenderResult *render_result); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 2f338e707c0..248dfae6df9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -82,6 +82,7 @@ #define RBUFS_EMISSION_SLOT 4 #define RBUFS_AOV_COLOR_SLOT 5 #define RBUFS_AOV_VALUE_SLOT 6 +#define RBUFS_CRYPTOMATTE_SLOT 7 /* Uniform Buffers. */ /* Only during prepass. */ @@ -96,6 +97,8 @@ #define LIGHT_TILE_BUF_SLOT 3 #define RBUFS_AOV_BUF_SLOT 5 #define SAMPLING_BUF_SLOT 6 +#define CRYPTOMATTE_BUF_SLOT 7 + /* Only during pre-pass. */ #define VELOCITY_OBJ_PREV_BUF_SLOT 0 #define VELOCITY_OBJ_NEXT_BUF_SLOT 1 diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc index 2e476b7d891..5ef198838c9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_engine.cc +++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc @@ -140,7 +140,7 @@ static void eevee_instance_free(void *instance) delete reinterpret_cast<eevee::Instance *>(instance); } -static void eevee_render_to_image(void *UNUSED(vedata), +static void eevee_render_to_image(void *vedata, struct RenderEngine *engine, struct RenderLayer *layer, const struct rcti *UNUSED(rect)) @@ -164,59 +164,31 @@ static void eevee_render_to_image(void *UNUSED(vedata), instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, layer); instance->render_frame(layer, viewname); - delete instance; + EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata); + if (ved->instance) { + delete ved->instance; + } + ved->instance = instance; } -static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) +static void eevee_store_metadata(void *vedata, struct RenderResult *render_result) { if (!GPU_shader_storage_buffer_objects_support()) { return; } + EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata); + eevee::Instance *instance = ved->instance; + instance->store_metadata(render_result); + delete instance; + ved->instance = nullptr; +} - RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA); - -#define CHECK_PASS_LEGACY(name, type, channels, chanid) \ - if (view_layer->passflag & (SCE_PASS_##name)) { \ - RE_engine_register_pass( \ - engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ - } \ - ((void)0) -#define CHECK_PASS_EEVEE(name, type, channels, chanid) \ - if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \ - RE_engine_register_pass( \ - engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ - } \ - ((void)0) - - CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z"); - CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z"); - CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ"); - CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB"); - /* TODO: CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB"); - * CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB"); - * When available they should be converted from Value textures to RGB. */ - - LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { - if ((aov->flag & AOV_CONFLICT) != 0) { - continue; - } - switch (aov->type) { - case AOV_TYPE_COLOR: - RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA); - break; - case AOV_TYPE_VALUE: - RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT); - break; - default: - break; - } +static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; } + eevee::Instance::update_passes(engine, scene, view_layer); } static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data); @@ -238,7 +210,7 @@ DrawEngineType draw_engine_eevee_next_type = { nullptr, nullptr, &eevee_render_to_image, - nullptr, + &eevee_store_metadata, }; RenderEngineType DRW_engine_viewport_eevee_next_type = { diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc index 4679889e59a..b89746d99e2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.cc +++ b/source/blender/draw/engines/eevee_next/eevee_film.cc @@ -162,6 +162,45 @@ inline bool operator!=(const FilmData &a, const FilmData &b) /** \name Film * \{ */ +static eViewLayerEEVEEPassType enabled_passes(const ViewLayer *view_layer) +{ + eViewLayerEEVEEPassType result = eViewLayerEEVEEPassType(view_layer->eevee.render_passes); + +#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \ + SET_FLAG_FROM_TEST(result, \ + (view_layer->passflag & SCE_PASS_##name_legacy) != 0, \ + EEVEE_RENDER_PASS_##name_eevee); + + ENABLE_FROM_LEGACY(COMBINED, COMBINED) + ENABLE_FROM_LEGACY(Z, Z) + ENABLE_FROM_LEGACY(MIST, MIST) + ENABLE_FROM_LEGACY(NORMAL, NORMAL) + ENABLE_FROM_LEGACY(SHADOW, SHADOW) + ENABLE_FROM_LEGACY(AO, AO) + ENABLE_FROM_LEGACY(EMIT, EMIT) + ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) + ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR) + ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR) + ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT) + ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT) + ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) + ENABLE_FROM_LEGACY(VECTOR, VECTOR) + +#undef ENABLE_FROM_LEGACY + + SET_FLAG_FROM_TEST(result, + view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT, + EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT); + SET_FLAG_FROM_TEST(result, + view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET, + EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET); + SET_FLAG_FROM_TEST(result, + view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL, + EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL); + + return result; +} + void Film::init(const int2 &extent, const rcti *output_rect) { Sampling &sampling = inst_.sampling; @@ -186,29 +225,7 @@ void Film::init(const int2 &extent, const rcti *output_rect) } else { /* Render Case. */ - render_passes = eViewLayerEEVEEPassType(inst_.view_layer->eevee.render_passes); - -#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \ - SET_FLAG_FROM_TEST(render_passes, \ - (inst_.view_layer->passflag & SCE_PASS_##name_legacy) != 0, \ - EEVEE_RENDER_PASS_##name_eevee); - - ENABLE_FROM_LEGACY(COMBINED, COMBINED) - ENABLE_FROM_LEGACY(Z, Z) - ENABLE_FROM_LEGACY(MIST, MIST) - ENABLE_FROM_LEGACY(NORMAL, NORMAL) - ENABLE_FROM_LEGACY(SHADOW, SHADOW) - ENABLE_FROM_LEGACY(AO, AO) - ENABLE_FROM_LEGACY(EMIT, EMIT) - ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) - ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR) - ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR) - ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT) - ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT) - ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) - ENABLE_FROM_LEGACY(VECTOR, VECTOR) - -#undef ENABLE_FROM_LEGACY + render_passes = enabled_passes(inst_.view_layer); } /* Filter obsolete passes. */ @@ -241,6 +258,7 @@ void Film::init(const int2 &extent, const rcti *output_rect) /* TODO(fclem): parameter hidden in experimental. * We need to figure out LOD bias first in order to preserve texture crispiness. */ data.scaling_factor = 1; + data.cryptomatte_samples_len = inst_.view_layer->cryptomatte_levels; data.background_opacity = (scene.r.alphamode == R_ALPHAPREMUL) ? 0.0f : 1.0f; if (inst_.is_viewport() && false /* TODO(fclem): StudioLight */) { @@ -273,7 +291,8 @@ void Film::init(const int2 &extent, const rcti *output_rect) /* Set pass offsets. */ data_.display_id = aovs_info.display_id; - data_.display_is_value = aovs_info.display_is_value; + data_.display_storage_type = aovs_info.display_is_value ? PASS_STORAGE_VALUE : + PASS_STORAGE_COLOR; /* Combined is in a separate buffer. */ data_.combined_id = (enabled_passes_ & EEVEE_RENDER_PASS_COMBINED) ? 0 : -1; @@ -284,13 +303,13 @@ void Film::init(const int2 &extent, const rcti *output_rect) data_.value_len = 0; auto pass_index_get = [&](eViewLayerEEVEEPassType pass_type) { - bool is_value = pass_is_value(pass_type); + ePassStorageType storage_type = pass_storage_type(pass_type); int index = (enabled_passes_ & pass_type) ? - (is_value ? data_.value_len : data_.color_len)++ : + (storage_type == PASS_STORAGE_VALUE ? data_.value_len : data_.color_len)++ : -1; if (inst_.is_viewport() && inst_.v3d->shading.render_pass == pass_type) { data_.display_id = index; - data_.display_is_value = is_value; + data_.display_storage_type = storage_type; } return index; }; @@ -316,6 +335,24 @@ void Film::init(const int2 &extent, const rcti *output_rect) data_.color_len += data_.aov_color_len; data_.value_len += data_.aov_value_len; + + int cryptomatte_id = 0; + auto cryptomatte_index_get = [&](eViewLayerEEVEEPassType pass_type) { + int index = -1; + if (enabled_passes_ & pass_type) { + index = cryptomatte_id; + cryptomatte_id += data_.cryptomatte_samples_len / 2; + + if (inst_.is_viewport() && inst_.v3d->shading.render_pass == pass_type) { + data_.display_id = index; + data_.display_storage_type = PASS_STORAGE_CRYPTOMATTE; + } + } + return index; + }; + data_.cryptomatte_object_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT); + data_.cryptomatte_asset_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET); + data_.cryptomatte_material_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL); } { /* TODO(@fclem): Over-scans. */ @@ -327,6 +364,7 @@ void Film::init(const int2 &extent, const rcti *output_rect) eGPUTextureFormat float_format = GPU_R16F; eGPUTextureFormat weight_format = GPU_R32F; eGPUTextureFormat depth_format = GPU_R32F; + eGPUTextureFormat cryptomatte_format = GPU_RGBA32F; int reset = 0; reset += depth_tx_.ensure_2d(depth_format, data_.extent); @@ -341,6 +379,12 @@ void Film::init(const int2 &extent, const rcti *output_rect) reset += value_accum_tx_.ensure_2d_array(float_format, (data_.value_len > 0) ? data_.extent : int2(1), (data_.value_len > 0) ? data_.value_len : 1); + /* Divided by two as two cryptomatte samples fit in pixel (RG, BA). */ + int cryptomatte_array_len = cryptomatte_layer_len_get() * data_.cryptomatte_samples_len / 2; + reset += cryptomatte_tx_.ensure_2d_array(cryptomatte_format, + (cryptomatte_array_len > 0) ? data_.extent : int2(1), + (cryptomatte_array_len > 0) ? cryptomatte_array_len : + 1); if (reset > 0) { sampling.reset(); @@ -353,6 +397,7 @@ void Film::init(const int2 &extent, const rcti *output_rect) combined_tx_.current().clear(float4(0.0f)); weight_tx_.current().clear(float4(0.0f)); depth_tx_.clear(float4(0.0f)); + cryptomatte_tx_.clear(float4(0.0f)); } } @@ -398,6 +443,7 @@ void Film::sync() accumulate_ps_.bind_texture("ambient_occlusion_tx", &rbuffers.ambient_occlusion_tx); accumulate_ps_.bind_texture("aov_color_tx", &rbuffers.aov_color_tx); accumulate_ps_.bind_texture("aov_value_tx", &rbuffers.aov_value_tx); + accumulate_ps_.bind_texture("cryptomatte_tx", &rbuffers.cryptomatte_tx); /* NOTE(@fclem): 16 is the max number of sampled texture in many implementations. * If we need more, we need to pack more of the similar passes in the same textures as arrays or * use image binding instead. */ @@ -408,6 +454,7 @@ void Film::sync() accumulate_ps_.bind_image("depth_img", &depth_tx_); accumulate_ps_.bind_image("color_accum_img", &color_accum_tx_); accumulate_ps_.bind_image("value_accum_img", &value_accum_tx_); + accumulate_ps_.bind_image("cryptomatte_img", &cryptomatte_tx_); /* Sync with rendering passes. */ accumulate_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); if (use_compute) { @@ -416,6 +463,22 @@ void Film::sync() else { accumulate_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); } + + const int cryptomatte_layer_count = cryptomatte_layer_len_get(); + const bool is_cryptomatte_pass_enabled = cryptomatte_layer_count > 0; + const bool do_cryptomatte_sorting = inst_.is_viewport() == false; + cryptomatte_post_ps_.init(); + if (is_cryptomatte_pass_enabled && do_cryptomatte_sorting) { + cryptomatte_post_ps_.state_set(DRW_STATE_NO_DRAW); + cryptomatte_post_ps_.shader_set(inst_.shaders.static_shader_get(FILM_CRYPTOMATTE_POST)); + cryptomatte_post_ps_.bind_image("cryptomatte_img", &cryptomatte_tx_); + cryptomatte_post_ps_.bind_image("weight_img", &weight_tx_.current()); + cryptomatte_post_ps_.push_constant("cryptomatte_layer_len", cryptomatte_layer_count); + cryptomatte_post_ps_.push_constant("cryptomatte_samples_per_layer", + inst_.view_layer->cryptomatte_levels); + int2 dispatch_size = math::divide_ceil(int2(cryptomatte_tx_.size()), int2(FILM_GROUP_SIZE)); + cryptomatte_post_ps_.dispatch(int3(UNPACK2(dispatch_size), 1)); + } } void Film::end_sync() @@ -463,6 +526,29 @@ eViewLayerEEVEEPassType Film::enabled_passes_get() const return enabled_passes_; } +int Film::cryptomatte_layer_len_get() const +{ + int result = 0; + result += data_.cryptomatte_object_id == -1 ? 0 : 1; + result += data_.cryptomatte_asset_id == -1 ? 0 : 1; + result += data_.cryptomatte_material_id == -1 ? 0 : 1; + return result; +} + +int Film::cryptomatte_layer_max_get() const +{ + if (data_.cryptomatte_material_id != -1) { + return 3; + } + if (data_.cryptomatte_asset_id != -1) { + return 2; + } + if (data_.cryptomatte_object_id != -1) { + return 1; + } + return 0; +} + void Film::update_sample_table() { data_.subpixel_offset = pixel_jitter_get(); @@ -599,20 +685,28 @@ void Film::display() /* IMPORTANT: Do not swap! No accumulation has happened. */ } -float *Film::read_pass(eViewLayerEEVEEPassType pass_type) +void Film::cryptomatte_sort() { + DRW_manager_get()->submit(cryptomatte_post_ps_); +} + +float *Film::read_pass(eViewLayerEEVEEPassType pass_type, int layer_offset) +{ + ePassStorageType storage_type = pass_storage_type(pass_type); + const bool is_value = storage_type == PASS_STORAGE_VALUE; + const bool is_cryptomatte = storage_type == PASS_STORAGE_CRYPTOMATTE; - bool is_value = pass_is_value(pass_type); Texture &accum_tx = (pass_type == EEVEE_RENDER_PASS_COMBINED) ? combined_tx_.current() : (pass_type == EEVEE_RENDER_PASS_Z) ? depth_tx_ : - (is_value ? value_accum_tx_ : color_accum_tx_); + (is_cryptomatte ? cryptomatte_tx_ : + (is_value ? value_accum_tx_ : color_accum_tx_)); accum_tx.ensure_layer_views(); int index = pass_id_get(pass_type); - GPUTexture *pass_tx = accum_tx.layer_view(index); + GPUTexture *pass_tx = accum_tx.layer_view(index + layer_offset); GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh index 796fcb24808..5478c20aff2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.hh +++ b/source/blender/draw/engines/eevee_next/eevee_film.hh @@ -43,11 +43,16 @@ class Film { /** Incoming combined buffer with post FX applied (motion blur + depth of field). */ GPUTexture *combined_final_tx_ = nullptr; - /** Main accumulation textures containing every render-pass except depth and combined. */ + /** + * Main accumulation textures containing every render-pass except depth, cryptomatte and + * combined. + */ Texture color_accum_tx_; Texture value_accum_tx_; /** Depth accumulation texture. Separated because using a different format. */ Texture depth_tx_; + /** Cryptomatte texture. Separated because it requires full floats. */ + Texture cryptomatte_tx_; /** Combined "Color" buffer. Double buffered to allow re-projection. */ SwapChain<Texture, 2> combined_tx_; /** Weight buffers. Double buffered to allow updating it during accumulation. */ @@ -56,6 +61,7 @@ class Film { bool force_disable_reprojection_ = false; PassSimple accumulate_ps_ = {"Film.Accumulate"}; + PassSimple cryptomatte_post_ps_ = {"Film.Cryptomatte.Post"}; FilmDataBuf data_; @@ -73,10 +79,13 @@ class Film { /** Accumulate the newly rendered sample contained in #RenderBuffers and blit to display. */ void accumulate(const DRWView *view, GPUTexture *combined_final_tx); + /** Sort and normalize cryptomatte samples. */ + void cryptomatte_sort(); + /** Blit to display. No rendered sample needed. */ void display(); - float *read_pass(eViewLayerEEVEEPassType pass_type); + float *read_pass(eViewLayerEEVEEPassType pass_type, int layer_offset); float *read_aov(ViewLayerAOV *aov); /** Returns shading views internal resolution. */ @@ -93,17 +102,23 @@ class Film { } eViewLayerEEVEEPassType enabled_passes_get() const; + int cryptomatte_layer_max_get() const; + int cryptomatte_layer_len_get() const; - static bool pass_is_value(eViewLayerEEVEEPassType pass_type) + static ePassStorageType pass_storage_type(eViewLayerEEVEEPassType pass_type) { switch (pass_type) { case EEVEE_RENDER_PASS_Z: case EEVEE_RENDER_PASS_MIST: case EEVEE_RENDER_PASS_SHADOW: case EEVEE_RENDER_PASS_AO: - return true; + return PASS_STORAGE_VALUE; + case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT: + case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET: + case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL: + return PASS_STORAGE_CRYPTOMATTE; default: - return false; + return PASS_STORAGE_COLOR; } } @@ -154,8 +169,12 @@ class Film { return data_.shadow_id; case EEVEE_RENDER_PASS_AO: return data_.ambient_occlusion_id; - case EEVEE_RENDER_PASS_CRYPTOMATTE: - return -1; /* TODO */ + case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT: + return data_.cryptomatte_object_id; + case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET: + return data_.cryptomatte_asset_id; + case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL: + return data_.cryptomatte_material_id; case EEVEE_RENDER_PASS_VECTOR: return data_.vector_id; default: @@ -163,44 +182,80 @@ class Film { } } - static const char *pass_to_render_pass_name(eViewLayerEEVEEPassType pass_type) + static const Vector<std::string> pass_to_render_pass_names(eViewLayerEEVEEPassType pass_type, + const ViewLayer *view_layer) { + Vector<std::string> result; + + auto build_cryptomatte_passes = [&](const char *pass_name) { + const int num_cryptomatte_passes = (view_layer->cryptomatte_levels + 1) / 2; + for (int pass = 0; pass < num_cryptomatte_passes; pass++) { + std::stringstream ss; + ss.fill('0'); + ss << pass_name; + ss.width(2); + ss << pass; + result.append(ss.str()); + } + }; + switch (pass_type) { case EEVEE_RENDER_PASS_COMBINED: - return RE_PASSNAME_COMBINED; + result.append(RE_PASSNAME_COMBINED); + break; case EEVEE_RENDER_PASS_Z: - return RE_PASSNAME_Z; + result.append(RE_PASSNAME_Z); + break; case EEVEE_RENDER_PASS_MIST: - return RE_PASSNAME_MIST; + result.append(RE_PASSNAME_MIST); + break; case EEVEE_RENDER_PASS_NORMAL: - return RE_PASSNAME_NORMAL; + result.append(RE_PASSNAME_NORMAL); + break; case EEVEE_RENDER_PASS_DIFFUSE_LIGHT: - return RE_PASSNAME_DIFFUSE_DIRECT; + result.append(RE_PASSNAME_DIFFUSE_DIRECT); + break; case EEVEE_RENDER_PASS_DIFFUSE_COLOR: - return RE_PASSNAME_DIFFUSE_COLOR; + result.append(RE_PASSNAME_DIFFUSE_COLOR); + break; case EEVEE_RENDER_PASS_SPECULAR_LIGHT: - return RE_PASSNAME_GLOSSY_DIRECT; + result.append(RE_PASSNAME_GLOSSY_DIRECT); + break; case EEVEE_RENDER_PASS_SPECULAR_COLOR: - return RE_PASSNAME_GLOSSY_COLOR; + result.append(RE_PASSNAME_GLOSSY_COLOR); + break; case EEVEE_RENDER_PASS_VOLUME_LIGHT: - return RE_PASSNAME_VOLUME_LIGHT; + result.append(RE_PASSNAME_VOLUME_LIGHT); + break; case EEVEE_RENDER_PASS_EMIT: - return RE_PASSNAME_EMIT; + result.append(RE_PASSNAME_EMIT); + break; case EEVEE_RENDER_PASS_ENVIRONMENT: - return RE_PASSNAME_ENVIRONMENT; + result.append(RE_PASSNAME_ENVIRONMENT); + break; case EEVEE_RENDER_PASS_SHADOW: - return RE_PASSNAME_SHADOW; + result.append(RE_PASSNAME_SHADOW); + break; case EEVEE_RENDER_PASS_AO: - return RE_PASSNAME_AO; - case EEVEE_RENDER_PASS_CRYPTOMATTE: - BLI_assert_msg(0, "Cryptomatte is not implemented yet."); - return ""; /* TODO */ + result.append(RE_PASSNAME_AO); + break; + case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT: + build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_OBJECT); + break; + case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET: + build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_ASSET); + break; + case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL: + build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_MATERIAL); + break; case EEVEE_RENDER_PASS_VECTOR: - return RE_PASSNAME_VECTOR; + result.append(RE_PASSNAME_VECTOR); + break; default: BLI_assert(0); - return ""; + break; } + return result; } private: diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 6150f32f150..9cba3749d52 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -102,6 +102,7 @@ void Instance::begin_sync() materials.begin_sync(); velocity.begin_sync(); /* NOTE: Also syncs camera. */ lights.begin_sync(); + cryptomatte.begin_sync(); gpencil_engine_enabled = false; @@ -182,6 +183,7 @@ void Instance::end_sync() lights.end_sync(); sampling.end_sync(); film.end_sync(); + cryptomatte.end_sync(); } void Instance::render_sync() @@ -236,10 +238,15 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na continue; } - const char *pass_name = Film::pass_to_render_pass_name(pass_type); - RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name); - if (rp) { - float *result = film.read_pass(pass_type); + Vector<std::string> pass_names = Film::pass_to_render_pass_names(pass_type, view_layer); + for (int64_t pass_offset : IndexRange(pass_names.size())) { + RenderPass *rp = RE_pass_find_by_name( + render_layer, pass_names[pass_offset].c_str(), view_name); + if (!rp) { + continue; + } + float *result = film.read_pass(pass_type, pass_offset); + if (result) { BLI_mutex_lock(&render->update_render_passes_mutex); /* WORKAROUND: We use texture read to avoid using a framebuffer to get the render result. @@ -255,10 +262,13 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na /* The vector pass is initialized to weird values. Set it to neutral value if not rendered. */ if ((pass_bits & EEVEE_RENDER_PASS_VECTOR) == 0) { - const char *vector_pass_name = Film::pass_to_render_pass_name(EEVEE_RENDER_PASS_VECTOR); - RenderPass *vector_rp = RE_pass_find_by_name(render_layer, vector_pass_name, view_name); - if (vector_rp) { - memset(vector_rp->rect, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty); + for (std::string vector_pass_name : + Film::pass_to_render_pass_names(EEVEE_RENDER_PASS_VECTOR, view_layer)) { + RenderPass *vector_rp = RE_pass_find_by_name( + render_layer, vector_pass_name.c_str(), view_name); + if (vector_rp) { + memset(vector_rp->rect, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty); + } } } } @@ -290,6 +300,8 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name) #endif } + this->film.cryptomatte_sort(); + this->render_read_result(render_layer, view_name); } @@ -313,6 +325,76 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl) } } +void Instance::store_metadata(RenderResult *render_result) +{ + cryptomatte.store_metadata(render_result); +} + +void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) +{ + RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA); + +#define CHECK_PASS_LEGACY(name, type, channels, chanid) \ + if (view_layer->passflag & (SCE_PASS_##name)) { \ + RE_engine_register_pass( \ + engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ + } \ + ((void)0) +#define CHECK_PASS_EEVEE(name, type, channels, chanid) \ + if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \ + RE_engine_register_pass( \ + engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ + } \ + ((void)0) + + CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z"); + CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z"); + CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ"); + CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB"); + /* TODO: CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB"); + * CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB"); + * When available they should be converted from Value textures to RGB. */ + + LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { + if ((aov->flag & AOV_CONFLICT) != 0) { + continue; + } + switch (aov->type) { + case AOV_TYPE_COLOR: + RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA); + break; + case AOV_TYPE_VALUE: + RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT); + break; + default: + break; + } + } + + /* NOTE: Name channels lowercase rgba so that compression rules check in OpenEXR DWA code uses + * loseless compression. Reportedly this naming is the only one which works good from the + * interoperability point of view. Using xyzw naming is not portable. */ + auto register_cryptomatte_passes = [&](eViewLayerCryptomatteFlags cryptomatte_layer, + eViewLayerEEVEEPassType eevee_pass) { + if (view_layer->cryptomatte_flag & cryptomatte_layer) { + for (std::string pass_name : Film::pass_to_render_pass_names(eevee_pass, view_layer)) { + RE_engine_register_pass( + engine, scene, view_layer, pass_name.c_str(), 4, "rgba", SOCK_RGBA); + } + } + }; + register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_OBJECT, EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT); + register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_ASSET, EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET); + register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_MATERIAL, + EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL); +} + /** \} */ } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 4ab20d540bf..c8eecbd812d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -16,6 +16,7 @@ #include "DRW_render.h" #include "eevee_camera.hh" +#include "eevee_cryptomatte.hh" #include "eevee_depth_of_field.hh" #include "eevee_film.hh" #include "eevee_hizbuffer.hh" @@ -49,6 +50,7 @@ class Instance { VelocityModule velocity; MotionBlurModule motion_blur; DepthOfField depth_of_field; + Cryptomatte cryptomatte; HiZBuffer hiz_buffer; Sampling sampling; Camera camera; @@ -91,6 +93,7 @@ class Instance { velocity(*this), motion_blur(*this), depth_of_field(*this), + cryptomatte(*this), hiz_buffer(*this), sampling(*this), camera(*this), @@ -117,9 +120,12 @@ class Instance { void render_sync(); void render_frame(RenderLayer *render_layer, const char *view_name); + void store_metadata(RenderResult *render_result); void draw_viewport(DefaultFramebufferList *dfbl); + static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer); + bool is_viewport() const { return render == nullptr; diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 16bdfb04d14..33978518ffc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -44,6 +44,7 @@ void WorldPipeline::sync(GPUMaterial *gpumat) world_ps_.bind_image("rp_diffuse_color_img", &rbufs.diffuse_color_tx); world_ps_.bind_image("rp_specular_color_img", &rbufs.specular_color_tx); world_ps_.bind_image("rp_emission_img", &rbufs.emission_tx); + world_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx); world_ps_.draw(DRW_cache_fullscreen_quad_get(), handle); /* To allow opaque pass rendering over it. */ @@ -110,6 +111,8 @@ void ForwardPipeline::sync() /* AOVs. */ opaque_ps_.bind_image(RBUFS_AOV_COLOR_SLOT, &inst_.render_buffers.aov_color_tx); opaque_ps_.bind_image(RBUFS_AOV_VALUE_SLOT, &inst_.render_buffers.aov_value_tx); + /* Cryptomatte. */ + opaque_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx); /* Storage Buf. */ opaque_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info); /* Textures. */ @@ -117,6 +120,7 @@ void ForwardPipeline::sync() inst_.lights.bind_resources(&opaque_ps_); inst_.sampling.bind_resources(&opaque_ps_); + inst_.cryptomatte.bind_resources(&opaque_ps_); } opaque_single_sided_ps_ = &opaque_ps_.sub("SingleSided"); diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc index c18c913d797..8e36e1d071c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc +++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc @@ -72,6 +72,20 @@ void RenderBuffers::acquire(int2 extent) color_format, (aovs.color_len > 0) ? extent : int2(1), max_ii(1, aovs.color_len)); aov_value_tx.ensure_2d_array( float_format, (aovs.value_len > 0) ? extent : int2(1), max_ii(1, aovs.value_len)); + + eGPUTextureFormat cryptomatte_format = GPU_R32F; + const int cryptomatte_layer_len = inst_.film.cryptomatte_layer_max_get(); + if (cryptomatte_layer_len == 2) { + cryptomatte_format = GPU_RG32F; + } + else if (cryptomatte_layer_len == 3) { + cryptomatte_format = GPU_RGBA32F; + } + cryptomatte_tx.acquire( + pass_extent(static_cast<eViewLayerEEVEEPassType>(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | + EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET | + EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL)), + cryptomatte_format); } void RenderBuffers::release() @@ -88,6 +102,7 @@ void RenderBuffers::release() environment_tx.release(); shadow_tx.release(); ambient_occlusion_tx.release(); + cryptomatte_tx.release(); } } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh index 0b761d618cc..ae5d7fbae5c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh +++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh @@ -35,7 +35,7 @@ class RenderBuffers { TextureFromPool environment_tx; TextureFromPool shadow_tx; TextureFromPool ambient_occlusion_tx; - // TextureFromPool cryptomatte_tx; /* TODO */ + TextureFromPool cryptomatte_tx; /* TODO(fclem): Use texture from pool once they support texture array. */ Texture light_tx; Texture aov_color_tx; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 7ff343d14a8..64b1d4891a9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -84,6 +84,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_film_frag"; case FILM_COMP: return "eevee_film_comp"; + case FILM_CRYPTOMATTE_POST: + return "eevee_film_cryptomatte_post"; case HIZ_DEBUG: return "eevee_hiz_debug"; case HIZ_UPDATE: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 9ef42c84373..88538557c07 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -28,6 +28,7 @@ namespace blender::eevee { enum eShaderType { FILM_FRAG = 0, FILM_COMP, + FILM_CRYPTOMATTE_POST, DOF_BOKEH_LUT, DOF_DOWNSAMPLE, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index bcdb42c0093..8e96445d6b9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -199,6 +199,12 @@ enum eFilmWeightLayerIndex : uint32_t { FILM_WEIGHT_LAYER_DISTANCE = 1u, }; +enum ePassStorageType : uint32_t { + PASS_STORAGE_COLOR = 0u, + PASS_STORAGE_VALUE = 1u, + PASS_STORAGE_CRYPTOMATTE = 2u, +}; + struct FilmSample { int2 texel; float weight; @@ -255,13 +261,19 @@ struct FilmData { int combined_id; /** Id of the render-pass to be displayed. -1 for combined. */ int display_id; - /** True if the render-pass to be displayed is from the value accum buffer. */ - bool1 display_is_value; + /** Storage type of the render-pass to be displayed. */ + ePassStorageType display_storage_type; /** True if we bypass the accumulation and directly output the accumulation buffer. */ bool1 display_only; /** Start of AOVs and number of aov. */ int aov_color_id, aov_color_len; int aov_value_id, aov_value_len; + /** Start of cryptomatte per layer (-1 if pass is not enabled). */ + int cryptomatte_object_id; + int cryptomatte_asset_id; + int cryptomatte_material_id; + /** Max number of samples stored per layer (is even number). */ + int cryptomatte_samples_len; /** Settings to render mist pass */ float mist_scale, mist_bias, mist_exponent; /** Scene exposure used for better noise reduction. */ @@ -750,6 +762,7 @@ using SamplingDataBuf = draw::StorageBuffer<SamplingData>; using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>; using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>; using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>; +using CryptomatteObjectBuf = draw::StorageArrayBuffer<float2, 16>; } // namespace blender::eevee #endif diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index 5f8b87c24b9..09ea7c9ec3d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -120,10 +120,14 @@ void SyncModule::sync_mesh(Object *ob, is_shadow_caster = is_shadow_caster || material->shadow.sub_pass != nullptr; is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent; + + GPUMaterial *gpu_material = material_array.gpu_materials[i]; + ::Material *mat = GPU_material_get_material(gpu_material); + inst_.cryptomatte.sync_material(mat); } inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials); - + inst_.cryptomatte.sync_object(ob, res_handle); // shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend); } @@ -320,6 +324,12 @@ void SyncModule::sync_curves(Object *ob, shgroup_curves_call(material.prepass, ob, part_sys, modifier_data); shgroup_curves_call(material.shadow, ob, part_sys, modifier_data); + inst_.cryptomatte.sync_object(ob, res_handle); + GPUMaterial *gpu_material = + inst_.materials.material_array_get(ob, has_motion).gpu_materials[mat_nr - 1]; + ::Material *mat = GPU_material_get_material(gpu_material); + inst_.cryptomatte.sync_material(mat); + /* TODO(fclem) Hair velocity. */ // shading_passes.velocity.gpencil_add(ob, ob_handle); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl new file mode 100644 index 00000000000..e874a6b56ea --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl @@ -0,0 +1,70 @@ +/** Storing/merging and sorting cryptomatte samples. */ + +bool cryptomatte_can_merge_sample(vec2 dst, vec2 src) +{ + if (dst == vec2(0.0, 0.0)) { + return true; + } + if (dst.x == src.x) { + return true; + } + return false; +} + +vec2 cryptomatte_merge_sample(vec2 dst, vec2 src) +{ + return vec2(src.x, dst.y + src.y); +} + +vec4 cryptomatte_false_color(float hash) +{ + uint m3hash = floatBitsToUint(hash); + return vec4(hash, + float(m3hash << 8) / float(0xFFFFFFFFu), + float(m3hash << 16) / float(0xFFFFFFFFu), + 1.0); +} + +void cryptomatte_clear_samples(FilmSample dst) +{ + int layer_len = imageSize(cryptomatte_img).z; + for (int i = 0; i < layer_len; i++) { + imageStore(cryptomatte_img, ivec3(dst.texel, i), vec4(0.0)); + } +} + +void cryptomatte_store_film_sample(FilmSample dst, + int cryptomatte_layer_id, + vec2 crypto_sample, + out vec4 out_color) +{ + if (crypto_sample.y == 0.0) { + return; + } + for (int i = 0; i < film_buf.cryptomatte_samples_len / 2; i++) { + ivec3 img_co = ivec3(dst.texel, cryptomatte_layer_id + i); + vec4 sample_pair = imageLoad(cryptomatte_img, img_co); + if (cryptomatte_can_merge_sample(sample_pair.xy, crypto_sample)) { + sample_pair.xy = cryptomatte_merge_sample(sample_pair.xy, crypto_sample); + /* In viewport only one layer is active. */ + /* TODO(jbakker): we are displaying the first sample, but we should display the highest + * weighted one. */ + if (cryptomatte_layer_id + i == 0) { + out_color = cryptomatte_false_color(sample_pair.x); + } + } + else if (cryptomatte_can_merge_sample(sample_pair.zw, crypto_sample)) { + sample_pair.zw = cryptomatte_merge_sample(sample_pair.zw, crypto_sample); + } + else if (i == film_buf.cryptomatte_samples_len / 2 - 1) { + /* TODO(jbakker): New hash detected, but there is no space left to store it. Currently we + * will ignore this sample, but ideally we could replace a sample with a lowest weight. */ + continue; + } + else { + continue; + } + imageStore(cryptomatte_img, img_co, sample_pair); + break; + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl new file mode 100644 index 00000000000..120edd9c35e --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl @@ -0,0 +1,77 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +#define CRYPTOMATTE_LEVELS_MAX 16 + +void cryptomatte_load_samples(ivec2 texel, int layer, out vec2 samples[CRYPTOMATTE_LEVELS_MAX]) +{ + int pass_len = divide_ceil(cryptomatte_samples_per_layer, 2); + int layer_id = layer * pass_len; + + /* Read all samples from the cryptomatte layer. */ + for (int p = 0; p < pass_len; p++) { + vec4 pass_sample = imageLoad(cryptomatte_img, ivec3(texel, p + layer_id)); + samples[p * 2] = pass_sample.xy; + samples[p * 2 + 1] = pass_sample.zw; + } + for (int i = pass_len * 2; i < CRYPTOMATTE_LEVELS_MAX; i++) { + samples[i] = vec2(0.0); + } +} + +void cryptomatte_sort_samples(inout vec2 samples[CRYPTOMATTE_LEVELS_MAX]) +{ + /* Sort samples. Lame implementation, can be replaced with a more efficient algorithm. */ + for (int i = 0; i < cryptomatte_samples_per_layer - 1 && samples[i].y != 0.0; i++) { + int highest_index = i; + float highest_weight = samples[i].y; + for (int j = i + 1; j < cryptomatte_samples_per_layer && samples[j].y != 0.0; j++) { + if (samples[j].y > highest_weight) { + highest_index = j; + highest_weight = samples[j].y; + } + }; + + if (highest_index != i) { + vec2 tmp = samples[i]; + samples[i] = samples[highest_index]; + samples[highest_index] = tmp; + } + } +} +void cryptomatte_normalize_weight(float total_weight, inout vec2 samples[CRYPTOMATTE_LEVELS_MAX]) +{ + for (int i = 0; i < CRYPTOMATTE_LEVELS_MAX; i++) { + samples[i].y /= total_weight; + } +} + +void cryptomatte_store_samples(ivec2 texel, int layer, in vec2 samples[CRYPTOMATTE_LEVELS_MAX]) +{ + int pass_len = divide_ceil(cryptomatte_samples_per_layer, 2); + int layer_id = layer * pass_len; + + /* Store samples back to the cryptomatte layer. */ + for (int p = 0; p < pass_len; p++) { + vec4 pass_sample; + pass_sample.xy = samples[p * 2]; + pass_sample.zw = samples[p * 2 + 1]; + imageStore(cryptomatte_img, ivec3(texel, p + layer_id), pass_sample); + } +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + for (int layer = 0; layer < cryptomatte_layer_len; layer++) { + vec2 samples[CRYPTOMATTE_LEVELS_MAX]; + cryptomatte_load_samples(texel, layer, samples); + cryptomatte_sort_samples(samples); + /* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */ + float weight = imageLoad( + weight_img, + ivec3(texel % imageSize(weight_img).xy, FILM_WEIGHT_LAYER_ACCUMULATION)) + .x; + cryptomatte_normalize_weight(weight, samples); + cryptomatte_store_samples(texel, layer, samples); + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl index 26040234fd0..e2aaf9128a5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl @@ -13,13 +13,17 @@ void main() if (film_buf.display_id == -1) { out_color = texelFetch(in_combined_tx, texel_film, 0); } - else if (film_buf.display_is_value) { + else if (film_buf.display_storage_type == PASS_STORAGE_VALUE) { out_color.rgb = imageLoad(value_accum_img, ivec3(texel_film, film_buf.display_id)).rrr; out_color.a = 1.0; } - else { + else if (film_buf.display_storage_type == PASS_STORAGE_COLOR) { out_color = imageLoad(color_accum_img, ivec3(texel_film, film_buf.display_id)); } + else /* PASS_STORAGE_CRYPTOMATTE */ { + out_color = cryptomatte_false_color( + imageLoad(cryptomatte_img, ivec3(texel_film, film_buf.display_id)).r); + } } else { film_process_data(texel_film, out_color, out_depth); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl index 087efa9100d..21b9a83abb9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl @@ -8,6 +8,7 @@ #pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) #pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) #pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_cryptomatte_lib.glsl) /* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */ float film_depth_convert_to_scene(float depth) @@ -158,6 +159,45 @@ void film_sample_accum_combined(FilmSample samp, inout vec4 accum, inout float w weight_accum += weight; } +void film_sample_cryptomatte_accum(FilmSample samp, + int layer, + sampler2D tex, + inout vec2 crypto_samples[4]) +{ + float hash = texelFetch(tex, samp.texel, 0)[layer]; + /* Find existing entry. */ + for (int i = 0; i < 4; i++) { + if (crypto_samples[i].x == hash) { + crypto_samples[i].y += samp.weight; + return; + } + } + /* Overwrite entry with less weight. */ + for (int i = 0; i < 4; i++) { + if (crypto_samples[i].y < samp.weight) { + crypto_samples[i] = vec2(hash, samp.weight); + return; + } + } +} + +void film_cryptomatte_layer_accum_and_store( + FilmSample dst, ivec2 texel_film, int pass_id, int layer_component, inout vec4 out_color) +{ + if (pass_id == -1) { + return; + } + /* x = hash, y = accumed weight. Only keep track of 4 highest weighted samples. */ + vec2 crypto_samples[4] = vec2[4](vec2(0.0), vec2(0.0), vec2(0.0), vec2(0.0)); + for (int i = 0; i < film_buf.samples_len; i++) { + FilmSample src = film_sample_get(i, texel_film); + film_sample_cryptomatte_accum(src, layer_component, cryptomatte_tx, crypto_samples); + } + for (int i = 0; i < 4; i++) { + cryptomatte_store_film_sample(dst, pass_id, crypto_samples[i], out_color); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -698,4 +738,18 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth } film_store_value(dst, film_buf.aov_value_id + aov, aov_accum, out_color); } + + if (film_buf.cryptomatte_samples_len != 0) { + /* Cryptomatte passes cannot be cleared by a weighted store like other passes. */ + if (!film_buf.use_history || film_buf.use_reprojection) { + cryptomatte_clear_samples(dst); + } + + film_cryptomatte_layer_accum_and_store( + dst, texel_film, film_buf.cryptomatte_object_id, 0, out_color); + film_cryptomatte_layer_accum_and_store( + dst, texel_film, film_buf.cryptomatte_asset_id, 1, out_color); + film_cryptomatte_layer_accum_and_store( + dst, texel_film, film_buf.cryptomatte_material_id, 2, out_color); + } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl index 39758c0dfc1..ab29067763d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -107,6 +107,9 @@ void main() imageStore(rp_diffuse_color_img, out_texel, vec4(g_diffuse_data.color, 1.0)); imageStore(rp_specular_color_img, out_texel, vec4(specular_color, 1.0)); imageStore(rp_emission_img, out_texel, vec4(g_emission, 1.0)); + imageStore(rp_cryptomatte_img, + out_texel, + vec4(cryptomatte_object_buf[resource_id], node_tree.crypto_hash, 0.0)); #endif out_radiance.rgb *= 1.0 - g_holdout; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl index 1ef1c1f84b8..442c2579c84 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl @@ -33,6 +33,7 @@ void main() imageStore(rp_diffuse_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0)); imageStore(rp_specular_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0)); imageStore(rp_emission_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0)); + imageStore(rp_cryptomatte_img, out_texel, vec4(0.0)); out_background.rgb = safe_color(g_emission) * (1.0 - g_holdout); out_background.a = saturate(avg(g_transmittance)) * g_holdout; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh index db82a3265d7..4541f14d96c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh @@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(eevee_film) .sampler(13, ImageType::FLOAT_2D_ARRAY, "aov_value_tx") /* Color History for TAA needs to be sampler to leverage bilinear sampling. */ .sampler(14, ImageType::FLOAT_2D, "in_combined_tx") - // .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx") /* TODO */ + .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx") .image(0, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_weight_img") .image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img") /* Color History for TAA needs to be sampler to leverage bilinear sampling. */ @@ -30,6 +30,7 @@ GPU_SHADER_CREATE_INFO(eevee_film) .image(4, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "depth_img") .image(5, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "color_accum_img") .image(6, GPU_R16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "value_accum_img") + .image(7, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "cryptomatte_img") .additional_info("eevee_shared") .additional_info("eevee_velocity_camera") .additional_info("draw_view"); @@ -45,3 +46,13 @@ GPU_SHADER_CREATE_INFO(eevee_film_comp) .local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE) .compute_source("eevee_film_comp.glsl") .additional_info("eevee_film"); + +GPU_SHADER_CREATE_INFO(eevee_film_cryptomatte_post) + .do_static_compilation(true) + .image(0, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "cryptomatte_img") + .image(1, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "weight_img") + .push_constant(Type::INT, "cryptomatte_layer_len") + .push_constant(Type::INT, "cryptomatte_samples_per_layer") + .local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE) + .compute_source("eevee_film_cryptomatte_post_comp.glsl") + .additional_info("eevee_shared"); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 9abdd1f8adf..78d52d4b90e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -92,6 +92,10 @@ GPU_SHADER_CREATE_INFO(eevee_render_pass_out) .image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img") .image_out(RBUFS_EMISSION_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img"); +GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out) + .storage_buf(7, Qualifier::READ, "vec2", "cryptomatte_object_buf[]", Frequency::PASS) + .image_out(7, Qualifier::WRITE, GPU_RGBA32F, "rp_cryptomatte_img"); + GPU_SHADER_CREATE_INFO(eevee_surf_deferred) .vertex_out(eevee_surf_iface) /* NOTE: This removes the possibility of using gl_FragDepth. */ @@ -121,7 +125,10 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0) .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1) .fragment_source("eevee_surf_forward_frag.glsl") - .additional_info("eevee_light_data", "eevee_utility_texture", "eevee_sampling_data" + .additional_info("eevee_cryptomatte_out", + "eevee_light_data", + "eevee_utility_texture", + "eevee_sampling_data" // "eevee_lightprobe_data", // "eevee_shadow_data" /* Optionally added depending on the material. */ @@ -141,7 +148,10 @@ GPU_SHADER_CREATE_INFO(eevee_surf_world) .push_constant(Type::FLOAT, "world_opacity_fade") .fragment_out(0, Type::VEC4, "out_background") .fragment_source("eevee_surf_world_frag.glsl") - .additional_info("eevee_aov_out", "eevee_render_pass_out", "eevee_utility_texture"); + .additional_info("eevee_aov_out", + "eevee_cryptomatte_out", + "eevee_render_pass_out", + "eevee_utility_texture"); #undef image_out #undef image_array_out diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 65ddb80ad55..e54ac99a888 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -460,7 +460,7 @@ GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void) GPENCIL_ViewLayerData **vldata = (GPENCIL_ViewLayerData **)DRW_view_layer_engine_data_ensure( &draw_engine_gpencil_type, gpencil_view_layer_data_free); - /* NOTE(&fclem): Putting this stuff in viewlayer means it is shared by all viewports. + /* NOTE(@fclem): Putting this stuff in view-layer means it is shared by all viewports. * For now it is ok, but in the future, it could become a problem if we implement * the caching system. */ if (*vldata == NULL) { diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 4f520e61936..42c396a0d43 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -799,7 +799,7 @@ static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL } GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, i); - /* When filtering by viewlayer, the mask could be null and must be ignored. */ + /* When filtering by view-layer, the mask could be null and must be ignored. */ if (mask_layer == NULL) { continue; } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 7b80ffd2b88..b49203d85f6 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -207,6 +207,10 @@ struct GPUShader *DRW_shader_create_with_lib_ex(const char *vert, const char *lib, const char *defines, const char *name); +struct GPUShader *DRW_shader_create_compute_with_shaderlib(const char *comp, + const DRWShaderLibrary *lib, + const char *defines, + const char *name); struct GPUShader *DRW_shader_create_with_shaderlib_ex(const char *vert, const char *geom, const char *frag, diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index e60689f0237..c22382b3e09 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -556,8 +556,7 @@ static bool mesh_batch_cache_valid(Object *object, Mesh *me) } if (object->sculpt && object->sculpt->pbvh) { - if (cache->pbvh_is_drawing != BKE_pbvh_is_drawing(object->sculpt->pbvh) || - BKE_pbvh_draw_cache_invalid(object->sculpt->pbvh)) { + if (cache->pbvh_is_drawing != BKE_pbvh_is_drawing(object->sculpt->pbvh)) { return false; } diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.cc b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc index 57efed855f5..a43b23c8969 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.cc +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc @@ -141,7 +141,7 @@ static void pointcloud_batch_cache_ensure_pos(const PointCloud &pointcloud, return; } - const bke::AttributeAccessor attributes = bke::pointcloud_attributes(pointcloud); + const bke::AttributeAccessor attributes = pointcloud.attributes(); const VArraySpan<float3> positions = attributes.lookup<float3>("position", ATTR_DOMAIN_POINT); const VArray<float> radii = attributes.lookup<float>("radius", ATTR_DOMAIN_POINT); /* From the opengl wiki: diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 4e985843123..ab935809f96 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -10,6 +10,7 @@ #include "BKE_attribute.hh" #include "BKE_editmesh.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_scene.h" @@ -807,15 +808,15 @@ struct DRWCacheBuildingContext { }; static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_context, - const int num_vertices, + const int num_verts, const int num_edges, const int num_loops, - const int num_polygons, + const int num_polys, const int *subdiv_polygon_offset) { /* num_loops does not take into account meshes with only loose geometry, which might be meshes - * used as custom bone shapes, so let's check the num_vertices also. */ - if (num_vertices == 0 && num_loops == 0) { + * used as custom bone shapes, so let's check the num_verts also. */ + if (num_verts == 0 && num_loops == 0) { return false; } @@ -826,12 +827,12 @@ static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_con if (num_loops != 0) { cache->num_subdiv_edges = (uint)num_edges; cache->num_subdiv_loops = (uint)num_loops; - cache->num_subdiv_verts = (uint)num_vertices; - cache->num_subdiv_quads = (uint)num_polygons; + cache->num_subdiv_verts = (uint)num_verts; + cache->num_subdiv_quads = (uint)num_polys; cache->subdiv_polygon_offset = static_cast<int *>(MEM_dupallocN(subdiv_polygon_offset)); } - cache->may_have_loose_geom = num_vertices != 0 || num_edges != 0; + cache->may_have_loose_geom = num_verts != 0 || num_edges != 0; /* Initialize cache buffers, prefer dynamic usage so we can reuse memory on the host even after * it was sent to the device, since we may use the data while building other buffers on the CPU @@ -882,7 +883,7 @@ static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_con if (cache->num_subdiv_verts) { ctx->vert_origindex_map = static_cast<int *>( MEM_mallocN(cache->num_subdiv_verts * sizeof(int), "subdiv_vert_origindex_map")); - for (int i = 0; i < num_vertices; i++) { + for (int i = 0; i < num_verts; i++) { ctx->vert_origindex_map[i] = -1; } } @@ -1967,9 +1968,8 @@ static void draw_subdiv_cache_ensure_mat_offsets(DRWSubdivCache *cache, return; } - const blender::VArraySpan<int> material_indices = blender::bke::mesh_attributes(*mesh_eval) - .lookup_or_default<int>( - "material_index", ATTR_DOMAIN_FACE, 0); + const blender::VArraySpan<int> material_indices = mesh_eval->attributes().lookup_or_default<int>( + "material_index", ATTR_DOMAIN_FACE, 0); /* Count number of subdivided polygons for each material. */ int *mat_start = static_cast<int *>(MEM_callocN(sizeof(int) * mat_len, "subdiv mat_start")); @@ -2156,7 +2156,17 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac int subd_vert_offset = 0; /* Subdivide each loose coarse edge. */ + const Span<MVert> coarse_verts = coarse_mesh->verts(); const Span<MEdge> coarse_edges = coarse_mesh->edges(); + + int *vert_to_edge_buffer; + MeshElemMap *vert_to_edge_map; + BKE_mesh_vert_edge_map_create(&vert_to_edge_map, + &vert_to_edge_buffer, + coarse_edges.data(), + coarse_mesh->totvert, + coarse_edges.size()); + for (int i = 0; i < coarse_loose_edge_len; i++) { const int coarse_edge_index = cache->loose_geom.edges[i]; const MEdge *coarse_edge = &coarse_edges[cache->loose_geom.edges[i]]; @@ -2170,8 +2180,13 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac DRWSubdivLooseVertex &subd_v1 = loose_subd_verts[subd_vert_offset]; subd_v1.coarse_vertex_index = (i == 0) ? coarse_edge->v1 : -1u; const float u1 = i * inv_resolution_1; - BKE_subdiv_mesh_interpolate_position_on_edge( - coarse_mesh, coarse_edge, is_simple, u1, subd_v1.co); + BKE_subdiv_mesh_interpolate_position_on_edge(coarse_verts.data(), + coarse_edges.data(), + vert_to_edge_map, + coarse_edge_index, + is_simple, + u1, + subd_v1.co); subd_edge.loose_subdiv_v1_index = subd_vert_offset++; @@ -2179,15 +2194,22 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac DRWSubdivLooseVertex &subd_v2 = loose_subd_verts[subd_vert_offset]; subd_v2.coarse_vertex_index = ((i + 1) == resolution - 1) ? coarse_edge->v2 : -1u; const float u2 = (i + 1) * inv_resolution_1; - BKE_subdiv_mesh_interpolate_position_on_edge( - coarse_mesh, coarse_edge, is_simple, u2, subd_v2.co); + BKE_subdiv_mesh_interpolate_position_on_edge(coarse_verts.data(), + coarse_edges.data(), + vert_to_edge_map, + coarse_edge_index, + is_simple, + u2, + subd_v2.co); subd_edge.loose_subdiv_v2_index = subd_vert_offset++; } } + MEM_freeN(vert_to_edge_buffer); + MEM_freeN(vert_to_edge_map); + /* Copy the remaining loose_verts. */ - const Span<MVert> coarse_verts = coarse_mesh->verts(); for (int i = 0; i < coarse_loose_vert_len; i++) { const int coarse_vertex_index = cache->loose_geom.verts[i]; const MVert &coarse_vertex = coarse_verts[coarse_vertex_index]; diff --git a/source/blender/draw/intern/draw_command.hh b/source/blender/draw/intern/draw_command.hh index b9117580d91..46a9199a267 100644 --- a/source/blender/draw/intern/draw_command.hh +++ b/source/blender/draw/intern/draw_command.hh @@ -531,4 +531,4 @@ class DrawMultiBuf { /** \} */ -}; // namespace blender::draw::command
\ No newline at end of file +}; // namespace blender::draw::command diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 2a54ca7f899..9761aa8c789 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -181,7 +181,7 @@ static void drw_task_graph_deinit(void) bool DRW_object_is_renderable(const Object *ob) { - BLI_assert((ob->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0); + BLI_assert((ob->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0); if (ob->type == OB_MESH) { if ((ob == DST.draw_ctx.object_edit) || DRW_object_is_in_edit_mode(ob)) { @@ -2485,7 +2485,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, } if (use_pose_exception && (ob->mode & OB_MODE_POSE)) { - if ((ob->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) { + if ((ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) { continue; } } diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh index aff56b0307b..fbd3d28d3f4 100644 --- a/source/blender/draw/intern/draw_manager.hh +++ b/source/blender/draw/intern/draw_manager.hh @@ -98,7 +98,6 @@ class Manager { /** Number of object attribute recorded. */ uint attribute_len_ = 0; - Object *object = nullptr; Object *object_active = nullptr; public: diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 4bc3898c5e7..1ada99093c6 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -297,6 +297,18 @@ GPUShader *DRW_shader_create_with_lib_ex(const char *vert, return sh; } +GPUShader *DRW_shader_create_compute_with_shaderlib(const char *comp, + const DRWShaderLibrary *lib, + const char *defines, + const char *name) +{ + char *comp_with_lib = DRW_shader_library_create_shader_string(lib, comp); + GPUShader *sh = GPU_shader_create_compute(comp_with_lib, NULL, defines, name); + MEM_SAFE_FREE(comp_with_lib); + + return sh; +} + GPUShader *DRW_shader_create_with_shaderlib_ex(const char *vert, const char *geom, const char *frag, diff --git a/source/blender/draw/intern/draw_resource.hh b/source/blender/draw/intern/draw_resource.hh index 22ee43592a9..2df38e32ed2 100644 --- a/source/blender/draw/intern/draw_resource.hh +++ b/source/blender/draw/intern/draw_resource.hh @@ -85,10 +85,11 @@ inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active if (ref.dupli_object == nullptr) { /* TODO(fclem): this is rather costly to do at draw time. Maybe we can * put it in ob->runtime and make depsgraph ensure it is up to date. */ - random = BLI_hash_int_2d(BLI_hash_string(ref.object->id.name + 2), 0) * (1.0f / 0xFFFFFFFF); + random = BLI_hash_int_2d(BLI_hash_string(ref.object->id.name + 2), 0) * + (1.0f / (float)0xFFFFFFFF); } else { - random = ref.dupli_object->random_id * (1.0f / 0xFFFFFFFF); + random = ref.dupli_object->random_id * (1.0f / (float)0xFFFFFFFF); } /* Default values. Set if needed. */ random = 0.0f; diff --git a/source/blender/draw/intern/draw_view.cc b/source/blender/draw/intern/draw_view.cc index 326e8629e52..cb0e1370c28 100644 --- a/source/blender/draw/intern/draw_view.cc +++ b/source/blender/draw/intern/draw_view.cc @@ -260,13 +260,15 @@ void View::update_view_vectors() } /** - * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and - * view_vecs[1] is the vector going from the near-bottom-left corner to - * the far-top-right corner. - * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner - * when Z = 1, and top-left corner if Z = 1. - * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) - * distance from the near plane to the far clip plane. + * - If orthographic: + * `view_vecs[0]` is the near-bottom-left corner of the frustum and + * `view_vecs[1]` is the vector going from the near-bottom-left corner to + * the far-top-right corner. + * - If perspective: + * `view_vecs[0].xy` and `view_vecs[1].xy` are respectively the bottom-left corner + * when `Z = 1`, and top-left corner if `Z = 1`. + * `view_vecs[0].z` the near clip distance and `view_vecs[1].z` is the (signed) + * distance from the near plane to the far clip plane. */ copy_v3_v3(data_.viewvecs[0], view_vecs[0]); diff --git a/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl b/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl index d834435e54e..511d4e49651 100644 --- a/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl +++ b/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl @@ -61,4 +61,4 @@ void main() vec3 size_inv = safe_rcp(size); infos_buf[resource_id].orco_add = -loc * size_inv; infos_buf[resource_id].orco_mul = size_inv; -}
\ No newline at end of file +} diff --git a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl index 7ec58c8f919..86add2d1fe2 100644 --- a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl +++ b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl @@ -43,4 +43,4 @@ void main() mask_visibility_bit(); } } -}
\ No newline at end of file +} diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index e7baac63aae..892fd999fb5 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -360,6 +360,8 @@ static void test_eevee_glsl_shaders_static() EXPECT_NE(EEVEE_shaders_volumes_integration_sh_get(), nullptr); EXPECT_NE(EEVEE_shaders_volumes_resolve_sh_get(false), nullptr); EXPECT_NE(EEVEE_shaders_volumes_resolve_sh_get(true), nullptr); + EXPECT_NE(EEVEE_shaders_volumes_resolve_comp_sh_get(false), nullptr); + EXPECT_NE(EEVEE_shaders_volumes_resolve_comp_sh_get(true), nullptr); EXPECT_NE(EEVEE_shaders_volumes_accum_sh_get(), nullptr); EXPECT_NE(EEVEE_shaders_studiolight_probe_sh_get(), nullptr); EXPECT_NE(EEVEE_shaders_studiolight_background_sh_get(), nullptr); diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 4fc01a546fa..5b4d436b0e0 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1880,8 +1880,8 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) { /* Layer visibility - we check both object and base, * since these may not be in sync yet. */ - if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0 || - (base->flag & BASE_VISIBLE_VIEWLAYER) == 0) { + if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0 || + (base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) { continue; } @@ -3093,7 +3093,8 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, */ if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) { /* layer visibility - we check both object and base, since these may not be in sync yet */ - if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0 || (base->flag & BASE_VISIBLE_VIEWLAYER) == 0) { + if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0 || + (base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) { return false; } diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 9172d7d377b..726724181a9 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -51,7 +51,6 @@ #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_transform.h" -#include "ED_types.h" #include "ED_util.h" #include "DEG_depsgraph.h" @@ -459,9 +458,7 @@ static void draw_marker_line(const uchar *color, int xpos, int ymin, int ymax) static int marker_get_icon_id(TimeMarker *marker, int flag) { if (flag & DRAW_MARKERS_LOCAL) { - return (marker->flag & ACTIVE) ? ICON_PMARKER_ACT : - (marker->flag & SELECT) ? ICON_PMARKER_SEL : - ICON_PMARKER; + return (marker->flag & SELECT) ? ICON_PMARKER_SEL : ICON_PMARKER; } #ifdef DURIAN_CAMERA_SWITCH if (marker->camera) { diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index dbb768d9e34..2e4e13d2773 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -920,7 +920,7 @@ EditBone *duplicateEditBone(EditBone *cur_bone, const char *name, ListBase *edit static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool do_flip_names = RNA_boolean_get(op->ptr, "do_flip_names"); @@ -1095,7 +1095,7 @@ static EditBone *get_symmetrized_bone(bArmature *arm, EditBone *bone) */ static int armature_symmetrize_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int direction = RNA_enum_get(op->ptr, "direction"); const int axis = 0; @@ -1351,7 +1351,7 @@ void ARMATURE_OT_symmetrize(wmOperatorType *ot) /* if forked && mirror-edit: makes two bones with flipped names */ static int armature_extrude_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool forked = RNA_boolean_get(op->ptr, "forked"); bool changed_multi = false; diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 86902e10022..81b1c096d76 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -254,7 +254,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = { static int armature_calc_roll_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob_active = CTX_data_edit_object(C); int ret = OPERATOR_FINISHED; @@ -285,7 +285,7 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op) copy_m3_m4(imat, ob->obmat); invert_m3(imat); - if (type == CALC_ROLL_CURSOR) { /* Cursor */ + if (type == CALC_ROLL_CURSOR) { /* Cursor */ float cursor_local[3]; const View3DCursor *cursor = &scene->cursor; @@ -463,7 +463,7 @@ void ARMATURE_OT_calculate_roll(wmOperatorType *ot) static int armature_roll_clear_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const float roll = RNA_float_get(op->ptr, "roll"); @@ -885,7 +885,7 @@ static void armature_clear_swap_done_flags(bArmature *arm) static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1159,7 +1159,7 @@ void ARMATURE_OT_align(wmOperatorType *ot) static int armature_split_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -1229,7 +1229,7 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1303,7 +1303,7 @@ static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p) static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); EditBone *ebone, *ebone_next; bool changed_multi = false; @@ -1476,7 +1476,7 @@ void ARMATURE_OT_dissolve(wmOperatorType *ot) static int armature_hide_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0; @@ -1542,7 +1542,7 @@ void ARMATURE_OT_hide(wmOperatorType *ot) static int armature_reveal_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool select = RNA_boolean_get(op->ptr, "select"); uint objects_len = 0; diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index abee402e63e..26ec05cc503 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -429,7 +429,7 @@ void ED_armature_bones_flip_names(Main *bmain, static int armature_flip_names_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob_active = CTX_data_edit_object(C); @@ -517,7 +517,7 @@ void ARMATURE_OT_flip_names(wmOperatorType *ot) static int armature_autoside_names_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Main *bmain = CTX_data_main(C); char newname[MAXBONENAME]; diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index da09a793252..9f1883ccac0 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -974,7 +974,7 @@ static void editbone_clear_parent(EditBone *ebone, int mode) static int armature_parent_clear_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int val = RNA_enum_get(op->ptr, "type"); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 82bf0439a13..e490f21f16d 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -495,7 +495,7 @@ static int armature_select_linked_exec(bContext *C, wmOperator *op) const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); bool changed_multi = false; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -950,7 +950,7 @@ bool ED_armature_edit_select_pick_bone(bContext *C, const int selmask, const struct SelectPick_Params *params) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); bool changed = false; @@ -1493,7 +1493,7 @@ static void armature_select_more_less(Object *ob, bool more) static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1533,7 +1533,7 @@ void ARMATURE_OT_select_more(wmOperatorType *ot) static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1608,7 +1608,7 @@ static float bone_length_squared_worldspace_get(Object *ob, EditBone *ebone) static void select_similar_length(bContext *C, const float thresh) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob_act = CTX_data_edit_object(C); EditBone *ebone_act = CTX_data_active_bone(C); @@ -1659,7 +1659,7 @@ static void bone_direction_worldspace_get(Object *ob, EditBone *ebone, float *r_ static void select_similar_direction(bContext *C, const float thresh) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob_act = CTX_data_edit_object(C); EditBone *ebone_act = CTX_data_active_bone(C); @@ -1698,7 +1698,7 @@ static void select_similar_direction(bContext *C, const float thresh) static void select_similar_layer(bContext *C) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); EditBone *ebone_act = CTX_data_active_bone(C); @@ -1729,7 +1729,7 @@ static void select_similar_layer(bContext *C) static void select_similar_prefix(bContext *C) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); EditBone *ebone_act = CTX_data_active_bone(C); @@ -1772,7 +1772,7 @@ static void select_similar_prefix(bContext *C) static void select_similar_suffix(bContext *C) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); EditBone *ebone_act = CTX_data_active_bone(C); @@ -2112,7 +2112,7 @@ void ARMATURE_OT_select_hierarchy(wmOperatorType *ot) */ static int armature_select_mirror_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool active_only = RNA_boolean_get(op->ptr, "only_active"); const bool extend = RNA_boolean_get(op->ptr, "extend"); diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index ffcb06fc597..379ad4f5376 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -141,7 +141,7 @@ static bool armature_undosys_step_encode(struct bContext *C, struct Main *bmain, /* Important not to use the 3D view when getting objects because all objects * outside of this list will be moved out of edit-mode when reading back undo steps. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len); diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index a6366381df0..56a3744e383 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -499,7 +499,7 @@ void POSE_OT_paths_range_update(wmOperatorType *ot) static int pose_flip_names_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers"); @@ -857,7 +857,7 @@ static int pose_bone_layers_exec(bContext *C, wmOperator *op) struct Main *bmain = CTX_data_main(C); wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { @@ -1003,7 +1003,7 @@ static int hide_pose_bone_fn(Object *ob, Bone *bone, void *ptr) /* active object is armature in posemode, poll checked */ static int pose_hide_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_object_pose_array_get_unique( @@ -1070,7 +1070,7 @@ static int show_pose_bone_cb(Object *ob, Bone *bone, void *data) /* active object is armature in posemode, poll checked */ static int pose_reveal_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_object_pose_array_get_unique( diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 515ec17efd6..6a31c7f1496 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -1209,7 +1209,7 @@ void POSE_OT_select_grouped(wmOperatorType *ot) */ static int pose_select_mirror_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob_active = CTX_data_active_object(C); diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index ad7bc6d12c8..4211dd78b88 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -491,7 +491,7 @@ void POSE_OT_armature_apply(wmOperatorType *ot) /* set the current pose as the restpose */ static int pose_visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 05d0b7d0af4..ba7b56db3ec 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -781,6 +781,7 @@ static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSE const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(false); if (!items) { *r_free = false; + return nullptr; } *r_free = true; diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 06550714b89..46634674c34 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -47,7 +47,6 @@ #include "ED_select_utils.h" #include "ED_transform.h" #include "ED_transform_snap_object_context.h" -#include "ED_types.h" #include "ED_view3d.h" #include "curve_intern.h" @@ -1469,7 +1468,7 @@ void CURVE_OT_separate(wmOperatorType *ot) static int curve_split_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); bool changed = false; @@ -2089,7 +2088,7 @@ bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag) const int copy_from = intvls_u[i - 1]; const int copy_to = intvls_u[i]; const int copy_count = copy_to - copy_from + 1; - const bool sel_status = selected_u || selected_v ? SELECT : DESELECT; + const bool sel_status = selected_u || selected_v ? true : false; ED_curve_bpcpy(editnurb, new_bp_u_v, old_bp_v + copy_from, copy_count); select_bpoints(new_bp_u_v, 1, copy_count, sel_status, flag, HIDDEN); new_bp_u_v += copy_count; @@ -2155,7 +2154,7 @@ static void adduplicateflagNurb( starta = a; while ((bezt->f1 & flag) || (bezt->f2 & flag) || (bezt->f3 & flag)) { if (!split) { - select_beztriple(bezt, DESELECT, flag, HIDDEN); + select_beztriple(bezt, false, flag, HIDDEN); } enda = a; if (a >= nu->pntsu - 1) { @@ -2195,7 +2194,7 @@ static void adduplicateflagNurb( } for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) { - select_beztriple(bezt1, SELECT, flag, HIDDEN); + select_beztriple(bezt1, true, flag, HIDDEN); } BLI_addtail(newnurb, newnu); @@ -2213,7 +2212,7 @@ static void adduplicateflagNurb( newnu->flagu &= ~CU_NURB_CYCLIC; for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) { - select_beztriple(bezt1, SELECT, flag, HIDDEN); + select_beztriple(bezt1, true, flag, HIDDEN); } BLI_addtail(newnurb, newnu); @@ -2225,7 +2224,7 @@ static void adduplicateflagNurb( starta = a; while (bp->f1 & flag) { if (!split) { - select_bpoint(bp, DESELECT, flag, HIDDEN); + select_bpoint(bp, false, flag, HIDDEN); } enda = a; if (a >= nu->pntsu - 1) { @@ -2265,7 +2264,7 @@ static void adduplicateflagNurb( } for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) { - select_bpoint(bp1, SELECT, flag, HIDDEN); + select_bpoint(bp1, true, flag, HIDDEN); } BLI_addtail(newnurb, newnu); @@ -2283,7 +2282,7 @@ static void adduplicateflagNurb( newnu->flagu &= ~CU_NURB_CYCLIC; for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) { - select_bpoint(bp1, SELECT, flag, HIDDEN); + select_bpoint(bp1, true, flag, HIDDEN); } BLI_addtail(newnurb, newnu); @@ -2503,7 +2502,7 @@ static void adduplicateflagNurb( for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) { bp1->f1 &= ~SURF_SEEN; if (!split) { - select_bpoint(bp1, DESELECT, flag, HIDDEN); + select_bpoint(bp1, false, flag, HIDDEN); } } } @@ -2547,7 +2546,7 @@ static void adduplicateflagNurb( static int switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -2609,7 +2608,7 @@ void CURVE_OT_switch_direction(wmOperatorType *ot) static int set_goal_weight_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -2676,7 +2675,7 @@ void CURVE_OT_spline_weight_set(wmOperatorType *ot) static int set_radius_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -2788,7 +2787,7 @@ static void smooth_single_bp(BPoint *bp, static int smooth_exec(bContext *C, wmOperator *UNUSED(op)) { const float factor = 1.0f / 6.0f; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -3085,7 +3084,7 @@ static void curve_smooth_value(ListBase *editnurb, const int bezt_offsetof, cons static int curve_smooth_weight_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -3129,7 +3128,7 @@ void CURVE_OT_smooth_weight(wmOperatorType *ot) static int curve_smooth_radius_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -3173,7 +3172,7 @@ void CURVE_OT_smooth_radius(wmOperatorType *ot) static int curve_smooth_tilt_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -3217,7 +3216,7 @@ void CURVE_OT_smooth_tilt(wmOperatorType *ot) static int hide_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -3246,11 +3245,11 @@ static int hide_exec(bContext *C, wmOperator *op) sel = 0; while (a--) { if (invert == 0 && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + select_beztriple(bezt, false, SELECT, HIDDEN); bezt->hide = 1; } else if (invert && !BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + select_beztriple(bezt, false, SELECT, HIDDEN); bezt->hide = 1; } if (bezt->hide) { @@ -3268,11 +3267,11 @@ static int hide_exec(bContext *C, wmOperator *op) sel = 0; while (a--) { if (invert == 0 && (bp->f1 & SELECT)) { - select_bpoint(bp, DESELECT, SELECT, HIDDEN); + select_bpoint(bp, false, SELECT, HIDDEN); bp->hide = 1; } else if (invert && (bp->f1 & SELECT) == 0) { - select_bpoint(bp, DESELECT, SELECT, HIDDEN); + select_bpoint(bp, false, SELECT, HIDDEN); bp->hide = 1; } if (bp->hide) { @@ -3320,7 +3319,7 @@ void CURVE_OT_hide(wmOperatorType *ot) static int reveal_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool select = RNA_boolean_get(op->ptr, "select"); bool changed_multi = false; @@ -3800,7 +3799,7 @@ static int subdivide_exec(bContext *C, wmOperator *op) const int number_cuts = RNA_int_get(op->ptr, "number_cuts"); Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -3859,7 +3858,7 @@ void CURVE_OT_subdivide(wmOperatorType *ot) static int set_spline_type_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -3953,7 +3952,7 @@ void CURVE_OT_spline_type_set(wmOperatorType *ot) static int set_handle_type_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); const int handle_type = RNA_enum_get(op->ptr, "type"); @@ -4016,7 +4015,7 @@ void CURVE_OT_handle_type_set(wmOperatorType *ot) static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -4367,7 +4366,7 @@ static bool merge_2_nurb(Curve *cu, ListBase *editnurb, Nurb *nu1, Nurb *nu2) keyIndex_updateBP(cu->editnurb, bp1, bp, 1); *bp = *bp1; bp1++; - select_bpoint(bp, SELECT, SELECT, HIDDEN); + select_bpoint(bp, true, SELECT, HIDDEN); } else { keyIndex_updateBP(cu->editnurb, bp2, bp, 1); @@ -4457,7 +4456,7 @@ static int merge_nurb(View3D *v3d, Object *obedit) static int make_segment_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -4823,7 +4822,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, bezt->f2 |= SELECT; } else { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); + select_beztriple(bezt, true, SELECT, HIDDEN); } } else { @@ -4837,7 +4836,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, BKE_curve_nurb_vert_active_set(cu, nu, bezt); } else { - select_bpoint(bp, SELECT, SELECT, HIDDEN); + select_bpoint(bp, true, SELECT, HIDDEN); BKE_curve_nurb_vert_active_set(cu, nu, bp); } break; @@ -4849,7 +4848,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, bezt->f2 &= ~SELECT; } else { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + select_beztriple(bezt, false, SELECT, HIDDEN); } if (bezt == vert) { cu->actvert = CU_ACT_NONE; @@ -4863,7 +4862,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, } } else { - select_bpoint(bp, DESELECT, SELECT, HIDDEN); + select_bpoint(bp, false, SELECT, HIDDEN); if (bp == vert) { cu->actvert = CU_ACT_NONE; } @@ -4878,7 +4877,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, bezt->f2 &= ~SELECT; } else { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + select_beztriple(bezt, false, SELECT, HIDDEN); } if (bezt == vert) { cu->actvert = CU_ACT_NONE; @@ -4889,7 +4888,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, bezt->f2 |= SELECT; } else { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); + select_beztriple(bezt, true, SELECT, HIDDEN); } BKE_curve_nurb_vert_active_set(cu, nu, bezt); } @@ -4903,13 +4902,13 @@ bool ED_curve_editnurb_select_pick(bContext *C, } else { if (bp->f1 & SELECT) { - select_bpoint(bp, DESELECT, SELECT, HIDDEN); + select_bpoint(bp, false, SELECT, HIDDEN); if (bp == vert) { cu->actvert = CU_ACT_NONE; } } else { - select_bpoint(bp, SELECT, SELECT, HIDDEN); + select_bpoint(bp, true, SELECT, HIDDEN); BKE_curve_nurb_vert_active_set(cu, nu, bp); } } @@ -4925,7 +4924,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, bezt->f2 |= SELECT; } else { - select_beztriple(bezt, SELECT, SELECT, HIDDEN); + select_beztriple(bezt, true, SELECT, HIDDEN); } } else { @@ -4939,7 +4938,7 @@ bool ED_curve_editnurb_select_pick(bContext *C, BKE_curve_nurb_vert_active_set(cu, nu, bezt); } else { - select_bpoint(bp, SELECT, SELECT, HIDDEN); + select_bpoint(bp, true, SELECT, HIDDEN); BKE_curve_nurb_vert_active_set(cu, nu, bp); } break; @@ -5063,7 +5062,7 @@ bool ed_editnurb_spin( static int spin_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = ED_view3d_context_rv3d(C); @@ -5722,7 +5721,7 @@ void CURVE_OT_vertex_add(wmOperatorType *ot) static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -5865,7 +5864,7 @@ static int toggle_cyclic_exec(bContext *C, wmOperator *op) { const int direction = RNA_enum_get(op->ptr, "direction"); View3D *v3d = CTX_wm_view3d(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); bool changed_multi = false; @@ -5954,7 +5953,7 @@ void CURVE_OT_cyclic_toggle(wmOperatorType *ot) static int duplicate_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -6427,7 +6426,7 @@ static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split) if (split) { /* deselect for split operator */ for (b = 0, bezt1 = nu->bezt; b < nu->pntsu; b++, bezt1++) { - select_beztriple(bezt1, DESELECT, SELECT, true); + select_beztriple(bezt1, false, SELECT, true); } } @@ -6437,7 +6436,7 @@ static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split) if (split) { /* deselect for split operator */ for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) { - select_bpoint(bp1, DESELECT, SELECT, HIDDEN); + select_bpoint(bp1, false, SELECT, HIDDEN); } } @@ -6463,7 +6462,7 @@ static int curve_delete_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); View3D *v3d = CTX_wm_view3d(C); eCurveElem_Types type = RNA_enum_get(op->ptr, "type"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -6640,7 +6639,7 @@ void ed_dissolve_bez_segment(BezTriple *bezt_prev, static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -6735,7 +6734,7 @@ static int curve_decimate_exec(bContext *C, wmOperator *op) float ratio = RNA_float_get(op->ptr, "ratio"); bool all_supported_multi = true; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -6816,7 +6815,7 @@ void CURVE_OT_decimate(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { View3D *v3d = CTX_wm_view3d(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); int clear = (STREQ(op->idname, "CURVE_OT_shade_flat")); uint objects_len; @@ -7011,7 +7010,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) static int clear_tilt_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c index bbcbd4b4fb8..4015ae545da 100644 --- a/source/blender/editors/curve/editcurve_select.c +++ b/source/blender/editors/curve/editcurve_select.c @@ -30,7 +30,6 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_select_utils.h" -#include "ED_types.h" #include "ED_view3d.h" #include "curve_intern.h" @@ -47,7 +46,7 @@ bool select_beztriple(BezTriple *bezt, bool selstatus, uint8_t flag, eVisible_Types hidden) { if ((bezt->hide == 0) || (hidden == HIDDEN)) { - if (selstatus == SELECT) { /* selects */ + if (selstatus) { /* selects */ bezt->f1 |= flag; bezt->f2 |= flag; bezt->f3 |= flag; @@ -66,7 +65,7 @@ bool select_beztriple(BezTriple *bezt, bool selstatus, uint8_t flag, eVisible_Ty bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden) { if ((bp->hide == 0) || (hidden == 1)) { - if (selstatus == SELECT) { + if (selstatus) { bp->f1 |= flag; return true; } @@ -80,17 +79,17 @@ bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden) static bool swap_selection_beztriple(BezTriple *bezt) { if (bezt->f2 & SELECT) { - return select_beztriple(bezt, DESELECT, SELECT, VISIBLE); + return select_beztriple(bezt, false, SELECT, VISIBLE); } - return select_beztriple(bezt, SELECT, SELECT, VISIBLE); + return select_beztriple(bezt, true, SELECT, VISIBLE); } static bool swap_selection_bpoint(BPoint *bp) { if (bp->f1 & SELECT) { - return select_bpoint(bp, DESELECT, SELECT, VISIBLE); + return select_bpoint(bp, false, SELECT, VISIBLE); } - return select_bpoint(bp, SELECT, SELECT, VISIBLE); + return select_bpoint(bp, true, SELECT, VISIBLE); } bool ED_curve_nurb_select_check(const View3D *v3d, const Nurb *nu) @@ -336,9 +335,9 @@ static void select_adjacent_cp(ListBase *editnurb, break; } if ((lastsel == false) && (bezt->hide == 0) && - ((bezt->f2 & SELECT) || (selstatus == DESELECT))) { + ((bezt->f2 & SELECT) || (selstatus == false))) { bezt += next; - if (!(bezt->f2 & SELECT) || (selstatus == DESELECT)) { + if (!(bezt->f2 & SELECT) || (selstatus == false)) { bool sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE); if (sel && !cont) { lastsel = true; @@ -363,10 +362,9 @@ static void select_adjacent_cp(ListBase *editnurb, if (a - abs(next) < 0) { break; } - if ((lastsel == false) && (bp->hide == 0) && - ((bp->f1 & SELECT) || (selstatus == DESELECT))) { + if ((lastsel == false) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == false))) { bp += next; - if (!(bp->f1 & SELECT) || (selstatus == DESELECT)) { + if (!(bp->f1 & SELECT) || (selstatus == false)) { bool sel = select_bpoint(bp, selstatus, SELECT, VISIBLE); if (sel && !cont) { lastsel = true; @@ -470,7 +468,7 @@ static void selectend_nurb(Object *obedit, eEndPoint_Types selfirst, bool doswap static int de_select_first_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -478,7 +476,7 @@ static int de_select_first_exec(bContext *C, wmOperator *UNUSED(op)) for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - selectend_nurb(obedit, FIRST, true, DESELECT); + selectend_nurb(obedit, FIRST, true, false); DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); BKE_curve_nurb_vert_active_validate(obedit->data); @@ -512,7 +510,7 @@ static int de_select_last_exec(bContext *C, wmOperator *UNUSED(op)) for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - selectend_nurb(obedit, LAST, true, DESELECT); + selectend_nurb(obedit, LAST, true, false); DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); BKE_curve_nurb_vert_active_validate(obedit->data); @@ -546,6 +544,7 @@ void CURVE_OT_de_select_last(wmOperatorType *ot) static int de_select_all_exec(bContext *C, wmOperator *op) { int action = RNA_enum_get(op->ptr, "action"); + Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -783,12 +782,12 @@ static int select_row_exec(bContext *C, wmOperator *UNUSED(op)) for (b = 0; b < nu->pntsu; b++, bp++) { if (direction) { if (a == v) { - select_bpoint(bp, SELECT, SELECT, VISIBLE); + select_bpoint(bp, true, SELECT, VISIBLE); } } else { if (b == u) { - select_bpoint(bp, SELECT, SELECT, VISIBLE); + select_bpoint(bp, true, SELECT, VISIBLE); } } } @@ -928,7 +927,7 @@ static void curve_select_more(Object *obedit) if (a % nu->pntsu != 0) { tempbp = bp - 1; if (!(tempbp->f1 & SELECT)) { - select_bpoint(tempbp, SELECT, SELECT, VISIBLE); + select_bpoint(tempbp, true, SELECT, VISIBLE); } } @@ -937,7 +936,7 @@ static void curve_select_more(Object *obedit) sel = 0; tempbp = bp + nu->pntsu; if (!(tempbp->f1 & SELECT)) { - sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE); + sel = select_bpoint(tempbp, true, SELECT, VISIBLE); } /* make sure selected bpoint is discarded */ if (sel == 1) { @@ -949,7 +948,7 @@ static void curve_select_more(Object *obedit) if (a + nu->pntsu < nu->pntsu * nu->pntsv) { tempbp = bp - nu->pntsu; if (!(tempbp->f1 & SELECT)) { - select_bpoint(tempbp, SELECT, SELECT, VISIBLE); + select_bpoint(tempbp, true, SELECT, VISIBLE); } } @@ -958,7 +957,7 @@ static void curve_select_more(Object *obedit) sel = 0; tempbp = bp + 1; if (!(tempbp->f1 & SELECT)) { - sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE); + sel = select_bpoint(tempbp, true, SELECT, VISIBLE); } if (sel) { bp++; @@ -1086,7 +1085,7 @@ static void curve_select_less(Object *obedit) } if (sel != 4) { - select_bpoint(bp, DESELECT, SELECT, VISIBLE); + select_bpoint(bp, false, SELECT, VISIBLE); BLI_BITMAP_ENABLE(selbpoints, a); } } @@ -1136,7 +1135,7 @@ static void curve_select_less(Object *obedit) } if (sel != 2) { - select_beztriple(bezt, DESELECT, SELECT, VISIBLE); + select_beztriple(bezt, false, SELECT, VISIBLE); lastsel = true; } else { @@ -1181,7 +1180,7 @@ static void curve_select_less(Object *obedit) } if (sel != 2) { - select_bpoint(bp, DESELECT, SELECT, VISIBLE); + select_bpoint(bp, false, SELECT, VISIBLE); lastsel = true; } else { @@ -1201,7 +1200,7 @@ static void curve_select_less(Object *obedit) static int curve_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1243,7 +1242,7 @@ static int curve_select_random_exec(bContext *C, wmOperator *op) const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1367,7 +1366,7 @@ static void select_nth_bezt(Nurb *nu, BezTriple *bezt, const struct CheckerInter while (a--) { const int depth = abs(start - a); if (!WM_operator_properties_checker_interval_test(params, depth)) { - select_beztriple(bezt, DESELECT, SELECT, HIDDEN); + select_beztriple(bezt, false, SELECT, HIDDEN); } bezt--; @@ -1390,7 +1389,7 @@ static void select_nth_bp(Nurb *nu, BPoint *bp, const struct CheckerIntervalPara while (a--) { const int depth = abs(pnt - startpnt) + abs(row - startrow); if (!WM_operator_properties_checker_interval_test(params, depth)) { - select_bpoint(bp, DESELECT, SELECT, HIDDEN); + select_bpoint(bp, false, SELECT, HIDDEN); } pnt--; @@ -1424,7 +1423,7 @@ static bool ed_curve_select_nth(Curve *cu, const struct CheckerIntervalParams *p static int select_nth_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *obact = CTX_data_edit_object(C); View3D *v3d = CTX_wm_view3d(C); @@ -1654,7 +1653,7 @@ static bool curve_nurb_select_similar_type(Object *ob, } if (select) { - select_beztriple(bezt, SELECT, SELECT, VISIBLE); + select_beztriple(bezt, true, SELECT, VISIBLE); changed = true; } } @@ -1699,7 +1698,7 @@ static bool curve_nurb_select_similar_type(Object *ob, } if (select) { - select_bpoint(bp, SELECT, SELECT, VISIBLE); + select_bpoint(bp, true, SELECT, VISIBLE); changed = true; } } @@ -1715,7 +1714,7 @@ static int curve_select_similar_exec(bContext *C, wmOperator *op) const float thresh = RNA_float_get(op->ptr, "threshold"); const int compare = RNA_enum_get(op->ptr, "compare"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); int tot_nurbs_selected_all = 0; @@ -1908,10 +1907,10 @@ static void curve_select_shortest_path_curve(Nurb *nu, int vert_src, int vert_ds i = vert_src; while (true) { if (nu->type & CU_BEZIER) { - select_beztriple(&nu->bezt[i], SELECT, SELECT, HIDDEN); + select_beztriple(&nu->bezt[i], true, SELECT, HIDDEN); } else { - select_bpoint(&nu->bp[i], SELECT, SELECT, HIDDEN); + select_bpoint(&nu->bp[i], true, SELECT, HIDDEN); } if (i == vert_dst) { @@ -1989,10 +1988,10 @@ static void curve_select_shortest_path_surf(Nurb *nu, int vert_src, int vert_dst int i = 0; while (vert_curr != vert_src && i++ < vert_num) { if (nu->type == CU_BEZIER) { - select_beztriple(&nu->bezt[vert_curr], SELECT, SELECT, HIDDEN); + select_beztriple(&nu->bezt[vert_curr], true, SELECT, HIDDEN); } else { - select_bpoint(&nu->bp[vert_curr], SELECT, SELECT, HIDDEN); + select_bpoint(&nu->bp[vert_curr], true, SELECT, HIDDEN); } vert_curr = data[vert_curr].vert_prev; } diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index b2e2d2f1bee..54d91ccfc90 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -549,7 +549,7 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob, BKE_mesh_runtime_looptri_len(&surface_mesh)}; VArraySpan<float2> surface_uv_map; if (curves_id.surface_uv_map != nullptr) { - const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh); + const bke::AttributeAccessor surface_attributes = surface_mesh.attributes(); surface_uv_map = surface_attributes .lookup(curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2) .typed<float2>(); diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index eafe13f093d..14f2f8c6af5 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -282,7 +282,7 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) RNA_enum_get(op->ptr, "mode")); Mesh *mesh = reinterpret_cast<Mesh *>(ob_data); - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); /* General conversion steps are always the same: * 1. Convert old data to right domain and data type. @@ -646,8 +646,7 @@ bool ED_geometry_attribute_convert(Mesh *mesh, return false; } - blender::bke::MutableAttributeAccessor attributes = blender::bke::mesh_attributes_for_write( - *mesh); + blender::bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); GVArray src_varray = attributes.lookup_or_default(name, new_domain, new_type); diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 120c806c727..d53c0af2c54 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -67,6 +67,7 @@ #include "ED_armature.h" #include "ED_gpencil.h" +#include "ED_keyframing.h" #include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" @@ -1713,12 +1714,17 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op) } } - /* Ensure we have a frame to draw into + /* Ensure we have a frame to draw into. * NOTE: Since this is an op which creates strokes, - * we are obliged to add a new frame if one - * doesn't exist already + * we reuse active frame or add a new frame if one + * doesn't exist already depending on REC button status. */ - gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW); + if (IS_AUTOKEY_ON(scene) || (gpl->actframe == NULL)) { + gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW); + } + else { + gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV); + } if (gpf) { /* Create new stroke */ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true); @@ -3791,6 +3797,101 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Stroke Start Set Operator + * \{ */ + +static int gpencil_stroke_start_set_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ob->data; + + /* sanity checks */ + if (ELEM(NULL, ob, gpd)) { + return OPERATOR_CANCELLED; + } + + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + if (is_curve_edit) { + BKE_report(op->reports, RPT_ERROR, "Curve Edit mode not supported"); + return OPERATOR_CANCELLED; + } + + bool changed = false; + /* Read all selected strokes. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { + continue; + } + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + continue; + } + + /* Only cyclic strokes. */ + if ((gps->flag & GP_STROKE_CYCLIC) == 0) { + continue; + } + + /* Find first selected point and set start. */ + bGPDspoint *pt; + for (int i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + if (pt->flag & GP_SPOINT_SELECT) { + BKE_gpencil_stroke_start_set(gps, i); + BKE_gpencil_stroke_geometry_update(gpd, gps); + changed = true; + break; + } + } + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + + if (changed) { + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_start_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Start Point"; + ot->idname = "GPENCIL_OT_stroke_start_set"; + ot->description = "Set start point for cyclic strokes"; + + /* api callbacks */ + ot->exec = gpencil_stroke_start_set_exec; + ot->poll = gpencil_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Stroke Re-project Operator * \{ */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 3cb3a50e702..4d62f834d86 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -593,6 +593,7 @@ void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot); */ void GPENCIL_OT_stroke_caps_set(struct wmOperatorType *ot); void GPENCIL_OT_stroke_join(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_start_set(struct wmOperatorType *ot); void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot); void GPENCIL_OT_stroke_subdivide(struct wmOperatorType *ot); void GPENCIL_OT_stroke_simplify(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 3d92fbabfc4..85cc281ca90 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -621,6 +621,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_caps_set); WM_operatortype_append(GPENCIL_OT_stroke_join); WM_operatortype_append(GPENCIL_OT_stroke_flip); + WM_operatortype_append(GPENCIL_OT_stroke_start_set); WM_operatortype_append(GPENCIL_OT_stroke_subdivide); WM_operatortype_append(GPENCIL_OT_stroke_simplify); WM_operatortype_append(GPENCIL_OT_stroke_simplify_fixed); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index b6a652bd3ab..26743a2bd08 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -139,6 +139,7 @@ struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, const struct Scene *scene, bool uv_selected, bool use_winding, + bool use_seams, bool do_islands); void BM_uv_element_map_free(struct UvElementMap *element_map); struct UvElement *BM_uv_element_get(const struct UvElementMap *map, diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 88a81f8f9d9..3dffa2ba9fc 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -543,8 +543,8 @@ bool ED_object_modifier_move_to_index(struct ReportList *reports, bool ED_object_modifier_convert_psys_to_mesh(struct ReportList *reports, struct Main *bmain, - struct Scene *scene, struct Depsgraph *depsgraph, + struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob, struct ModifierData *md); diff --git a/source/blender/editors/include/ED_types.h b/source/blender/editors/include/ED_types.h deleted file mode 100644 index eba93ed6744..00000000000 --- a/source/blender/editors/include/ED_types.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2008 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup editors - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/* **************** GENERAL EDITOR-WIDE TYPES AND DEFINES ************************** */ - -/* old blender defines... should be deprecated? */ -#define DESELECT 0 -#define SELECT 1 -#define ACTIVE 2 - -/* proposal = put scene pointers on function calls? */ -// #define BASACT (scene->basact) -// #define OBACT (BASACT ? BASACT->object : NULL) - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 82bfdd7e212..6c756984203 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -13,7 +13,7 @@ #include "UI_resources.h" -namespace blender::nodes::geometry_nodes_eval_log { +namespace blender::nodes::geo_eval_log { struct GeometryAttributeInfo; } @@ -44,12 +44,11 @@ void context_path_add_generic(Vector<ContextPathItem> &path, void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path); -void attribute_search_add_items( - StringRefNull str, - bool can_create_attribute, - Span<const nodes::geometry_nodes_eval_log::GeometryAttributeInfo *> infos, - uiSearchItems *items, - bool is_first); +void attribute_search_add_items(StringRefNull str, + bool can_create_attribute, + Span<const nodes::geo_eval_log::GeometryAttributeInfo *> infos, + uiSearchItems *items, + bool is_first); } // namespace blender::ui diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index 0c7c3a238ec..4bf2dac4151 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -37,7 +37,7 @@ void UI_but_drag_set_asset(uiBut *but, { wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); - /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the + /* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the * #wmDropBox. * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its * copy callback. diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index cc53b638437..73ff678e658 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -1503,7 +1503,7 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll) } /* Find the containing Object. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Base *base = nullptr; const short id_type = GS(ptr.owner_id->name); diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index 0a684903f0f..55ca945671f 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -14,13 +14,15 @@ #include "BLT_translation.h" -#include "NOD_geometry_nodes_eval_log.hh" +#include "BKE_attribute.hh" + +#include "NOD_geometry_nodes_log.hh" #include "UI_interface.h" #include "UI_interface.hh" #include "UI_resources.h" -using blender::nodes::geometry_nodes_eval_log::GeometryAttributeInfo; +using blender::nodes::geo_eval_log::GeometryAttributeInfo; namespace blender::ui { diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c index 12d87113a66..662a372b608 100644 --- a/source/blender/editors/io/io_gpencil_export.c +++ b/source/blender/editors/io/io_gpencil_export.c @@ -217,7 +217,7 @@ void WM_OT_gpencil_export_svg(wmOperatorType *ot) FILE_SAVE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, - FILE_SORT_ALPHA); + FILE_SORT_DEFAULT); gpencil_export_common_props_definition(ot); @@ -375,7 +375,7 @@ void WM_OT_gpencil_export_pdf(wmOperatorType *ot) FILE_SAVE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, - FILE_SORT_ALPHA); + FILE_SORT_DEFAULT); static const EnumPropertyItem gpencil_export_frame_items[] = { {GP_EXPORT_FRAME_ACTIVE, "ACTIVE", 0, "Active", "Include only active frame"}, diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 0c935a0e1da..cb8eafeb52d 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -93,6 +93,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.path_mode = RNA_enum_get(op->ptr, "path_mode"); export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh"); export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs"); + export_params.export_pbr_extensions = RNA_boolean_get(op->ptr, "export_pbr_extensions"); export_params.export_object_groups = RNA_boolean_get(op->ptr, "export_object_groups"); export_params.export_material_groups = RNA_boolean_get(op->ptr, "export_material_groups"); @@ -114,51 +115,50 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - /* Animation options. */ - uiLayout *box = uiLayoutBox(layout); - uiItemL(box, IFACE_("Animation"), ICON_ANIM); - uiLayout *col = uiLayoutColumn(box, false); - uiLayout *sub = uiLayoutColumn(col, false); - uiItemR(sub, imfptr, "export_animation", 0, NULL, ICON_NONE); - sub = uiLayoutColumn(sub, true); - uiItemR(sub, imfptr, "start_frame", 0, IFACE_("Frame Start"), ICON_NONE); - uiItemR(sub, imfptr, "end_frame", 0, IFACE_("End"), ICON_NONE); - uiLayoutSetEnabled(sub, export_animation); + uiLayout *box, *col, *sub, *row; /* Object Transform options. */ box = uiLayoutBox(layout); - uiItemL(box, IFACE_("Object Properties"), ICON_OBJECT_DATA); col = uiLayoutColumn(box, false); - sub = uiLayoutColumn(col, false); - uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE); - uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE); - sub = uiLayoutColumn(col, false); + sub = uiLayoutColumnWithHeading(col, false, IFACE_("Limit to")); + uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE); uiItemR(sub, imfptr, "scaling_factor", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "forward_axis", UI_ITEM_R_EXPAND, IFACE_("Forward Axis"), ICON_NONE); + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "up_axis", UI_ITEM_R_EXPAND, IFACE_("Up Axis"), ICON_NONE); + + col = uiLayoutColumn(box, false); + sub = uiLayoutColumn(col, false); sub = uiLayoutColumnWithHeading(col, false, IFACE_("Objects")); - uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE); uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE); uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE); - sub = uiLayoutColumn(sub, false); - uiLayoutSetEnabled(sub, export_materials); - uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE); - /* Options for what to write. */ + /* Geometry options. */ box = uiLayoutBox(layout); - uiItemL(box, IFACE_("Geometry Export"), ICON_EXPORT); col = uiLayoutColumn(box, false); - sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export")); + sub = uiLayoutColumnWithHeading(col, false, IFACE_("Geometry")); uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE); uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE); uiItemR(sub, imfptr, "export_colors", 0, IFACE_("Colors"), ICON_NONE); - uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE); uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE); uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE); + /* Material options. */ + box = uiLayoutBox(layout); + col = uiLayoutColumn(box, false); + sub = uiLayoutColumnWithHeading(col, false, IFACE_("Materials")); + uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Export"), ICON_NONE); + sub = uiLayoutColumn(sub, false); + uiLayoutSetEnabled(sub, export_materials); + uiItemR(sub, imfptr, "export_pbr_extensions", 0, IFACE_("PBR Extensions"), ICON_NONE); + uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE); + /* Grouping options. */ box = uiLayoutBox(layout); - uiItemL(box, IFACE_("Grouping"), ICON_GROUP); col = uiLayoutColumn(box, false); - sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export")); + sub = uiLayoutColumnWithHeading(col, false, IFACE_("Grouping")); uiItemR(sub, imfptr, "export_object_groups", 0, IFACE_("Object Groups"), ICON_NONE); uiItemR(sub, imfptr, "export_material_groups", 0, IFACE_("Material Groups"), ICON_NONE); uiItemR(sub, imfptr, "export_vertex_groups", 0, IFACE_("Vertex Groups"), ICON_NONE); @@ -166,6 +166,16 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) sub = uiLayoutColumn(sub, false); uiLayoutSetEnabled(sub, export_smooth_groups); uiItemR(sub, imfptr, "smooth_group_bitflags", 0, IFACE_("Smooth Group Bitflags"), ICON_NONE); + + /* Animation options. */ + box = uiLayoutBox(layout); + col = uiLayoutColumn(box, false); + sub = uiLayoutColumnWithHeading(col, false, IFACE_("Animation")); + uiItemR(sub, imfptr, "export_animation", 0, IFACE_("Export"), ICON_NONE); + sub = uiLayoutColumn(sub, true); + uiLayoutSetEnabled(sub, export_animation); + uiItemR(sub, imfptr, "start_frame", 0, IFACE_("Frame Start"), ICON_NONE); + uiItemR(sub, imfptr, "end_frame", 0, IFACE_("End"), ICON_NONE); } static void wm_obj_export_draw(bContext *UNUSED(C), wmOperator *op) @@ -211,15 +221,30 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op) RNA_int_set(op->ptr, "start_frame", start); RNA_int_set(op->ptr, "end_frame", end); } + return changed; +} + +/* Both forward and up axes cannot be along the same direction. */ +static void forward_axis_update(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) +{ + int forward = RNA_enum_get(ptr, "forward_axis"); + int up = RNA_enum_get(ptr, "up_axis"); + if ((forward % 3) == (up % 3)) { + RNA_enum_set(ptr, "up_axis", (up + 1) % 6); + } +} - /* Both forward and up axes cannot be the same (or same except opposite sign). */ - if (RNA_enum_get(op->ptr, "forward_axis") % TOTAL_AXES == - (RNA_enum_get(op->ptr, "up_axis") % TOTAL_AXES)) { - /* TODO(@ankitm): Show a warning here. */ - RNA_enum_set(op->ptr, "up_axis", RNA_enum_get(op->ptr, "up_axis") % TOTAL_AXES + 1); - changed = true; +static void up_axis_update(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) +{ + int forward = RNA_enum_get(ptr, "forward_axis"); + int up = RNA_enum_get(ptr, "up_axis"); + if ((forward % 3) == (up % 3)) { + RNA_enum_set(ptr, "forward_axis", (forward + 1) % 6); } - return changed; } void WM_OT_obj_export(struct wmOperatorType *ot) @@ -244,7 +269,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) FILE_SAVE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, - FILE_SORT_ALPHA); + FILE_SORT_DEFAULT); /* Animation options. */ RNA_def_boolean(ot->srna, @@ -271,9 +296,11 @@ void WM_OT_obj_export(struct wmOperatorType *ot) INT_MIN, INT_MAX); /* Object transform options. */ - RNA_def_enum( + prop = RNA_def_enum( ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", ""); - RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", ""); + RNA_def_property_update_runtime(prop, (void *)forward_axis_update); + prop = RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", ""); + RNA_def_property_update_runtime(prop, (void *)up_axis_update); RNA_def_float(ot->srna, "scaling_factor", 1.0f, @@ -312,6 +339,12 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export Materials", "Export MTL library. There must be a Principled-BSDF node for image textures to " "be exported to the MTL file"); + RNA_def_boolean(ot->srna, + "export_pbr_extensions", + false, + "Export Materials with PBR Extensions", + "Export MTL library using PBR extensions (roughness, metallic, sheen, " + "clearcoat, anisotropy, transmission)"); RNA_def_enum(ot->srna, "path_mode", io_obj_path_mode, @@ -428,8 +461,11 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr) uiLayout *sub = uiLayoutColumn(col, false); uiItemR(sub, imfptr, "clamp_size", 0, NULL, ICON_NONE); sub = uiLayoutColumn(col, false); - uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE); - uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE); + + uiLayout *row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "forward_axis", UI_ITEM_R_EXPAND, IFACE_("Forward Axis"), ICON_NONE); + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "up_axis", UI_ITEM_R_EXPAND, IFACE_("Up Axis"), ICON_NONE); box = uiLayoutBox(layout); uiItemL(box, IFACE_("Options"), ICON_EXPORT); @@ -467,7 +503,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY, - FILE_SORT_ALPHA); + FILE_SORT_DEFAULT); RNA_def_float( ot->srna, "clamp_size", @@ -478,9 +514,11 @@ void WM_OT_obj_import(struct wmOperatorType *ot) "Resize the objects to keep bounding box under this value. Value 0 disables clamping", 0.0f, 1000.0f); - RNA_def_enum( + prop = RNA_def_enum( ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", ""); - RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", ""); + RNA_def_property_update_runtime(prop, (void *)forward_axis_update); + prop = RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", ""); + RNA_def_property_update_runtime(prop, (void *)up_axis_update); RNA_def_boolean(ot->srna, "import_vertex_groups", false, diff --git a/source/blender/editors/io/io_stl_ops.c b/source/blender/editors/io/io_stl_ops.c index 858ea131577..c98e5beaf3b 100644 --- a/source/blender/editors/io/io_stl_ops.c +++ b/source/blender/editors/io/io_stl_ops.c @@ -104,7 +104,7 @@ void WM_OT_stl_import(struct wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, - FILE_SORT_ALPHA); + FILE_SORT_DEFAULT); RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f); RNA_def_boolean(ot->srna, diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 74ce0cca16c..eb80cabcd7f 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -191,6 +191,19 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE); } +static void free_operator_customdata(wmOperator *op) +{ + if (op->customdata) { + MEM_freeN(op->customdata); + op->customdata = NULL; + } +} + +static void wm_usd_export_cancel(bContext *UNUSED(C), wmOperator *op) +{ + free_operator_customdata(op); +} + static bool wm_usd_export_check(bContext *UNUSED(C), wmOperator *op) { char filepath[FILE_MAX]; @@ -215,6 +228,7 @@ void WM_OT_usd_export(struct wmOperatorType *ot) ot->exec = wm_usd_export_exec; ot->poll = WM_operator_winactive; ot->ui = wm_usd_export_draw; + ot->cancel = wm_usd_export_cancel; ot->check = wm_usd_export_check; ot->flag = OPTYPE_REGISTER | OPTYPE_PRESET; /* No UNDO possible. */ @@ -360,7 +374,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) const bool create_collection = RNA_boolean_get(op->ptr, "create_collection"); - char *prim_path_mask = malloc(1024); + char prim_path_mask[1024]; RNA_string_get(op->ptr, "prim_path_mask", prim_path_mask); const bool import_guide = RNA_boolean_get(op->ptr, "import_guide"); @@ -402,7 +416,6 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) .import_materials = import_materials, .import_meshes = import_meshes, .import_volumes = import_volumes, - .prim_path_mask = prim_path_mask, .import_subdiv = import_subdiv, .import_instance_proxies = import_instance_proxies, .create_collection = create_collection, @@ -416,11 +429,18 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) .light_intensity_scale = light_intensity_scale, .mtl_name_collision_mode = mtl_name_collision_mode}; + STRNCPY(params.prim_path_mask, prim_path_mask); + const bool ok = USD_import(C, filename, ¶ms, as_background_job); return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +static void wm_usd_import_cancel(bContext *UNUSED(C), wmOperator *op) +{ + free_operator_customdata(op); +} + static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; @@ -476,6 +496,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot) ot->invoke = wm_usd_import_invoke; ot->exec = wm_usd_import_exec; + ot->cancel = wm_usd_import_cancel; ot->poll = WM_operator_winactive; ot->ui = wm_usd_import_draw; @@ -487,7 +508,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot) FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, - FILE_SORT_ALPHA); + FILE_SORT_DEFAULT); RNA_def_float( ot->srna, diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c index c85d9cfa43a..22a9d41fcf7 100644 --- a/source/blender/editors/lattice/editlattice_select.c +++ b/source/blender/editors/lattice/editlattice_select.c @@ -96,7 +96,7 @@ static int lattice_select_random_exec(bContext *C, wmOperator *op) const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -206,7 +206,7 @@ static int lattice_select_mirror_exec(bContext *C, wmOperator *op) const int axis_flag = RNA_enum_get(op->ptr, "axis"); const bool extend = RNA_boolean_get(op->ptr, "extend"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -273,7 +273,7 @@ static bool lattice_test_bitmap_uvw( static int lattice_select_more_less(bContext *C, const bool select) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; bool changed = false; @@ -399,7 +399,7 @@ bool ED_lattice_flags_set(Object *obedit, int flag) static int lattice_select_all_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); int action = RNA_enum_get(op->ptr, "action"); @@ -488,7 +488,7 @@ void LATTICE_OT_select_all(wmOperatorType *ot) static int lattice_select_ungrouped_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; const bool is_extend = RNA_boolean_get(op->ptr, "extend"); diff --git a/source/blender/editors/lattice/editlattice_tools.c b/source/blender/editors/lattice/editlattice_tools.c index 87f2ccf56dc..cee39ff7d70 100644 --- a/source/blender/editors/lattice/editlattice_tools.c +++ b/source/blender/editors/lattice/editlattice_tools.c @@ -49,7 +49,7 @@ static bool make_regular_poll(bContext *C) static int make_regular_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); const bool is_editmode = CTX_data_edit_object(C) != NULL; @@ -196,7 +196,7 @@ static void lattice_swap_point_pairs( static int lattice_flip_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; bool changed = false; diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c index 49dbf2e19e2..b77786b2421 100644 --- a/source/blender/editors/lattice/editlattice_undo.c +++ b/source/blender/editors/lattice/editlattice_undo.c @@ -175,7 +175,7 @@ static bool lattice_undosys_step_encode(struct bContext *C, Main *bmain, UndoSte /* Important not to use the 3D view when getting objects because all objects * outside of this list will be moved out of edit-mode when reading back undo steps. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len); diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index 26184acc8b4..f729db29b8c 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -67,11 +67,11 @@ void paintface_flush_flags(bContext *C, return; } - bke::AttributeAccessor attributes_me = bke::mesh_attributes(*me); + bke::AttributeAccessor attributes_me = me->attributes(); Mesh *me_orig = (Mesh *)ob_eval->runtime.data_orig; - bke::MutableAttributeAccessor attributes_orig = bke::mesh_attributes_for_write(*me_orig); + bke::MutableAttributeAccessor attributes_orig = me_orig->attributes_for_write(); Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval; - bke::MutableAttributeAccessor attributes_eval = bke::mesh_attributes_for_write(*me_eval); + bke::MutableAttributeAccessor attributes_eval = me_eval->attributes_for_write(); bool updated = false; const Span<MPoly> me_polys = me->polys(); @@ -142,7 +142,7 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected) } MutableSpan<MPoly> polys = me->polys_for_write(); - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>( ".hide_poly", ATTR_DOMAIN_FACE); @@ -175,7 +175,7 @@ void paintface_reveal(bContext *C, Object *ob, const bool select) } MutableSpan<MPoly> polys = me->polys_for_write(); - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); if (select) { const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( @@ -209,7 +209,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo const Span<MEdge> edges = me->edges(); MutableSpan<MPoly> polys = me->polys_for_write(); const Span<MLoop> loops = me->loops(); - bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + bke::AttributeAccessor attributes = me->attributes(); const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( ".hide_poly", ATTR_DOMAIN_FACE, false); @@ -306,7 +306,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl } MutableSpan<MPoly> polys = me->polys_for_write(); - bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + bke::AttributeAccessor attributes = me->attributes(); const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( ".hide_poly", ATTR_DOMAIN_FACE, false); @@ -372,7 +372,7 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) const Span<MVert> verts = me->verts(); const Span<MPoly> polys = me->polys(); const Span<MLoop> loops = me->loops(); - bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + bke::AttributeAccessor attributes = me->attributes(); const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( ".hide_poly", ATTR_DOMAIN_FACE, false); @@ -410,7 +410,7 @@ bool paintface_mouse_select(bContext *C, Mesh *me = BKE_mesh_from_object(ob); MutableSpan<MPoly> polys = me->polys_for_write(); - bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + bke::AttributeAccessor attributes = me->attributes(); const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( ".hide_poly", ATTR_DOMAIN_FACE, false); @@ -494,21 +494,21 @@ void paintvert_flush_flags(Object *ob) index_array = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); - const Span<MVert> vertices = me->verts_for_write(); - MutableSpan<MVert> vertices_eval = me_eval->verts_for_write(); + const Span<MVert> verts = me->verts_for_write(); + MutableSpan<MVert> verts_eval = me_eval->verts_for_write(); if (index_array) { int orig_index; - for (const int i : vertices_eval.index_range()) { + for (const int i : verts_eval.index_range()) { orig_index = index_array[i]; if (orig_index != ORIGINDEX_NONE) { - vertices_eval[i].flag = vertices[index_array[i]].flag; + verts_eval[i].flag = verts[index_array[i]].flag; } } } else { - for (const int i : vertices_eval.index_range()) { - vertices_eval[i].flag = vertices[i].flag; + for (const int i : verts_eval.index_range()) { + verts_eval[i].flag = verts[i].flag; } } @@ -530,7 +530,7 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) } MutableSpan<MVert> verts = me->verts_for_write(); - bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + bke::AttributeAccessor attributes = me->attributes(); const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( ".hide_vert", ATTR_DOMAIN_POINT, false); @@ -607,7 +607,7 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) } MutableSpan<MVert> verts = me->verts_for_write(); - bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + bke::AttributeAccessor attributes = me->attributes(); const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( ".hide_vert", ATTR_DOMAIN_POINT, false); @@ -636,7 +636,7 @@ void paintvert_hide(bContext *C, Object *ob, const bool unselected) } MutableSpan<MVert> verts = me->verts_for_write(); - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); bke::SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_span<bool>( ".hide_vert", ATTR_DOMAIN_POINT); @@ -669,7 +669,7 @@ void paintvert_reveal(bContext *C, Object *ob, const bool select) } MutableSpan<MVert> verts = me->verts_for_write(); - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( ".hide_vert", ATTR_DOMAIN_POINT, false); diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 4a6d1c15a37..5c5a12b3e64 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -99,7 +99,7 @@ static void mesh_bisect_interactive_calc(bContext *C, static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); int valid_objects = 0; diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 240681b945c..15e20d13e79 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -281,7 +281,7 @@ static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op) mul_v3_fl(offset, scale_offset); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -419,7 +419,7 @@ static bool edbm_extrude_mesh(Object *obedit, BMEditMesh *em, wmOperator *op) /* extrude without transform */ static int edbm_extrude_region_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -479,7 +479,7 @@ void MESH_OT_extrude_region(wmOperatorType *ot) /* extrude without transform */ static int edbm_extrude_context_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -534,7 +534,7 @@ void MESH_OT_extrude_context(wmOperatorType *ot) static int edbm_extrude_verts_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -588,7 +588,7 @@ void MESH_OT_extrude_verts_indiv(wmOperatorType *ot) static int edbm_extrude_edges_exec(bContext *C, wmOperator *op) { const bool use_normal_flip = RNA_boolean_get(op->ptr, "use_normal_flip"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -642,7 +642,7 @@ void MESH_OT_extrude_edges_indiv(wmOperatorType *ot) static int edbm_extrude_faces_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( diff --git a/source/blender/editors/mesh/editmesh_extrude_screw.c b/source/blender/editors/mesh/editmesh_extrude_screw.c index c22f888e90d..be2d04b14a1 100644 --- a/source/blender/editors/mesh/editmesh_extrude_screw.c +++ b/source/blender/editors/mesh/editmesh_extrude_screw.c @@ -41,7 +41,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op) int valence; uint objects_empty_len = 0; uint failed_axis_len = 0; - uint failed_vertices_len = 0; + uint failed_verts_len = 0; turns = RNA_int_get(op->ptr, "turns"); steps = RNA_int_get(op->ptr, "steps"); @@ -49,7 +49,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "axis", axis); uint objects_len = 0; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); @@ -98,7 +98,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op) } if (v1 == NULL || v2 == NULL) { - failed_vertices_len++; + failed_verts_len++; continue; } @@ -152,7 +152,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op) if (failed_axis_len == objects_len - objects_empty_len) { BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis"); } - else if (failed_vertices_len == objects_len - objects_empty_len) { + else if (failed_verts_len == objects_len - objects_empty_len) { BKE_report(op->reports, RPT_ERROR, "You have to select a string of connected vertices too"); } diff --git a/source/blender/editors/mesh/editmesh_extrude_spin.c b/source/blender/editors/mesh/editmesh_extrude_spin.c index d13cfd80d76..9e2b7aa7f4d 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin.c @@ -36,7 +36,7 @@ static int edbm_spin_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); float cent[3], axis[3]; const float d[3] = {0.0f, 0.0f, 0.0f}; diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 743c4656508..83cefd1c09d 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -180,7 +180,7 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) default: /* ISECT_SEPARATE_NONE */ break; } - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; uint isect_len = 0; @@ -351,7 +351,7 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) bool has_isect; test_fn = use_swap ? bm_face_isect_pair_swap : bm_face_isect_pair; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; uint isect_len = 0; @@ -817,7 +817,7 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op)) BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 1d05161a260..591e06be80c 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -376,7 +376,7 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event) .e_index = (uint)RNA_int_get(op->ptr, "edge_index"), }; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint bases_len; diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 6a080e78086..a4d41400bae 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -204,7 +204,7 @@ static int geometry_extract_apply(bContext *C, local_view_bits = v3d->local_view_uuid; } Object *new_ob = ED_object_add_type(C, OB_MESH, NULL, ob->loc, ob->rot, false, local_view_bits); - BKE_mesh_nomain_to_mesh(new_mesh, new_ob->data, new_ob, &CD_MASK_EVERYTHING, true); + BKE_mesh_nomain_to_mesh(new_mesh, new_ob->data, new_ob); /* Remove the Face Sets as they need to be recreated when entering Sculpt Mode in the new object. * TODO(pablodobarro): In the future we can try to preserve them from the original mesh. */ @@ -548,7 +548,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) /* Remove the mask from the new object so it can be sculpted directly after slicing. */ CustomData_free_layers(&new_ob_mesh->vdata, CD_PAINT_MASK, new_ob_mesh->totvert); - BKE_mesh_nomain_to_mesh(new_ob_mesh, new_ob->data, new_ob, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(new_ob_mesh, new_ob->data, new_ob); BKE_mesh_copy_parameters_for_eval(new_ob->data, mesh); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob); BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL); @@ -557,7 +557,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_DATA, new_ob->data); } - BKE_mesh_nomain_to_mesh(new_mesh, ob->data, ob, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(new_mesh, ob->data, ob); if (ob->mode == OB_MODE_SCULPT) { SculptSession *ss = ob->sculpt; diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index c2c07564c15..ec8c484d890 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -21,6 +21,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_report.h" @@ -348,7 +349,9 @@ static void edgetag_ensure_cd_flag(Mesh *me, const char edge_mode) BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE); break; case EDGE_MODE_TAG_BEVEL: - BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT); + if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { + BM_data_layer_add(bm, &bm->edata, CD_BWEIGHT); + } break; #ifdef WITH_FREESTYLE case EDGE_MODE_TAG_FREESTYLE: diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index e7d1854ae10..0c137c94d57 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -987,7 +987,7 @@ static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obed /* based on mouse cursor position, it defines how is being ripped */ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c index 56c447e4cd6..dd4b247a06f 100644 --- a/source/blender/editors/mesh/editmesh_rip_edge.c +++ b/source/blender/editors/mesh/editmesh_rip_edge.c @@ -35,7 +35,7 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve { ARegion *region = CTX_wm_region(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index ac3e845eceb..b66fe84e84e 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1515,7 +1515,7 @@ static void walker_select(BMEditMesh *em, int walkercode, void *start, const boo static int edbm_loop_multiselect_exec(bContext *C, wmOperator *op) { const bool is_ring = RNA_boolean_get(op->ptr, "ring"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1908,7 +1908,7 @@ void MESH_OT_edgering_select(wmOperatorType *ot) static int edbm_select_all_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); int action = RNA_enum_get(op->ptr, "action"); @@ -1977,7 +1977,7 @@ void MESH_OT_select_all(wmOperatorType *ot) static int edbm_faces_select_interior_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -3692,7 +3692,7 @@ static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op) BMElem *ele; { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); /* Intentionally wrap negative values so the lookup fails. */ const uint object_index = (uint)RNA_int_get(op->ptr, "object_index"); @@ -3764,7 +3764,7 @@ void MESH_OT_select_linked_pick(wmOperatorType *ot) static int edbm_select_face_by_sides_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; const bool extend = RNA_boolean_get(op->ptr, "extend"); @@ -3856,7 +3856,7 @@ void MESH_OT_select_face_by_sides(wmOperatorType *ot) static int edbm_select_loose_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool extend = RNA_boolean_get(op->ptr, "extend"); @@ -3947,7 +3947,7 @@ void MESH_OT_select_loose(wmOperatorType *ot) static int edbm_select_mirror_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int axis_flag = RNA_enum_get(op->ptr, "axis"); const bool extend = RNA_boolean_get(op->ptr, "extend"); @@ -4022,7 +4022,7 @@ void MESH_OT_select_mirror(wmOperatorType *ot) static int edbm_select_more_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step"); @@ -4073,7 +4073,7 @@ void MESH_OT_select_more(wmOperatorType *ot) static int edbm_select_less_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step"); @@ -4312,7 +4312,7 @@ static bool edbm_deselect_nth(BMEditMesh *em, const struct CheckerIntervalParams static int edbm_select_nth_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); struct CheckerIntervalParams op_params; WM_operator_properties_checker_interval_from_op(op, &op_params); @@ -4391,7 +4391,7 @@ static int edbm_select_sharp_edges_exec(bContext *C, wmOperator *op) */ const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness")); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -4468,7 +4468,7 @@ void MESH_OT_edges_select_sharp(wmOperatorType *ot) static int edbm_select_linked_flat_faces_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -4582,7 +4582,7 @@ static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op) const bool use_non_contiguous = RNA_boolean_get(op->ptr, "use_non_contiguous"); const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -4687,7 +4687,7 @@ static int edbm_select_random_exec(bContext *C, wmOperator *op) const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -4818,7 +4818,7 @@ static bool edbm_select_ungrouped_poll(bContext *C) static int edbm_select_ungrouped_exec(bContext *C, wmOperator *op) { const bool extend = RNA_boolean_get(op->ptr, "extend"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -5045,7 +5045,7 @@ void MESH_OT_select_axis(wmOperatorType *ot) static int edbm_region_to_loop_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5278,7 +5278,7 @@ static int edbm_loop_to_region_exec(bContext *C, wmOperator *op) { const bool select_bigger = RNA_boolean_get(op->ptr, "select_bigger"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index 84044914373..47c76b7709b 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -147,7 +147,7 @@ static void face_to_plane(const Object *ob, BMFace *face, float r_plane[4]) */ static int similar_face_select_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int type = RNA_enum_get(op->ptr, "type"); @@ -621,7 +621,7 @@ static bool edge_data_value_set(BMEdge *edge, const int hflag, int *r_value) */ static int similar_edge_select_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int type = RNA_enum_get(op->ptr, "type"); @@ -972,7 +972,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op) static int similar_vert_select_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); /* get the type from RNA */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 0c5ffb249d7..9f3ef8af17d 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -93,7 +93,7 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) const int quad_corner_type = RNA_enum_get(op->ptr, "quadcorner"); const int seed = RNA_int_get(op->ptr, "seed"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -290,7 +290,7 @@ static void mesh_operator_edgering_props_get(wmOperator *op, struct EdgeRingOpSu static int edbm_subdivide_edge_ring_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -358,8 +358,8 @@ void MESH_OT_subdivide_edgering(wmOperatorType *ot) static int edbm_unsubdivide_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); const int iterations = RNA_int_get(op->ptr, "iterations"); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode( @@ -446,7 +446,7 @@ static void edbm_report_delete_info(ReportList *reports, static int edbm_delete_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -590,7 +590,7 @@ static bool bm_face_is_loose(BMFace *f) static int edbm_delete_loose_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); int totelem_old_sel[3]; int totelem_old[3]; @@ -699,7 +699,7 @@ void MESH_OT_delete_loose(wmOperatorType *ot) static int edbm_collapse_edge_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -933,7 +933,7 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) { /* When this is used to dissolve we could avoid this, but checking isn't too slow. */ bool changed_multi = false; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1135,7 +1135,7 @@ static int edbm_mark_sharp_exec(bContext *C, wmOperator *op) BMIter iter; const bool clear = RNA_boolean_get(op->ptr, "clear"); const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -1319,7 +1319,7 @@ static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator * static int edbm_vert_connect_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; uint failed_objects_len = 0; @@ -1567,7 +1567,7 @@ static bool bm_vert_connect_select_history_edge_to_vert_path(BMesh *bm, ListBase static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; uint failed_selection_order_len = 0; @@ -1664,7 +1664,7 @@ void MESH_OT_vert_connect_path(wmOperatorType *ot) static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1716,7 +1716,7 @@ void MESH_OT_vert_connect_concave(wmOperatorType *ot) static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const float angle_limit = RNA_float_get(op->ptr, "angle_limit"); uint objects_len = 0; @@ -1791,7 +1791,7 @@ void MESH_OT_vert_connect_nonplanar(wmOperatorType *ot) static int edbm_face_make_planar_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -1959,7 +1959,7 @@ static int edbm_edge_split_exec(bContext *C, wmOperator *op) { const int type = RNA_enum_get(op->ptr, "type"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -2025,7 +2025,7 @@ void MESH_OT_edge_split(wmOperatorType *ot) static int edbm_duplicate_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -2257,7 +2257,7 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) { const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -2324,7 +2324,7 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op) int tot_failed_all = 0; bool no_selected_edges = true, invalid_selected_edges = true; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -2451,7 +2451,7 @@ void MESH_OT_edge_rotate(wmOperatorType *ot) static int edbm_hide_exec(bContext *C, wmOperator *op) { const bool unselected = RNA_boolean_get(op->ptr, "unselected"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); bool changed = false; @@ -2533,7 +2533,7 @@ void MESH_OT_hide(wmOperatorType *ot) static int edbm_reveal_exec(bContext *C, wmOperator *op) { const bool select = RNA_boolean_get(op->ptr, "select"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -2582,7 +2582,7 @@ void MESH_OT_reveal(wmOperatorType *ot) static int edbm_normals_make_consistent_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool inside = RNA_boolean_get(op->ptr, "inside"); @@ -2664,7 +2664,7 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) repeat = 1; } - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -2784,7 +2784,7 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) { int tot_unselected = 0; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const float lambda_factor = RNA_float_get(op->ptr, "lambda_factor"); @@ -2926,7 +2926,7 @@ static void mesh_set_smooth_faces(BMEditMesh *em, short smooth) static int edbm_faces_shade_smooth_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -2975,7 +2975,7 @@ void MESH_OT_faces_shade_smooth(wmOperatorType *ot) static int edbm_faces_shade_flat_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -3027,7 +3027,7 @@ static int edbm_rotate_uvs_exec(bContext *C, wmOperator *op) /* get the direction from RNA */ const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -3064,7 +3064,7 @@ static int edbm_rotate_uvs_exec(bContext *C, wmOperator *op) static int edbm_reverse_uvs_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -3103,7 +3103,7 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) /* get the direction from RNA */ const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -3157,7 +3157,7 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) static int edbm_reverse_colors_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -3567,7 +3567,7 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) int count_multi = 0; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -3714,7 +3714,7 @@ static bool shape_propagate(BMEditMesh *em) static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); int tot_shapekeys = 0; int tot_selected_verts_objects = 0; @@ -3788,7 +3788,7 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op) BMEditMesh *em_ref = me_ref->edit_mesh; BMVert *eve; BMIter iter; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); float co[3], *sco; int totshape_ref = 0; @@ -3968,7 +3968,7 @@ static int edbm_solidify_exec(bContext *C, wmOperator *op) { const float thickness = RNA_float_get(op->ptr, "thickness"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -4866,7 +4866,7 @@ static int edbm_fill_exec(bContext *C, wmOperator *op) bool has_selected_edges = false, has_faces_filled = false; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5122,7 +5122,7 @@ static int edbm_fill_grid_exec(bContext *C, wmOperator *op) { const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5264,7 +5264,7 @@ static int edbm_fill_holes_exec(bContext *C, wmOperator *op) { const int sides = RNA_int_get(op->ptr, "sides"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5328,7 +5328,7 @@ void MESH_OT_fill_holes(wmOperatorType *ot) static int edbm_beautify_fill_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5427,7 +5427,7 @@ static int edbm_poke_face_exec(bContext *C, wmOperator *op) const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset"); const int center_mode = RNA_enum_get(op->ptr, "center_mode"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5524,7 +5524,7 @@ static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op) { const int quad_method = RNA_enum_get(op->ptr, "quad_method"); const int ngon_method = RNA_enum_get(op->ptr, "ngon_method"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -5619,7 +5619,7 @@ void MESH_OT_quads_convert_to_tris(wmOperatorType *ot) static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -5781,7 +5781,7 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5999,7 +5999,7 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -6067,7 +6067,7 @@ static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op) const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -6133,7 +6133,7 @@ void MESH_OT_dissolve_edges(wmOperatorType *ot) static int edbm_dissolve_faces_exec(bContext *C, wmOperator *op) { const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -6250,7 +6250,7 @@ static int edbm_dissolve_limited_exec(bContext *C, wmOperator *op) const int delimit = RNA_enum_get(op->ptr, "delimit"); char dissolve_flag; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -6372,7 +6372,7 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot) static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); int totelem_old[3] = {0, 0, 0}; int totelem_new[3] = {0, 0, 0}; @@ -6457,7 +6457,7 @@ void MESH_OT_dissolve_degenerate(wmOperatorType *ot) static int edbm_delete_edgeloop_exec(bContext *C, wmOperator *op) { const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -6542,7 +6542,7 @@ void MESH_OT_delete_edgeloop(wmOperatorType *ot) static int edbm_split_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -7442,7 +7442,7 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) const bool use_merge = RNA_boolean_get(op->ptr, "use_merge"); const float merge_factor = RNA_float_get(op->ptr, "merge_factor"); const int twist_offset = RNA_int_get(op->ptr, "twist_offset"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -7523,7 +7523,7 @@ static int edbm_wireframe_exec(bContext *C, wmOperator *op) const float thickness = RNA_float_get(op->ptr, "thickness"); const float offset = RNA_float_get(op->ptr, "offset"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -7724,7 +7724,7 @@ static int edbm_convex_hull_exec(bContext *C, wmOperator *op) float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold"); float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -7858,7 +7858,7 @@ void MESH_OT_convex_hull(wmOperatorType *ot) static int mesh_symmetrize_exec(bContext *C, wmOperator *op) { const float thresh = RNA_float_get(op->ptr, "threshold"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -7958,7 +7958,7 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) int axis = axis_dir % 3; bool axis_sign = axis != axis_dir; -Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -8125,7 +8125,7 @@ static int edbm_mark_freestyle_edge_exec(bContext *C, wmOperator *op) BMIter iter; FreestyleEdge *fed; const bool clear = RNA_boolean_get(op->ptr, "clear"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -8206,7 +8206,7 @@ static int edbm_mark_freestyle_face_exec(bContext *C, wmOperator *op) BMIter iter; FreestyleFace *ffa; const bool clear = RNA_boolean_get(op->ptr, "clear"); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -9005,7 +9005,7 @@ static void normals_split(BMesh *bm) static int normals_split_merge(bContext *C, const bool do_merge) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -9135,7 +9135,7 @@ static EnumPropertyItem average_method_items[] = { static int edbm_average_normals_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -9602,7 +9602,7 @@ void MESH_OT_normals_tools(struct wmOperatorType *ot) static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -9718,7 +9718,7 @@ void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot) static int edbm_smooth_normals_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -9840,7 +9840,7 @@ void MESH_OT_smooth_normals(struct wmOperatorType *ot) static int edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index a50339c6ebe..44fab751de2 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -773,7 +773,7 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und /* Important not to use the 3D view when getting objects because all objects * outside of this list will be moved out of edit-mode when reading back undo steps. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); ToolSettings *ts = CTX_data_tool_settings(C); uint objects_len = 0; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 494e70ec9da..5c8ff930eb8 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -851,10 +851,99 @@ static void bm_uv_build_islands(UvElementMap *element_map, MEM_SAFE_FREE(map); } +/* return true if `loop` has UV co-ordinates which match `luv_a` and `luv_b` */ +static bool loop_uv_match(BMLoop *loop, MLoopUV *luv_a, MLoopUV *luv_b, int cd_loop_uv_offset) +{ + MLoopUV *luv_c = BM_ELEM_CD_GET_VOID_P(loop, cd_loop_uv_offset); + MLoopUV *luv_d = BM_ELEM_CD_GET_VOID_P(loop->next, cd_loop_uv_offset); + return compare_v2v2(luv_a->uv, luv_c->uv, STD_UV_CONNECT_LIMIT) && + compare_v2v2(luv_b->uv, luv_d->uv, STD_UV_CONNECT_LIMIT); +} + +/* Given `anchor` and `edge`, return true if there are edges that fan between them that are + * seam-free. */ +static bool seam_connected_recursive(BMVert *anchor, + BMEdge *edge, + MLoopUV *luv_anchor, + MLoopUV *luv_fan, + BMLoop *needle, + GSet *visited, + int cd_loop_uv_offset) +{ + BLI_assert(edge->v1 == anchor || edge->v2 == anchor); + BLI_assert(needle->v == anchor || needle->next->v == anchor); + + if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) { + return false; /* Edge is a seam, don't traverse. */ + } + + if (!BLI_gset_add(visited, edge)) { + return false; /* Already visited. */ + } + + BMLoop *loop; + BMIter liter; + BM_ITER_ELEM (loop, &liter, edge, BM_LOOPS_OF_EDGE) { + if (loop->v == anchor) { + if (!loop_uv_match(loop, luv_anchor, luv_fan, cd_loop_uv_offset)) { + continue; /* `loop` is disjoint in UV space. */ + } + + if (loop->prev == needle) { + return true; /* Success. */ + } + + MLoopUV *luv_far = BM_ELEM_CD_GET_VOID_P(loop->prev, cd_loop_uv_offset); + if (seam_connected_recursive( + anchor, loop->prev->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset)) { + return true; + } + } + else { + BLI_assert(loop->next->v == anchor); + if (!loop_uv_match(loop, luv_fan, luv_anchor, cd_loop_uv_offset)) { + continue; /* `loop` is disjoint in UV space. */ + } + + if (loop->next == needle) { + return true; /* Success. */ + } + + MLoopUV *luv_far = BM_ELEM_CD_GET_VOID_P(loop->next->next, cd_loop_uv_offset); + if (seam_connected_recursive( + anchor, loop->next->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset)) { + return true; + } + } + } + + return false; +} + +/* Given `loop_a` and `loop_b` originate from the same vertex and share a UV, + * return true if there are edges that fan between them that are seam-free. + * return false otherwise. + */ +static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset) +{ + BLI_assert(loop_a && loop_b); + BLI_assert(loop_a != loop_b); + BLI_assert(loop_a->v == loop_b->v); + + BLI_gset_clear(visited, NULL); + + MLoopUV *luv_anchor = BM_ELEM_CD_GET_VOID_P(loop_a, cd_loop_uv_offset); + MLoopUV *luv_fan = BM_ELEM_CD_GET_VOID_P(loop_a->next, cd_loop_uv_offset); + const bool result = seam_connected_recursive( + loop_a->v, loop_a->e, luv_anchor, luv_fan, loop_b, visited, cd_loop_uv_offset); + return result; +} + UvElementMap *BM_uv_element_map_create(BMesh *bm, const Scene *scene, const bool uv_selected, const bool use_winding, + const bool use_seams, const bool do_islands) { /* In uv sync selection, all UVs are visible. */ @@ -956,6 +1045,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, } BLI_buffer_free(&tf_uv_buf); + GSet *seam_visited_gset = use_seams ? BLI_gset_ptr_new(__func__) : NULL; + /* For each BMVert, sort associated linked list into unique uvs. */ int ev_index; BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) { @@ -1001,6 +1092,10 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, winding[BM_elem_index_get(v->l->f)]; } + if (connected && use_seams) { + connected = seam_connected(iterv->l, v->l, seam_visited_gset, cd_loop_uv_offset); + } + if (connected) { if (lastv) { lastv->next = next; @@ -1026,6 +1121,10 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, element_map->vertex[ev_index] = newvlist; } + if (seam_visited_gset) { + BLI_gset_free(seam_visited_gset, NULL); + seam_visited_gset = NULL; + } MEM_SAFE_FREE(winding); /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 4ee518b5662..e362501d86c 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -626,6 +626,28 @@ static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int typ return OPERATOR_CANCELLED; } +static int mesh_customdata_add_exec__internal(bContext *C, char htype, int type) +{ + Mesh *mesh = ED_mesh_context(C); + + int tot; + CustomData *data = mesh_customdata_get_type(mesh, htype, &tot); + + BLI_assert(CustomData_layertype_is_singleton(type) == true); + + if (mesh->edit_mesh) { + BM_data_layer_add(mesh->edit_mesh->bm, data, type); + } + else { + CustomData_add_layer(data, type, CD_SET_DEFAULT, NULL, tot); + } + + DEG_id_tag_update(&mesh->id, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); + + return CustomData_has_layer(data, type) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + /* Clear Mask */ static bool mesh_customdata_mask_clear_poll(bContext *C) { @@ -848,6 +870,126 @@ void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* Vertex bevel weight. */ + +static int mesh_customdata_bevel_weight_vertex_state(bContext *C) +{ + const Object *object = ED_object_context(C); + + if (object && object->type == OB_MESH) { + const Mesh *mesh = static_cast<Mesh *>(object->data); + if (!ID_IS_LINKED(mesh)) { + const CustomData *data = GET_CD_DATA(mesh, vdata); + return CustomData_has_layer(data, CD_BWEIGHT); + } + } + return -1; +} + +static bool mesh_customdata_bevel_weight_vertex_add_poll(bContext *C) +{ + return mesh_customdata_bevel_weight_vertex_state(C) == 0; +} + +static int mesh_customdata_bevel_weight_vertex_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return mesh_customdata_add_exec__internal(C, BM_VERT, CD_BWEIGHT); +} + +void MESH_OT_customdata_bevel_weight_vertex_add(wmOperatorType *ot) +{ + ot->name = "Add Vertex Bevel Weight"; + ot->idname = "MESH_OT_customdata_bevel_weight_vertex_add"; + ot->description = "Add a vertex bevel weight layer"; + + ot->exec = mesh_customdata_bevel_weight_vertex_add_exec; + ot->poll = mesh_customdata_bevel_weight_vertex_add_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static bool mesh_customdata_bevel_weight_vertex_clear_poll(bContext *C) +{ + return (mesh_customdata_bevel_weight_vertex_state(C) == 1); +} + +static int mesh_customdata_bevel_weight_vertex_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return mesh_customdata_clear_exec__internal(C, BM_VERT, CD_BWEIGHT); +} + +void MESH_OT_customdata_bevel_weight_vertex_clear(wmOperatorType *ot) +{ + ot->name = "Clear Vertex Bevel Weight"; + ot->idname = "MESH_OT_customdata_bevel_weight_vertex_clear"; + ot->description = "Clear the vertex bevel weight layer"; + + ot->exec = mesh_customdata_bevel_weight_vertex_clear_exec; + ot->poll = mesh_customdata_bevel_weight_vertex_clear_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* Edge bevel weight. */ + +static int mesh_customdata_bevel_weight_edge_state(bContext *C) +{ + const Object *ob = ED_object_context(C); + + if (ob && ob->type == OB_MESH) { + const Mesh *mesh = static_cast<Mesh *>(ob->data); + if (!ID_IS_LINKED(mesh)) { + const CustomData *data = GET_CD_DATA(mesh, edata); + return CustomData_has_layer(data, CD_BWEIGHT); + } + } + return -1; +} + +static bool mesh_customdata_bevel_weight_edge_add_poll(bContext *C) +{ + return mesh_customdata_bevel_weight_edge_state(C) == 0; +} + +static int mesh_customdata_bevel_weight_edge_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return mesh_customdata_add_exec__internal(C, BM_EDGE, CD_BWEIGHT); +} + +void MESH_OT_customdata_bevel_weight_edge_add(wmOperatorType *ot) +{ + ot->name = "Add Edge Bevel Weight"; + ot->idname = "MESH_OT_customdata_bevel_weight_edge_add"; + ot->description = "Add an edge bevel weight layer"; + + ot->exec = mesh_customdata_bevel_weight_edge_add_exec; + ot->poll = mesh_customdata_bevel_weight_edge_add_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static bool mesh_customdata_bevel_weight_edge_clear_poll(bContext *C) +{ + return mesh_customdata_bevel_weight_edge_state(C) == 1; +} + +static int mesh_customdata_bevel_weight_edge_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return mesh_customdata_clear_exec__internal(C, BM_EDGE, CD_BWEIGHT); +} + +void MESH_OT_customdata_bevel_weight_edge_clear(wmOperatorType *ot) +{ + ot->name = "Clear Edge Bevel Weight"; + ot->idname = "MESH_OT_customdata_bevel_weight_edge_clear"; + ot->description = "Clear the edge bevel weight layer"; + + ot->exec = mesh_customdata_bevel_weight_edge_clear_exec; + ot->poll = mesh_customdata_bevel_weight_edge_clear_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /************************** Add Geometry Layers *************************/ void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose) diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 4cf6f3d0eaa..75f63ed5d6f 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -315,6 +315,10 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); +void MESH_OT_customdata_bevel_weight_vertex_add(struct wmOperatorType *ot); +void MESH_OT_customdata_bevel_weight_vertex_clear(struct wmOperatorType *ot); +void MESH_OT_customdata_bevel_weight_edge_add(struct wmOperatorType *ot); +void MESH_OT_customdata_bevel_weight_edge_clear(struct wmOperatorType *ot); #ifdef __cplusplus } diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index b9e78740e3c..01c92a59fc9 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -139,6 +139,10 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_customdata_skin_clear); WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_add); WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_clear); + WM_operatortype_append(MESH_OT_customdata_bevel_weight_vertex_add); + WM_operatortype_append(MESH_OT_customdata_bevel_weight_vertex_clear); + WM_operatortype_append(MESH_OT_customdata_bevel_weight_edge_add); + WM_operatortype_append(MESH_OT_customdata_bevel_weight_edge_clear); WM_operatortype_append(MESH_OT_edgering_select); WM_operatortype_append(MESH_OT_loopcut); diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index ad7f504c87b..108fa210075 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -253,15 +253,18 @@ static void join_mesh_single(Depsgraph *depsgraph, CustomData_merge(&me->pdata, pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly); CustomData_copy_data_named(&me->pdata, pdata, 0, *polyofs, me->totpoly); - blender::bke::AttributeWriter<int> material_indices = - blender::bke::mesh_attributes_for_write(*me).lookup_for_write<int>("material_index"); + /* Apply matmap. In case we don't have material indices yet, create them if more than one + * material is the result of joining. */ + int *material_indices = static_cast<int *>( + CustomData_get_layer_named(pdata, CD_PROP_INT32, "material_index")); + if (!material_indices && totcol > 1) { + material_indices = (int *)CustomData_add_layer_named( + pdata, CD_PROP_INT32, CD_SET_DEFAULT, NULL, totpoly, "material_index"); + } if (material_indices) { - blender::MutableVArraySpan<int> material_indices_span(material_indices.varray); - for (const int i : material_indices_span.index_range()) { - material_indices_span[i] = matmap ? matmap[material_indices_span[i]] : 0; + for (a = 0; a < me->totpoly; a++) { + material_indices[a + *polyofs] = matmap ? matmap[material_indices[a + *polyofs]] : 0; } - material_indices_span.save(); - material_indices.finish(); } for (a = 0; a < me->totpoly; a++, mpoly++) { @@ -344,7 +347,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) int totloop = 0, totpoly = 0, vertofs, *matmap = nullptr; int i, haskey = 0, edgeofs, loopofs, polyofs; bool ok = false, join_parent = false; - CustomData vdata, edata, fdata, ldata, pdata; + CustomData vdata, edata, ldata, pdata; if (ob->mode & OB_MODE_EDIT) { BKE_report(op->reports, RPT_WARNING, "Cannot join while in edit mode"); @@ -583,7 +586,6 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* setup new data for destination mesh */ CustomData_reset(&vdata); CustomData_reset(&edata); - CustomData_reset(&fdata); CustomData_reset(&ldata); CustomData_reset(&pdata); diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c index 63a8e77a880..0f9049ad70c 100644 --- a/source/blender/editors/metaball/editmball_undo.c +++ b/source/blender/editors/metaball/editmball_undo.c @@ -152,7 +152,7 @@ static bool mball_undosys_step_encode(struct bContext *C, struct Main *bmain, Un /* Important not to use the 3D view when getting objects because all objects * outside of this list will be moved out of edit-mode when reading back undo steps. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len); diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 940938bb715..9515306a26c 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -144,7 +144,8 @@ MetaElem *ED_mball_add_primitive( static int mball_select_all_exec(bContext *C, wmOperator *op) { int action = RNA_enum_get(op->ptr, "action"); - Scene *scene = CTX_data_scene(C); + + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( @@ -330,7 +331,7 @@ static int mball_select_similar_exec(bContext *C, wmOperator *op) const float thresh = RNA_float_get(op->ptr, "threshold"); int tot_mball_selected_all = 0; - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( @@ -464,7 +465,7 @@ static int select_random_metaelems_exec(bContext *C, wmOperator *op) const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -531,7 +532,7 @@ void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot) /* Duplicate selected MetaElements */ static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -589,7 +590,7 @@ void MBALL_OT_duplicate_metaelems(wmOperatorType *ot) static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 3c984e4a1bb..1b9e84967ef 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -3157,14 +3157,14 @@ static int object_convert_exec(bContext *C, wmOperator *op) BKE_mesh_edges_set_draw_render(me_eval); BKE_object_material_from_eval_data(bmain, newob, &me_eval->id); Mesh *new_mesh = (Mesh *)newob->data; - BKE_mesh_nomain_to_mesh(me_eval, new_mesh, newob, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(me_eval, new_mesh, newob); if (do_merge_customdata) { BKE_mesh_merge_customdata_for_apply_modifier(new_mesh); } /* Anonymous attributes shouldn't be available on the applied geometry. */ - blender::bke::mesh_attributes_for_write(*new_mesh).remove_anonymous(); + new_mesh->attributes_for_write().remove_anonymous(); BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */ } @@ -3583,7 +3583,7 @@ static Base *object_add_duplicate_internal(Main *bmain, DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); BKE_view_layer_synced_ensure(scene, view_layer); base = BKE_view_layer_base_find(view_layer, ob); - if ((base != nullptr) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { + if ((base != nullptr) && (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT)) { BKE_collection_object_add_from(bmain, scene, ob, obn); } else { @@ -3822,8 +3822,8 @@ static int object_add_named_exec(bContext *C, wmOperator *op) /* Do immediately, as #copy_object_set_idnew() below operates on visible objects. */ BKE_base_eval_flags(basen); - /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() - * or BKE_view_layer_base_deselect_all(). */ + /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or + * BKE_view_layer_base_deselect_all(). */ ED_object_base_deselect_all(scene, view_layer, nullptr, SEL_DESELECT); ED_object_base_select(basen, BA_SELECT); ED_object_base_activate(C, basen); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 9876a250170..bdaa3523402 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -1412,7 +1412,8 @@ static int bake(const BakeAPIRender *bkr, else { ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); ob_cage_eval->visibility_flag |= OB_HIDE_RENDER; - ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); + ob_cage_eval->base_flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | + BASE_ENABLED_RENDER); } } } @@ -1512,7 +1513,8 @@ static int bake(const BakeAPIRender *bkr, highpoly[i].ob = ob_iter; highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); highpoly[i].ob_eval->visibility_flag &= ~OB_HIDE_RENDER; - highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); + highpoly[i].ob_eval->base_flag |= (BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | + BASE_ENABLED_RENDER); highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false); /* Low-poly to high-poly transformation matrix. */ @@ -1528,10 +1530,11 @@ static int bake(const BakeAPIRender *bkr, if (ob_cage != NULL) { ob_cage_eval->visibility_flag |= OB_HIDE_RENDER; - ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); + ob_cage_eval->base_flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | + BASE_ENABLED_RENDER); } ob_low_eval->visibility_flag |= OB_HIDE_RENDER; - ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); + ob_low_eval->base_flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_ENABLED_RENDER); /* populate the pixel arrays with the corresponding face data for each high poly object */ pixel_array_high = MEM_mallocN(sizeof(BakePixel) * targets.pixels_num, diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 39c8a3c4c7b..c3482b13db6 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -140,7 +140,7 @@ Object **ED_object_array_in_mode_or_selected(bContext *C, uint *r_objects_len) { ScrArea *area = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); BKE_view_layer_synced_ensure(scene, view_layer); Object *ob_active = BKE_view_layer_active_object_get(view_layer); @@ -291,7 +291,7 @@ static int object_hide_view_set_exec(bContext *C, wmOperator *op) /* Hide selected or unselected objects. */ BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { - if (!(base->flag & BASE_VISIBLE_VIEWLAYER)) { + if (!(base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT)) { continue; } diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 5ccaf8ac109..38ebae6ba83 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -524,8 +524,8 @@ void ED_object_modifier_copy_to_object(bContext *C, bool ED_object_modifier_convert_psys_to_mesh(ReportList *UNUSED(reports), Main *bmain, - Scene *scene, Depsgraph *depsgraph, + Scene *scene, ViewLayer *view_layer, Object *ob, ModifierData *md) @@ -764,10 +764,10 @@ static bool modifier_apply_obdata( Main *bmain = DEG_get_bmain(depsgraph); BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id); - BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(mesh_applied, me, ob); /* Anonymous attributes shouldn't be available on the applied geometry. */ - blender::bke::mesh_attributes_for_write(*me).remove_anonymous(); + me->attributes_for_write().remove_anonymous(); if (md_eval->type == eModifierType_Multires) { multires_customdata_delete(me); @@ -1629,7 +1629,7 @@ static int modifier_convert_exec(bContext *C, wmOperator *op) ModifierData *md = edit_modifier_property_get(op, ob, 0); if (!md || !ED_object_modifier_convert_psys_to_mesh( - op->reports, bmain, scene, depsgraph, view_layer, ob, md)) { + op->reports, bmain, depsgraph, scene, view_layer, ob, md)) { return OPERATOR_CANCELLED; } @@ -2639,10 +2639,7 @@ static void skin_armature_bone_create(Object *skin_ob, } } -static Object *modifier_skin_armature_create(Depsgraph *depsgraph, - Main *bmain, - Scene *scene, - Object *skin_ob) +static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, Object *skin_ob) { Mesh *me = static_cast<Mesh *>(skin_ob->data); const Span<MVert> me_verts = me->verts(); @@ -2658,6 +2655,7 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, /* add vertex weights to original mesh */ CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_SET_DEFAULT, nullptr, me->totvert); + Scene *scene = DEG_get_input_scene(depsgraph); ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); Object *arm_ob = BKE_object_add(bmain, scene, view_layer, OB_ARMATURE, nullptr); BKE_object_transform_copy(arm_ob, skin_ob); @@ -2714,7 +2712,6 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, static int skin_armature_create_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob = CTX_data_active_object(C); Mesh *me = static_cast<Mesh *>(ob->data); @@ -2726,7 +2723,7 @@ static int skin_armature_create_exec(bContext *C, wmOperator *op) } /* create new armature */ - Object *arm_ob = modifier_skin_armature_create(depsgraph, bmain, scene, ob); + Object *arm_ob = modifier_skin_armature_create(depsgraph, bmain, ob); /* add a modifier to connect the new armature to the mesh */ ArmatureModifierData *arm_md = (ArmatureModifierData *)BKE_modifier_new(eModifierType_Armature); @@ -3404,6 +3401,7 @@ static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(o nmd->node_group = new_tree; id_us_min(&tree->id); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_random.c b/source/blender/editors/object/object_random.c index ba26443e482..3117cbb0166 100644 --- a/source/blender/editors/object/object_random.c +++ b/source/blender/editors/object/object_random.c @@ -78,7 +78,7 @@ static bool object_rand_transverts(TransVertStore *tvs, static int object_rand_verts_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob_active = CTX_data_edit_object(C); const int ob_mode = ob_active->mode; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 0b59b4e869f..4a523997473 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2155,7 +2155,7 @@ static int make_local_exec(bContext *C, wmOperator *op) /* NOTE: we (ab)use LIB_TAG_PRE_EXISTING to cherry pick which ID to make local... */ if (mode == MAKE_LOCAL_ALL) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Collection *collection = CTX_data_collection(C); diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index c93d7dc3ee5..aa8dc4debd9 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -131,8 +131,8 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) } /* Output mesh will be all smooth or all flat shading. */ - const Span<MPoly> polygons = mesh->polys(); - const bool smooth_normals = polygons.first().flag & ME_SMOOTH; + const Span<MPoly> polys = mesh->polys(); + const bool smooth_normals = polys.first().flag & ME_SMOOTH; float isovalue = 0.0f; if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) { @@ -179,14 +179,14 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) BKE_remesh_reproject_vertex_paint(new_mesh, mesh); } - BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob); if (smooth_normals) { BKE_mesh_smooth_flag_set(static_cast<Mesh *>(ob->data), true); } if (ob->mode == OB_MODE_SCULPT) { - BKE_sculpt_ensure_orig_mesh_data(CTX_data_scene(C), ob); + BKE_sculpt_ensure_orig_mesh_data(ob); ED_sculpt_undo_geometry_end(ob); } @@ -905,14 +905,14 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update BKE_mesh_remesh_reproject_paint_mask(new_mesh, mesh); } - BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob); if (qj->smooth_normals) { BKE_mesh_smooth_flag_set(static_cast<Mesh *>(ob->data), true); } if (ob->mode == OB_MODE_SCULPT) { - BKE_sculpt_ensure_orig_mesh_data(qj->scene, ob); + BKE_sculpt_ensure_orig_mesh_data(ob); ED_sculpt_undo_geometry_end(ob); } diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index 4a7c7c69828..43867877fdb 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -205,7 +205,7 @@ bool ED_object_base_deselect_all(const Scene *scene, static int get_base_select_priority(Base *base) { - if (base->flag & BASE_VISIBLE_DEPSGRAPH) { + if (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) { if (base->flag & BASE_SELECTABLE) { return 3; } @@ -250,7 +250,7 @@ Base *ED_object_find_first_by_data_id(const Scene *scene, ViewLayer *view_layer, bool ED_object_jump_to_object(bContext *C, Object *ob, const bool UNUSED(reveal_hidden)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); BKE_view_layer_synced_ensure(scene, view_layer); diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index d2cb7ad4b43..1d1263494c7 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -277,7 +277,7 @@ void ED_vgroup_parray_mirror_sync(Object *ob, } int flip_map_len; - const int *flip_map = BKE_object_defgroup_flip_map(ob, &flip_map_len, true); + const int *flip_map = BKE_object_defgroup_flip_map(ob, true, &flip_map_len); for (int i_src = 0; i_src < dvert_tot; i_src++) { if (dvert_array[i_src] != nullptr) { @@ -506,7 +506,7 @@ static void mesh_defvert_mirror_update_internal(Object *ob, if (def_nr == -1) { /* All vgroups, add groups where needed. */ int flip_map_len; - int *flip_map = BKE_object_defgroup_flip_map(ob, &flip_map_len, true); + int *flip_map = BKE_object_defgroup_flip_map_unlocked(ob, true, &flip_map_len); BKE_defvert_sync_mapped(dvert_dst, dvert_src, flip_map, flip_map_len, true); MEM_freeN(flip_map); } @@ -2392,8 +2392,8 @@ void ED_vgroup_mirror(Object *ob, } if (flip_vgroups) { - flip_map = all_vgroups ? BKE_object_defgroup_flip_map(ob, &flip_map_len, false) : - BKE_object_defgroup_flip_map_single(ob, &flip_map_len, false, def_nr); + flip_map = all_vgroups ? BKE_object_defgroup_flip_map(ob, false, &flip_map_len) : + BKE_object_defgroup_flip_map_single(ob, false, def_nr, &flip_map_len); BLI_assert(flip_map != nullptr); diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc index 8560de428e9..7f6a14126e0 100644 --- a/source/blender/editors/render/render_internal.cc +++ b/source/blender/editors/render/render_internal.cc @@ -857,7 +857,7 @@ static void screen_render_cancel(bContext *C, wmOperator *op) static void clean_viewport_memory_base(Base *base) { - if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) { + if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0) { return; } diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 0024a09ef12..10de7063bbc 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -551,7 +551,7 @@ static Scene *preview_prepare_scene( } } else if (base->object->type == OB_LAMP) { - base->flag |= BASE_VISIBLE_DEPSGRAPH; + base->flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT; } } } @@ -1308,41 +1308,33 @@ static void shader_preview_free(void *customdata) static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) { - static const int flags = IB_rect | IB_multilayer | IB_metadata; + if (!brush->icon_imbuf && (brush->flag & BRUSH_CUSTOM_ICON) && brush->icon_filepath[0]) { + const int flags = IB_rect | IB_multilayer | IB_metadata; - char filepath[FILE_MAX]; - const char *folder; + /* First use the path directly to try and load the file. */ + char filepath[FILE_MAX]; - if (!(brush->icon_imbuf)) { - if (brush->flag & BRUSH_CUSTOM_ICON) { + BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); - if (brush->icon_filepath[0]) { - /* First use the path directly to try and load the file. */ + /* Use default color-spaces for brushes. */ + brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); - BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath)); - BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); + /* Otherwise lets try to find it in other directories. */ + if (!(brush->icon_imbuf)) { + const char *brushicons_dir = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons"); + /* Expected to be found, but don't crash if it's not. */ + if (brushicons_dir) { + BLI_join_dirfile(filepath, sizeof(filepath), brushicons_dir, brush->icon_filepath); - /* Use default color-spaces for brushes. */ + /* Use default color spaces. */ brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); - - /* otherwise lets try to find it in other directories */ - if (!(brush->icon_imbuf)) { - folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons"); - - BLI_make_file_string( - BKE_main_blendfile_path_from_global(), filepath, folder, brush->icon_filepath); - - if (filepath[0]) { - /* Use default color spaces. */ - brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); - } - } - - if (brush->icon_imbuf) { - BKE_icon_changed(BKE_icon_id_ensure(&brush->id)); - } } } + + if (brush->icon_imbuf) { + BKE_icon_changed(BKE_icon_id_ensure(&brush->id)); + } } if (!(brush->icon_imbuf)) { diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 62fd3a0af70..692a4a66937 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -16,6 +16,7 @@ #include "BLI_linklist_stack.h" #include "BLI_math.h" #include "BLI_rand.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -1902,8 +1903,8 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) { WorkSpace *workspace = WM_window_get_active_workspace(win); const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Scene *scene = WM_window_get_active_scene(win); if (ED_area_is_global(area) && (area->global->flag & GLOBAL_AREA_IS_HIDDEN)) { return; @@ -2689,12 +2690,13 @@ static void ed_panel_draw(const bContext *C, const uiStyle *style = UI_style_get_dpi(); /* Draw panel. */ - char block_name[BKE_ST_MAXNAME + INSTANCED_PANEL_UNIQUE_STR_LEN]; - strncpy(block_name, pt->idname, BKE_ST_MAXNAME); - if (unique_panel_str != NULL) { + if (unique_panel_str) { /* Instanced panels should have already been added at this point. */ - strncat(block_name, unique_panel_str, INSTANCED_PANEL_UNIQUE_STR_LEN); + BLI_string_join(block_name, sizeof(block_name), pt->idname, unique_panel_str); + } + else { + STRNCPY(block_name, pt->idname); } uiBlock *block = UI_block_begin(C, region, block_name, UI_EMBOSS); diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 88684d3d4a4..ffd76e70eb8 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -211,7 +211,7 @@ static eContextResult screen_ctx_objects_in_mode(const bContext *C, bContextData { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); BKE_view_layer_synced_ensure(scene, view_layer); Object *obact = BKE_view_layer_active_object_get(view_layer); @@ -230,7 +230,7 @@ static eContextResult screen_ctx_objects_in_mode_unique_data(const bContext *C, { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ - Scene *scene = CTX_data_scene(C); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); BKE_view_layer_synced_ensure(scene, view_layer); Object *obact = BKE_view_layer_active_object_get(view_layer); @@ -256,7 +256,7 @@ static eContextResult screen_ctx_visible_or_editable_bones_(const bContext *C, const bool editable_bones) { wmWindow *win = CTX_wm_window(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); BKE_view_layer_synced_ensure(scene, view_layer); Object *obedit = BKE_view_layer_edit_object_get(view_layer); @@ -329,7 +329,7 @@ static eContextResult screen_ctx_selected_bones_(const bContext *C, const bool selected_editable_bones) { wmWindow *win = CTX_wm_window(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); BKE_view_layer_synced_ensure(scene, view_layer); Object *obedit = BKE_view_layer_edit_object_get(view_layer); @@ -401,7 +401,7 @@ static eContextResult screen_ctx_visible_pose_bones(const bContext *C, bContextD { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ - const Scene *scene = CTX_data_scene(C); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); BKE_view_layer_synced_ensure(scene, view_layer); Object *obact = BKE_view_layer_active_object_get(view_layer); @@ -431,9 +431,8 @@ static eContextResult screen_ctx_selected_pose_bones(const bContext *C, bContext { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ - const Scene *scene = CTX_data_scene(C); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - BKE_view_layer_synced_ensure(scene, view_layer); Object *obact = BKE_view_layer_active_object_get(view_layer); Object *obpose = BKE_object_pose_armature_get(obact); if (obpose && obpose->pose && obpose->data) { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index d452d94d2f0..b5d739ae08e 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -167,11 +167,10 @@ struct AddOperationExecutor { /* Find UV map. */ VArraySpan<float2> surface_uv_map; if (curves_id_orig_->surface_uv_map != nullptr) { - surface_uv_map = bke::mesh_attributes(surface_orig) - .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); - surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_) - .lookup<float2>(curves_id_orig_->surface_uv_map, - ATTR_DOMAIN_CORNER); + surface_uv_map = surface_orig.attributes().lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>( + curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); } if (surface_uv_map.is_empty()) { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc index 2e03e907e34..a37eb4bb560 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc @@ -137,11 +137,10 @@ struct DensityAddOperationExecutor { /* Find UV map. */ VArraySpan<float2> surface_uv_map; if (curves_id_orig_->surface_uv_map != nullptr) { - surface_uv_map = bke::mesh_attributes(*surface_orig_) - .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); - surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_) - .lookup<float2>(curves_id_orig_->surface_uv_map, - ATTR_DOMAIN_CORNER); + surface_uv_map = surface_orig_->attributes().lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>( + curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); } if (surface_uv_map.is_empty()) { report_missing_uv_map_on_original_surface(stroke_extension.reports); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc index 833f00ae0d0..1108f5c72a9 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc @@ -180,8 +180,8 @@ struct SlideOperationExecutor { } surface_looptris_orig_ = {BKE_mesh_runtime_looptri_ensure(surface_orig_), BKE_mesh_runtime_looptri_len(surface_orig_)}; - surface_uv_map_orig_ = - bke::mesh_attributes(*surface_orig_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER); + surface_uv_map_orig_ = surface_orig_->attributes().lookup<float2>(uv_map_name, + ATTR_DOMAIN_CORNER); if (surface_uv_map_orig_.is_empty()) { report_missing_uv_map_on_original_surface(stroke_extension.reports); return; @@ -209,8 +209,8 @@ struct SlideOperationExecutor { BKE_mesh_runtime_looptri_len(surface_eval_)}; surface_verts_eval_ = surface_eval_->verts(); surface_loops_eval_ = surface_eval_->loops(); - surface_uv_map_eval_ = - bke::mesh_attributes(*surface_eval_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>(uv_map_name, + ATTR_DOMAIN_CORNER); if (surface_uv_map_eval_.is_empty()) { report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); return; diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index c1289364fb2..2b80c62a0ba 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -383,6 +383,7 @@ static int hide_show_exec(bContext *C, wmOperator *op) * sculpt but it looks wrong when entering editmode otherwise). */ if (pbvh_type == PBVH_FACES) { BKE_mesh_flush_hidden_from_verts(me); + BKE_pbvh_update_hide_attributes_from_mesh(pbvh); } SCULPT_visibility_sync_all_vertex_to_face_sets(ob->sculpt); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 332a0830081..437ff7506ba 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -134,6 +134,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata, static int mask_flood_fill_exec(bContext *C, wmOperator *op) { + const Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); PaintMaskFloodMode mode; @@ -146,6 +147,9 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) mode = RNA_enum_get(op->ptr, "mode"); value = RNA_float_get(op->ptr, "value"); + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + BKE_sculpt_mask_layers_ensure(ob, mmd); + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); pbvh = ob->sculpt->pbvh; multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); @@ -774,6 +778,8 @@ static void sculpt_gesture_init_face_set_properties(SculptGestureContext *sgcont struct Mesh *mesh = BKE_mesh_from_object(sgcontext->vc.obact); sgcontext->operation = MEM_callocN(sizeof(SculptGestureFaceSetOperation), "Face Set Operation"); + sgcontext->ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + SculptGestureFaceSetOperation *face_set_operation = (SculptGestureFaceSetOperation *) sgcontext->operation; @@ -817,7 +823,7 @@ static void mask_gesture_apply_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { - float prevmask = *vd.mask; + float prevmask = vd.mask ? *vd.mask : 0.0f; if (!any_masked) { any_masked = true; @@ -863,6 +869,10 @@ static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, SculptGestureMaskOperation *mask_operation = (SculptGestureMaskOperation *)sgcontext->operation; + Object *object = sgcontext->vc.obact; + MultiresModifierData *mmd = BKE_sculpt_multires_active(sgcontext->vc.scene, object); + BKE_sculpt_mask_layers_ensure(sgcontext->vc.obact, mmd); + mask_operation->op.sculpt_gesture_begin = sculpt_gesture_mask_begin; mask_operation->op.sculpt_gesture_apply_for_symmetry_pass = sculpt_gesture_mask_apply_for_symmetry_pass; @@ -1315,8 +1325,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) }), sculpt_mesh); BM_mesh_free(bm); - BKE_mesh_nomain_to_mesh( - result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(result, sgcontext->vc.obact->data, sgcontext->vc.obact); } static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgcontext) diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc index 006ceb5f136..10ad4c2192f 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc @@ -92,7 +92,7 @@ static bool vertex_paint_from_weight(Object *ob) return false; } - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name); if (!color_attribute) { @@ -162,7 +162,7 @@ static IndexMask get_selected_indices(const Mesh &mesh, const Span<MVert> verts = mesh.verts(); const Span<MPoly> polys = mesh.polys(); - bke::AttributeAccessor attributes = bke::mesh_attributes(mesh); + bke::AttributeAccessor attributes = mesh.attributes(); if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) { const VArray<bool> selection = attributes.adapt_domain( @@ -186,7 +186,7 @@ static IndexMask get_selected_indices(const Mesh &mesh, return IndexMask(attributes.domain_size(domain)); } -static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask selection) +static void face_corner_color_equalize_verts(Mesh &mesh, const IndexMask selection) { using namespace blender; @@ -196,7 +196,7 @@ static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask sele return; } - bke::AttributeAccessor attributes = bke::mesh_attributes(mesh); + bke::AttributeAccessor attributes = mesh.attributes(); if (attributes.lookup_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) { return; @@ -221,7 +221,7 @@ static bool vertex_color_smooth(Object *ob) Vector<int64_t> indices; const IndexMask selection = get_selected_indices(*me, ATTR_DOMAIN_CORNER, indices); - face_corner_color_equalize_vertices(*me, selection); + face_corner_color_equalize_verts(*me, selection); tag_object_after_update(ob); @@ -270,7 +270,7 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn) return false; } - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name); if (!color_attribute) { diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 5a17d42468e..089a8a4cb54 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -116,7 +116,7 @@ int SCULPT_vertex_count_get(SculptSession *ss) case PBVH_BMESH: return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT); case PBVH_GRIDS: - return BKE_pbvh_get_grid_num_vertices(ss->pbvh); + return BKE_pbvh_get_grid_num_verts(ss->pbvh); } return 0; @@ -253,11 +253,11 @@ float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex) float *mask; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return ss->vmask[vertex.i]; + return ss->vmask ? ss->vmask[vertex.i] : 0.0f; case PBVH_BMESH: v = (BMVert *)vertex.i; mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); - return *mask; + return mask ? *mask : 0.0f; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; @@ -329,8 +329,14 @@ int SCULPT_active_face_set_get(SculptSession *ss) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: + if (!ss->face_sets) { + return SCULPT_FACE_SET_NONE; + } return ss->face_sets[ss->active_face_index]; case PBVH_GRIDS: { + if (!ss->face_sets) { + return SCULPT_FACE_SET_NONE; + } const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, ss->active_grid_index); return ss->face_sets[face_index]; @@ -383,6 +389,7 @@ bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible) { + BLI_assert(ss->face_sets != NULL); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: @@ -405,6 +412,7 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl void SCULPT_face_sets_visibility_invert(SculptSession *ss) { + BLI_assert(ss->face_sets != NULL); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: @@ -422,6 +430,9 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible) switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: + if (!ss->face_sets) { + return; + } for (int i = 0; i < ss->totfaces; i++) { /* This can run on geometry without a face set assigned, so its ID sign can't be changed to @@ -446,11 +457,15 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible) bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef vertex) { + const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh); + if (!hide_poly) { + return true; + } switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int j = 0; j < ss->pmap[vertex.i].count; j++) { - if (ss->face_sets[vert_map->indices[j]] > 0) { + if (!hide_poly[vert_map->indices[j]]) { return true; } } @@ -466,11 +481,15 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef verte bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRef vertex) { + const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh); + if (!hide_poly) { + return true; + } switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int j = 0; j < ss->pmap[vertex.i].count; j++) { - if (ss->face_sets[vert_map->indices[j]] < 0) { + if (hide_poly[vert_map->indices[j]]) { return false; } } @@ -482,7 +501,7 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRe const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); - return ss->face_sets[face_index] > 0; + return !hide_poly[face_index]; } } return true; @@ -492,6 +511,7 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_ { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { + BLI_assert(ss->face_sets != NULL); MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int j = 0; j < ss->pmap[vertex.i].count; j++) { if (ss->face_sets[vert_map->indices[j]] > 0) { @@ -502,6 +522,7 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_ case PBVH_BMESH: break; case PBVH_GRIDS: { + BLI_assert(ss->face_sets != NULL); const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); @@ -517,6 +538,9 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { + if (!ss->face_sets) { + return SCULPT_FACE_SET_NONE; + } MeshElemMap *vert_map = &ss->pmap[vertex.i]; int face_set = 0; for (int i = 0; i < ss->pmap[vertex.i].count; i++) { @@ -529,6 +553,9 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) case PBVH_BMESH: return 0; case PBVH_GRIDS: { + if (!ss->face_sets) { + return SCULPT_FACE_SET_NONE; + } const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); @@ -542,6 +569,9 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { + if (!ss->face_sets) { + return face_set == SCULPT_FACE_SET_NONE; + } MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] == face_set) { @@ -553,6 +583,9 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ case PBVH_BMESH: return true; case PBVH_GRIDS: { + if (!ss->face_sets) { + return face_set == SCULPT_FACE_SET_NONE; + } const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); @@ -562,13 +595,14 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ return true; } -void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob) +void SCULPT_visibility_sync_all_face_sets_to_verts(Object *ob) { SculptSession *ss = ob->sculpt; Mesh *mesh = BKE_object_get_original_mesh(ob); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh); + BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh); break; } case PBVH_GRIDS: { @@ -599,6 +633,9 @@ static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSe void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + if (ss->face_sets == NULL) { + return; + } for (int i = 0; i < ss->totfaces; i++) { const MPoly *poly = &ss->mpoly[i]; bool poly_visible = true; @@ -620,6 +657,9 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index) { + if (!ss->face_sets) { + return true; + } MeshElemMap *vert_map = &ss->pmap[index]; int face_set = -1; for (int i = 0; i < ss->pmap[index].count; i++) { @@ -676,6 +716,9 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex) case PBVH_BMESH: return true; case PBVH_GRIDS: { + if (!ss->face_sets) { + return true; + } const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int vertex_index = vertex.i - grid_index * key->grid_area; @@ -703,6 +746,9 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: { + if (!ss->face_sets) { + return 0; + } int next_face_set = 0; for (int i = 0; i < ss->totfaces; i++) { if (abs(ss->face_sets[i]) > next_face_set) { @@ -792,9 +838,10 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; iter->neighbor_indices = iter->neighbor_indices_fixed; + const bool *hide_poly = BKE_pbvh_get_vert_hide(ss->pbvh); for (int i = 0; i < ss->pmap[vertex.i].count; i++) { - if (ss->face_sets[vert_map->indices[i]] < 0) { + if (hide_poly && hide_poly[vert_map->indices[i]]) { /* Skip connectivity from hidden faces. */ continue; } @@ -1102,7 +1149,7 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood) SCULPT_vertex_random_access_ensure(ss); flood->queue = BLI_gsqueue_new(sizeof(intptr_t)); - flood->visited_vertices = BLI_BITMAP_NEW(vertex_count, "visited vertices"); + flood->visited_verts = BLI_BITMAP_NEW(vertex_count, "visited verts"); } void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex) @@ -1113,7 +1160,7 @@ void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex) void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex) { BLI_gsqueue_push(flood->queue, &vertex); - BLI_BITMAP_ENABLE(flood->visited_vertices, vertex.i); + BLI_BITMAP_ENABLE(flood->visited_verts, vertex.i); } void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd, @@ -1192,7 +1239,7 @@ void SCULPT_floodfill_execute(SculptSession *ss, const PBVHVertRef to_v = ni.vertex; int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); - if (BLI_BITMAP_TEST(flood->visited_vertices, to_v_i)) { + if (BLI_BITMAP_TEST(flood->visited_verts, to_v_i)) { continue; } @@ -1200,7 +1247,7 @@ void SCULPT_floodfill_execute(SculptSession *ss, continue; } - BLI_BITMAP_ENABLE(flood->visited_vertices, BKE_pbvh_vertex_to_index(ss->pbvh, to_v)); + BLI_BITMAP_ENABLE(flood->visited_verts, BKE_pbvh_vertex_to_index(ss->pbvh, to_v)); if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) { BLI_gsqueue_push(flood->queue, &to_v); @@ -1212,7 +1259,7 @@ void SCULPT_floodfill_execute(SculptSession *ss, void SCULPT_floodfill_free(SculptFloodFill *flood) { - MEM_SAFE_FREE(flood->visited_vertices); + MEM_SAFE_FREE(flood->visited_verts); BLI_gsqueue_free(flood->queue); flood->queue = NULL; } @@ -3302,6 +3349,15 @@ static void do_brush_action(Sculpt *sd, BKE_pbvh_ensure_node_loops(ss->pbvh); } + if (SCULPT_tool_is_mask(brush->sculpt_tool)) { + MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); + BKE_sculpt_mask_layers_ensure(ob, mmd); + } + if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) { + Mesh *mesh = BKE_object_get_original_mesh(ob); + ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + } + /* Build a list of all nodes that are potentially within the brush's area of influence */ if (SCULPT_tool_needs_all_pbvh_nodes(brush)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index 93da767e3c5..005892b88a0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -128,22 +128,22 @@ static void sculpt_boundary_index_add(SculptBoundary *boundary, const PBVHVertRef new_vertex, const int new_index, const float distance, - GSet *included_vertices) + GSet *included_verts) { - boundary->vertices[boundary->num_vertices] = new_vertex; + boundary->verts[boundary->verts_num] = new_vertex; if (boundary->distance) { boundary->distance[new_index] = distance; } - if (included_vertices) { - BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index)); + if (included_verts) { + BLI_gset_add(included_verts, POINTER_FROM_INT(new_index)); } - boundary->num_vertices++; - if (boundary->num_vertices >= boundary->vertices_capacity) { - boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE; - boundary->vertices = MEM_reallocN_id( - boundary->vertices, boundary->vertices_capacity * sizeof(PBVHVertRef), "boundary indices"); + boundary->verts_num++; + if (boundary->verts_num >= boundary->verts_capacity) { + boundary->verts_capacity += BOUNDARY_INDICES_BLOCK_SIZE; + boundary->verts = MEM_reallocN_id( + boundary->verts, boundary->verts_capacity * sizeof(PBVHVertRef), "boundary indices"); } }; @@ -152,11 +152,11 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const PBVHVertRef v2) { - boundary->edges[boundary->num_edges].v1 = v1; - boundary->edges[boundary->num_edges].v2 = v2; - boundary->num_edges++; + boundary->edges[boundary->edges_num].v1 = v1; + boundary->edges[boundary->edges_num].v2 = v2; + boundary->edges_num++; - if (boundary->num_edges >= boundary->edges_capacity) { + if (boundary->edges_num >= boundary->edges_capacity) { boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE; boundary->edges = MEM_reallocN_id(boundary->edges, boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge), @@ -209,7 +209,7 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, typedef struct BoundaryFloodFillData { SculptBoundary *boundary; - GSet *included_vertices; + GSet *included_verts; EdgeSet *preview_edges; PBVHVertRef last_visited_vertex; @@ -233,7 +233,7 @@ static bool boundary_floodfill_cb( boundary->distance[from_v_i] + edge_len : 0.0f; sculpt_boundary_index_add( - boundary, to_v, to_v_i, distance_boundary_to_dst, data->included_vertices); + boundary, to_v, to_v_i, distance_boundary_to_dst, data->included_verts); if (!is_duplicate) { sculpt_boundary_preview_edge_add(boundary, from_v, to_v); } @@ -247,7 +247,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, { const int totvert = SCULPT_vertex_count_get(ss); - boundary->vertices = MEM_malloc_arrayN( + boundary->verts = MEM_malloc_arrayN( BOUNDARY_INDICES_BLOCK_SIZE, sizeof(PBVHVertRef), "boundary indices"); if (init_boundary_distances) { @@ -256,7 +256,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, boundary->edges = MEM_malloc_arrayN( BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges"); - GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); + GSet *included_verts = BLI_gset_int_new_ex("included verts", BOUNDARY_INDICES_BLOCK_SIZE); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); @@ -268,12 +268,12 @@ static void sculpt_boundary_indices_init(SculptSession *ss, copy_v3_v3(boundary->initial_vertex_position, SCULPT_vertex_co_get(ss, boundary->initial_vertex)); sculpt_boundary_index_add( - boundary, initial_boundary_vertex, initial_boundary_index, 0.0f, included_vertices); + boundary, initial_boundary_vertex, initial_boundary_index, 0.0f, included_verts); SCULPT_floodfill_add_initial(&flood, boundary->initial_vertex); BoundaryFloodFillData fdata = { .boundary = boundary, - .included_vertices = included_vertices, + .included_verts = included_verts, .last_visited_vertex = {BOUNDARY_VERTEX_NONE}, }; @@ -286,7 +286,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) { SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) { - if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) && + if (BLI_gset_haskey(included_verts, POINTER_FROM_INT(ni.index)) && sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.vertex)) { sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.vertex); boundary->forms_loop = true; @@ -295,7 +295,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } - BLI_gset_free(included_vertices, NULL); + BLI_gset_free(included_verts, NULL); } /** @@ -318,7 +318,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, for (int i = 0; i < totvert; i++) { boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE; - boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE; + boundary->edit_info[i].propagation_steps_num = BOUNDARY_STEPS_NONE; } GSQueue *current_iteration = BLI_gsqueue_new(sizeof(PBVHVertRef)); @@ -326,38 +326,38 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Initialized the first iteration with the vertices already in the boundary. This is propagation * step 0. */ - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); - for (int i = 0; i < boundary->num_vertices; i++) { - int index = BKE_pbvh_vertex_to_index(ss->pbvh, boundary->vertices[i]); + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_verts"); + for (int i = 0; i < boundary->verts_num; i++) { + int index = BKE_pbvh_vertex_to_index(ss->pbvh, boundary->verts[i]); boundary->edit_info[index].original_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, - boundary->vertices[i]); - boundary->edit_info[index].num_propagation_steps = 0; + boundary->verts[i]); + boundary->edit_info[index].propagation_steps_num = 0; /* This ensures that all duplicate vertices in the boundary have the same original_vertex * index, so the deformation for them will be the same. */ if (has_duplicates) { SculptVertexNeighborIter ni_duplis; - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) { + SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->verts[i], ni_duplis) { if (ni_duplis.is_duplicate) { boundary->edit_info[ni_duplis.index].original_vertex_i = BKE_pbvh_vertex_to_index( - ss->pbvh, boundary->vertices[i]); + ss->pbvh, boundary->verts[i]); } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); } - BLI_gsqueue_push(current_iteration, &boundary->vertices[i]); + BLI_gsqueue_push(current_iteration, &boundary->verts[i]); } - int num_propagation_steps = 0; + int propagation_steps_num = 0; float accum_distance = 0.0f; while (true) { /* Stop adding steps to edit info. This happens when a steps is further away from the boundary * than the brush radius or when the entire mesh was already processed. */ if (accum_distance > radius || BLI_gsqueue_is_empty(current_iteration)) { - boundary->max_propagation_steps = num_propagation_steps; + boundary->max_propagation_steps = propagation_steps_num; break; } @@ -371,22 +371,22 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex); if (!is_visible || - boundary->edit_info[ni.index].num_propagation_steps != BOUNDARY_STEPS_NONE) { + boundary->edit_info[ni.index].propagation_steps_num != BOUNDARY_STEPS_NONE) { continue; } boundary->edit_info[ni.index].original_vertex_i = boundary->edit_info[from_v_i].original_vertex_i; - BLI_BITMAP_ENABLE(visited_vertices, ni.index); + BLI_BITMAP_ENABLE(visited_verts, ni.index); if (ni.is_duplicate) { /* Grids duplicates handling. */ - boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v_i].num_propagation_steps; + boundary->edit_info[ni.index].propagation_steps_num = + boundary->edit_info[from_v_i].propagation_steps_num; } else { - boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v_i].num_propagation_steps + 1; + boundary->edit_info[ni.index].propagation_steps_num = + boundary->edit_info[from_v_i].propagation_steps_num + 1; BLI_gsqueue_push(next_iteration, &ni.vertex); @@ -400,8 +400,8 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, if (ni_duplis.is_duplicate) { boundary->edit_info[ni_duplis.index].original_vertex_i = boundary->edit_info[from_v_i].original_vertex_i; - boundary->edit_info[ni_duplis.index].num_propagation_steps = - boundary->edit_info[from_v_i].num_propagation_steps + 1; + boundary->edit_info[ni_duplis.index].propagation_steps_num = + boundary->edit_info[from_v_i].propagation_steps_num + 1; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -428,10 +428,10 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, BLI_gsqueue_push(current_iteration, &next_v); } - num_propagation_steps++; + propagation_steps_num++; } - MEM_SAFE_FREE(visited_vertices); + MEM_SAFE_FREE(visited_verts); BLI_gsqueue_free(current_iteration); BLI_gsqueue_free(next_iteration); @@ -449,9 +449,9 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, BKE_curvemapping_init(brush->curve); for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps != -1) { + if (boundary->edit_info[i].propagation_steps_num != -1) { boundary->edit_info[i].strength_factor = BKE_brush_curve_strength( - brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps); + brush, boundary->edit_info[i].propagation_steps_num, boundary->max_propagation_steps); } if (boundary->edit_info[i].original_vertex_i == @@ -542,7 +542,7 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object, void SCULPT_boundary_data_free(SculptBoundary *boundary) { - MEM_SAFE_FREE(boundary->vertices); + MEM_SAFE_FREE(boundary->verts); MEM_SAFE_FREE(boundary->edges); MEM_SAFE_FREE(boundary->distance); MEM_SAFE_FREE(boundary->edit_info); @@ -564,7 +564,7 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo 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) { + if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) { continue; } @@ -586,7 +586,7 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo } for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) { + if (boundary->edit_info[i].propagation_steps_num == BOUNDARY_STEPS_NONE) { continue; } copy_v3_v3(boundary->bend.pivot_positions[i], @@ -602,7 +602,7 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b 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) { + if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) { continue; } sub_v3_v3v3( @@ -614,7 +614,7 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b } for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) { + if (boundary->edit_info[i].propagation_steps_num == BOUNDARY_STEPS_NONE) { continue; } copy_v3_v3(boundary->slide.directions[i], @@ -625,15 +625,14 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *boundary) { zero_v3(boundary->twist.pivot_position); - float(*poly_verts)[3] = MEM_malloc_arrayN( - 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])); + float(*poly_verts)[3] = MEM_malloc_arrayN(boundary->verts_num, sizeof(float[3]), "poly verts"); + for (int i = 0; i < boundary->verts_num; i++) { + add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->verts[i])); + copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->verts[i])); } - mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->num_vertices); + mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->verts_num); if (boundary->forms_loop) { - normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->num_vertices); + normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->verts_num); } else { sub_v3_v3v3(boundary->twist.rotation_axis, @@ -684,7 +683,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, const float angle = angle_factor * M_PI; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -732,7 +731,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -778,7 +777,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -822,7 +821,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -873,7 +872,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, const float angle = angle_factor * M_PI; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -919,7 +918,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -931,10 +930,10 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, float coord_accum[3] = {0.0f, 0.0f, 0.0f}; int total_neighbors = 0; - const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps; + const int current_propagation_steps = boundary->edit_info[vd.index].propagation_steps_num; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { - if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) { + if (current_propagation_steps == boundary->edit_info[ni.index].propagation_steps_num) { add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.vertex)); total_neighbors++; } @@ -1053,8 +1052,8 @@ void SCULPT_boundary_edges_preview_draw(const uint gpuattr, } immUniformColor3fvAlpha(outline_col, outline_alpha); GPU_line_width(2.0f); - immBegin(GPU_PRIM_LINES, ss->boundary_preview->num_edges * 2); - for (int i = 0; i < ss->boundary_preview->num_edges; i++) { + immBegin(GPU_PRIM_LINES, ss->boundary_preview->edges_num * 2); + for (int i = 0; i < ss->boundary_preview->edges_num; i++) { immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v1)); immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v2)); } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index ad8a1cde9dc..46674c5d239 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -215,13 +215,7 @@ static void SCULPT_dynamic_topology_disable_ex( BKE_sculptsession_bm_to_me(ob, true); /* Reset Face Sets as they are no longer valid. */ - if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) { - CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_SET_DEFAULT, NULL, me->totpoly); - } - ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); - for (int i = 0; i < me->totpoly; i++) { - ss->face_sets[i] = 1; - } + CustomData_free_layers(&me->pdata, CD_SCULPT_FACE_SETS, me->totpoly); me->face_sets_color_default = 1; /* Sync the visibility to vertices manually as the pmap is still not initialized. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 7c4c47261b3..414a855ab2f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -17,6 +17,7 @@ #include "DNA_brush_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BKE_brush.h" @@ -351,13 +352,13 @@ static float sculpt_expand_gradient_value_get(SculptSession *ss, static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCache *expand_cache) { const int totvert = SCULPT_vertex_count_get(ss); - BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); + BLI_bitmap *enabled_verts = BLI_BITMAP_NEW(totvert, "enabled verts"); for (int i = 0; i < totvert; i++) { const bool enabled = sculpt_expand_state_get( ss, expand_cache, BKE_pbvh_index_to_vertex(ss->pbvh, i)); - BLI_BITMAP_SET(enabled_vertices, i, enabled); + BLI_BITMAP_SET(enabled_verts, i, enabled); } - return enabled_vertices; + return enabled_verts; } /** @@ -366,13 +367,13 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa * vertex that is not enabled. */ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, - const BLI_bitmap *enabled_vertices, + const BLI_bitmap *enabled_verts, const bool use_mesh_boundary) { const int totvert = SCULPT_vertex_count_get(ss); - BLI_bitmap *boundary_vertices = BLI_BITMAP_NEW(totvert, "boundary vertices"); + BLI_bitmap *boundary_verts = BLI_BITMAP_NEW(totvert, "boundary verts"); for (int i = 0; i < totvert; i++) { - if (!BLI_BITMAP_TEST(enabled_vertices, i)) { + if (!BLI_BITMAP_TEST(enabled_verts, i)) { continue; } @@ -381,7 +382,7 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, bool is_expand_boundary = false; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) { + if (!BLI_BITMAP_TEST(enabled_verts, ni.index)) { is_expand_boundary = true; } } @@ -391,10 +392,10 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, is_expand_boundary = true; } - BLI_BITMAP_SET(boundary_vertices, i, is_expand_boundary); + BLI_BITMAP_SET(boundary_verts, i, is_expand_boundary); } - return boundary_vertices; + return boundary_verts; } /* Functions implementing different algorithms for initializing falloff values. */ @@ -596,7 +597,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const P SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist"); - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts"); GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); /* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */ @@ -614,9 +615,9 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const P continue; } - for (int i = 0; i < boundary->num_vertices; i++) { - BLI_gsqueue_push(queue, &boundary->vertices[i]); - BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices_i[i]); + for (int i = 0; i < boundary->verts_num; i++) { + BLI_gsqueue_push(queue, &boundary->verts[i]); + BLI_BITMAP_ENABLE(visited_verts, boundary->verts_i[i]); } SCULPT_boundary_data_free(boundary); } @@ -635,18 +636,18 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const P SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) { - if (BLI_BITMAP_TEST(visited_vertices, ni.index)) { + if (BLI_BITMAP_TEST(visited_verts, ni.index)) { continue; } dists[ni.index] = dists[v_next_i] + 1.0f; - BLI_BITMAP_ENABLE(visited_vertices, ni.index); + BLI_BITMAP_ENABLE(visited_verts, ni.index); BLI_gsqueue_push(queue, &ni.vertex); } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } BLI_gsqueue_free(queue); - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); return dists; } @@ -669,7 +670,7 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertR } /* Search and mask as visited the initial vertices using the enabled symmetry passes. */ - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts"); GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char symm_it = 0; symm_it <= symm; symm_it++) { @@ -682,7 +683,7 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertR int symm_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, symm_vertex); BLI_gsqueue_push(queue, &symm_vertex); - BLI_BITMAP_ENABLE(visited_vertices, symm_vertex_i); + BLI_BITMAP_ENABLE(visited_verts, symm_vertex_i); } if (BLI_gsqueue_is_empty(queue)) { @@ -700,18 +701,18 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertR const MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]]; for (int l = 0; l < p->totloop; l++) { const PBVHVertRef neighbor_v = BKE_pbvh_make_vref(ss->mloop[p->loopstart + l].v); - if (BLI_BITMAP_TEST(visited_vertices, neighbor_v.i)) { + if (BLI_BITMAP_TEST(visited_verts, neighbor_v.i)) { continue; } dists[neighbor_v.i] = dists[v_next_i] + 1.0f; - BLI_BITMAP_ENABLE(visited_vertices, neighbor_v.i); + BLI_BITMAP_ENABLE(visited_verts, neighbor_v.i); BLI_gsqueue_push(queue, &neighbor_v); } } } BLI_gsqueue_free(queue); - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); return dists; } @@ -842,27 +843,27 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s */ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, ExpandCache *expand_cache, - BLI_bitmap *enabled_vertices) + BLI_bitmap *enabled_verts) { SculptSession *ss = ob->sculpt; BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES); - GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); - BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); + GSet *initial_verts = BLI_gset_int_new("initial_verts"); + BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false); const int totvert = SCULPT_vertex_count_get(ss); for (int i = 0; i < totvert; i++) { - if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + if (!BLI_BITMAP_TEST(boundary_verts, i)) { continue; } - BLI_gset_add(initial_vertices, POINTER_FROM_INT(i)); + BLI_gset_add(initial_verts, POINTER_FROM_INT(i)); } - MEM_freeN(boundary_vertices); + MEM_freeN(boundary_verts); MEM_SAFE_FREE(expand_cache->vert_falloff); MEM_SAFE_FREE(expand_cache->face_falloff); - expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX); - BLI_gset_free(initial_vertices, NULL); + expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_verts, FLT_MAX); + BLI_gset_free(initial_verts, NULL); } /** @@ -871,7 +872,7 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, */ static void sculpt_expand_topology_from_state_boundary(Object *ob, ExpandCache *expand_cache, - BLI_bitmap *enabled_vertices) + BLI_bitmap *enabled_verts) { MEM_SAFE_FREE(expand_cache->vert_falloff); MEM_SAFE_FREE(expand_cache->face_falloff); @@ -880,19 +881,19 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, const int totvert = SCULPT_vertex_count_get(ss); float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "topology dist"); - BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); + BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); for (int i = 0; i < totvert; i++) { - if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + if (!BLI_BITMAP_TEST(boundary_verts, i)) { continue; } PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); SCULPT_floodfill_add_and_skip_initial(&flood, vertex); } - MEM_freeN(boundary_vertices); + MEM_freeN(boundary_verts); ExpandFloodFillData fdata; fdata.dists = dists; @@ -914,7 +915,7 @@ static void sculpt_expand_resursion_step_add(Object *ob, return; } - BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache); /* Each time a new recursion step is created, reset the distortion strength. This is the expected * result from the recursion, as otherwise the new falloff will render with undesired distortion @@ -923,10 +924,10 @@ static void sculpt_expand_resursion_step_add(Object *ob, switch (recursion_type) { case SCULPT_EXPAND_RECURSION_GEODESICS: - sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_verts); break; case SCULPT_EXPAND_RECURSION_TOPOLOGY: - sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_verts); break; } @@ -936,7 +937,7 @@ static void sculpt_expand_resursion_step_add(Object *ob, sculpt_expand_update_max_face_falloff_factor(ss, expand_cache); } - MEM_freeN(enabled_vertices); + MEM_freeN(enabled_verts); } /* Face Set Boundary falloff. */ @@ -953,7 +954,7 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); + BLI_bitmap *enabled_verts = BLI_BITMAP_NEW(totvert, "enabled verts"); for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); @@ -963,17 +964,17 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { continue; } - BLI_BITMAP_ENABLE(enabled_vertices, i); + BLI_BITMAP_ENABLE(enabled_verts, i); } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_verts); } else { - sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_verts); } - MEM_freeN(enabled_vertices); + MEM_freeN(enabled_verts); if (internal_falloff) { for (int i = 0; i < totvert; i++) { @@ -1086,7 +1087,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, expand_cache->snap = false; expand_cache->invert = false; - BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache); const int totface = ss->totfaces; for (int i = 0; i < totface; i++) { @@ -1099,7 +1100,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, bool any_disabled = false; for (int l = 0; l < poly->totloop; l++) { const MLoop *loop = &ss->mloop[l + poly->loopstart]; - if (!BLI_BITMAP_TEST(enabled_vertices, loop->v)) { + if (!BLI_BITMAP_TEST(enabled_verts, loop->v)) { any_disabled = true; break; } @@ -1110,7 +1111,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, } } - MEM_freeN(enabled_vertices); + MEM_freeN(enabled_verts); expand_cache->snap = prev_snap_state; expand_cache->invert = prev_invert_state; } @@ -1390,9 +1391,15 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c /* Face Sets are always stored as they are needed for snapping. */ expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set"); expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set"); - for (int i = 0; i < totface; i++) { - expand_cache->initial_face_sets[i] = ss->face_sets[i]; - expand_cache->original_face_sets[i] = ss->face_sets[i]; + if (ss->face_sets) { + for (int i = 0; i < totface; i++) { + expand_cache->initial_face_sets[i] = ss->face_sets[i]; + expand_cache->original_face_sets[i] = ss->face_sets[i]; + } + } + else { + memset(expand_cache->initial_face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * totface); + memset(expand_cache->original_face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * totface); } if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { @@ -1514,7 +1521,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache const bool initial_invert_state = expand_cache->invert; expand_cache->invert = false; - BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache); /* For boundary topology, position the pivot using only the boundary of the enabled vertices, * without taking mesh boundary into account. This allows to create deformations like bending the @@ -1522,8 +1529,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache const float use_mesh_boundary = expand_cache->falloff_type != SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; - BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled( - ss, enabled_vertices, use_mesh_boundary); + BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled( + ss, enabled_verts, use_mesh_boundary); /* Ignore invert state, as this is the expected behavior in most cases and mask are created in * inverted state by default. */ @@ -1537,7 +1544,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + if (!BLI_BITMAP_TEST(boundary_verts, i)) { continue; } @@ -1555,8 +1562,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache total++; } - MEM_freeN(enabled_vertices); - MEM_freeN(boundary_vertices); + MEM_freeN(enabled_verts); + MEM_freeN(boundary_verts); if (total > 0) { mul_v3_v3fl(ss->pivot_pos, avg, 1.0f / total); @@ -2118,6 +2125,16 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even return OPERATOR_CANCELLED; } + if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) { + Mesh *mesh = ob->data; + ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + } + + if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { + MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); + BKE_sculpt_mask_layers_ensure(ob, mmd); + } + /* Face Set operations are not supported in dyntopo. */ if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index b89944628da..8aa645c6af5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -303,6 +303,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + Mesh *mesh = ob->data; + ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false); const int tot_vert = SCULPT_vertex_count_get(ss); @@ -349,7 +352,6 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } if (all_visible) { - Mesh *mesh = ob->data; mesh->face_sets_color_default = next_face_set; BKE_pbvh_face_sets_color_set( ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); @@ -373,7 +375,6 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } if (mode == SCULPT_FACE_SET_SELECTION) { - Mesh *mesh = ob->data; BMesh *bm; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); bm = BM_mesh_create(&allocsize, @@ -712,6 +713,9 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) const float threshold = RNA_float_get(op->ptr, "threshold"); + Mesh *mesh = ob->data; + ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + switch (mode) { case SCULPT_FACE_SETS_FROM_LOOSE_PARTS: sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold); @@ -746,7 +750,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(ob); /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); @@ -850,6 +854,10 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (!pbvh_has_face_sets(ss->pbvh)) { + return OPERATOR_CANCELLED; + } + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); const int tot_vert = SCULPT_vertex_count_get(ss); @@ -933,7 +941,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) } /* Sync face sets visibility and vertex visibility. */ - SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); SCULPT_undo_push_end(ob); @@ -1000,6 +1008,10 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE return OPERATOR_CANCELLED; } + if (!pbvh_has_face_sets(ss->pbvh)) { + return OPERATOR_CANCELLED; + } + PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; @@ -1154,7 +1166,9 @@ static void sculpt_face_set_shrink(Object *ob, static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only) { - + if (face_sets == NULL) { + return true; + } int first_face_set = SCULPT_FACE_SET_NONE; if (check_visible_only) { for (int f = 0; f < ss->totfaces; f++) { @@ -1233,21 +1247,21 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, const int totvert = SCULPT_vertex_count_get(ss); Mesh *mesh = ob->data; - bool *fair_vertices = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); + bool *fair_verts = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); SCULPT_boundary_info_ensure(ob); for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, vertex) && - SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) && - SCULPT_vertex_has_unique_face_set(ss, vertex); + fair_verts[i] = !SCULPT_vertex_is_boundary(ss, vertex) && + SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, vertex); } MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); - BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order); - MEM_freeN(fair_vertices); + BKE_mesh_prefair_and_fair_verts(mesh, mvert, fair_verts, fair_order); + MEM_freeN(fair_verts); } static void sculpt_face_set_apply_edit(Object *ob, @@ -1339,7 +1353,7 @@ static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **node PBVH *pbvh = ss->pbvh; /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index cba1d3dcdc1..bb27e4f1e9e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -14,6 +14,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "BKE_brush.h" #include "BKE_context.h" @@ -174,11 +175,15 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + const Scene *scene = CTX_data_scene(C); PBVHNode **nodes; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; int totnode; int filter_type = RNA_enum_get(op->ptr, "filter_type"); + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + BKE_sculpt_mask_layers_ensure(ob, mmd); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); SculptSession *ss = ob->sculpt; diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c index c07c6126405..c0856ab21d2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c @@ -61,9 +61,9 @@ /* Propagate distance from v1 and v2 to v0. */ static bool sculpt_geodesic_mesh_test_dist_add( - MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices) + MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_verts) { - if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) { + if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(v0))) { return false; } @@ -96,7 +96,7 @@ static bool sculpt_geodesic_mesh_test_dist_add( } static float *SCULPT_geodesic_mesh_create(Object *ob, - GSet *initial_vertices, + GSet *initial_verts, const float limit_radius) { SculptSession *ss = ob->sculpt; @@ -137,7 +137,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, BLI_LINKSTACK_INIT(queue_next); for (int i = 0; i < totvert; i++) { - if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) { + if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(i))) { dists[i] = 0.0f; } else { @@ -159,7 +159,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When * this optimization is needed, it is expected for the tool to request the distance to a low * number of vertices (usually just 1 or 2). */ - GSET_ITER (gs_iter, initial_vertices) { + GSET_ITER (gs_iter, initial_verts) { const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); float *v_co = verts[v].co; for (int i = 0; i < totvert; i++) { @@ -170,6 +170,8 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, } } + const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh); + /* Add edges adjacent to an initial vertex to the queue. */ for (int i = 0; i < totedge; i++) { const int v1 = edges[i].v1; @@ -193,13 +195,13 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, SWAP(int, v1, v2); } sculpt_geodesic_mesh_test_dist_add( - verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_vertices); + verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts); } if (ss->epmap[e].count != 0) { for (int poly_map_index = 0; poly_map_index < ss->epmap[e].count; poly_map_index++) { const int poly = ss->epmap[e].indices[poly_map_index]; - if (ss->face_sets[poly] <= 0) { + if (hide_poly && hide_poly[poly]) { continue; } const MPoly *mpoly = &polys[poly]; @@ -210,8 +212,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, if (ELEM(v_other, v1, v2)) { continue; } - if (sculpt_geodesic_mesh_test_dist_add( - verts, v_other, v1, v2, dists, initial_vertices)) { + if (sculpt_geodesic_mesh_test_dist_add(verts, v_other, v1, v2, dists, initial_verts)) { for (int edge_map_index = 0; edge_map_index < ss->vemap[v_other].count; edge_map_index++) { const int e_other = ss->vemap[v_other].indices[edge_map_index]; @@ -258,7 +259,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, /* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the * distance to each vertex. In this case, only one of the initial vertices will be used to * calculate the distance. */ -static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices) +static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_verts) { SculptSession *ss = ob->sculpt; @@ -267,7 +268,7 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); int first_affected = SCULPT_GEODESIC_VERTEX_NONE; GSetIterator gs_iter; - GSET_ITER (gs_iter, initial_vertices) { + GSET_ITER (gs_iter, initial_verts) { first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); break; } @@ -290,17 +291,15 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices return dists; } -float *SCULPT_geodesic_distances_create(Object *ob, - GSet *initial_vertices, - const float limit_radius) +float *SCULPT_geodesic_distances_create(Object *ob, GSet *initial_verts, const float limit_radius) { SculptSession *ss = ob->sculpt; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius); + return SCULPT_geodesic_mesh_create(ob, initial_verts, limit_radius); case PBVH_BMESH: case PBVH_GRIDS: - return SCULPT_geodesic_fallback_create(ob, initial_vertices); + return SCULPT_geodesic_fallback_create(ob, initial_verts); } BLI_assert(false); return NULL; @@ -312,7 +311,7 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, const float limit_radius) { SculptSession *ss = ob->sculpt; - GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); + GSet *initial_verts = BLI_gset_int_new("initial_verts"); const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char i = 0; i <= symm; ++i) { @@ -328,22 +327,22 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false); } if (v.i != PBVH_REF_NONE) { - BLI_gset_add(initial_vertices, POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ss->pbvh, v))); + BLI_gset_add(initial_verts, POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ss->pbvh, v))); } } } - float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); - BLI_gset_free(initial_vertices, NULL); + float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius); + BLI_gset_free(initial_verts, NULL); return dists; } float *SCULPT_geodesic_from_vertex(Object *ob, const PBVHVertRef vertex, const float limit_radius) { - GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); - BLI_gset_add(initial_vertices, + GSet *initial_verts = BLI_gset_int_new("initial_verts"); + BLI_gset_add(initial_verts, POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ob->sculpt->pbvh, vertex))); - float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); - BLI_gset_free(initial_vertices, NULL); + float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius); + BLI_gset_free(initial_verts, NULL); return dists; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e4bba135518..7a72e5cc84b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -97,7 +97,7 @@ typedef struct { /* Flood Fill. */ typedef struct { GSQueue *queue; - BLI_bitmap *visited_vertices; + BLI_bitmap *visited_verts; } SculptFloodFill; typedef enum eBoundaryAutomaskMode { @@ -1005,7 +1005,7 @@ void SCULPT_connected_components_ensure(Object *ob); void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible); bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex); -void SCULPT_visibility_sync_all_face_sets_to_vertices(struct Object *ob); +void SCULPT_visibility_sync_all_face_sets_to_verts(struct Object *ob); void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); /** \} */ @@ -1837,6 +1837,16 @@ BLI_INLINE bool SCULPT_tool_is_paint(int tool) return ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR); } +BLI_INLINE bool SCULPT_tool_is_mask(int tool) +{ + return ELEM(tool, SCULPT_TOOL_MASK); +} + +BLI_INLINE bool SCULPT_tool_is_face_sets(int tool) +{ + return ELEM(tool, SCULPT_TOOL_DRAW_FACE_SETS); +} + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 9556d24f12c..ec246cd3788 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -391,7 +391,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent if (create_face_set) { ss->filter_cache->prev_face_set = MEM_callocN(sizeof(float) * ss->totfaces, "prev face mask"); for (int i = 0; i < ss->totfaces; i++) { - ss->filter_cache->prev_face_set[i] = ss->face_sets[i]; + ss->filter_cache->prev_face_set[i] = ss->face_sets ? ss->face_sets[i] : 0; } ss->filter_cache->new_face_set = SCULPT_face_set_next_available_get(ss); } diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 4f75999ea70..f942aac2e18 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -300,28 +300,30 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); ob->sculpt->mode_type = OB_MODE_SCULPT; - BKE_sculpt_ensure_orig_mesh_data(scene, ob); + BKE_sculpt_ensure_orig_mesh_data(ob); BKE_scene_graph_evaluated_ensure(depsgraph, bmain); /* This function expects a fully evaluated depsgraph. */ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); - /* Here we can detect geometry that was just added to Sculpt Mode as it has the - * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */ - /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not - * initialized, which is used is some operators that modify the mesh topology to perform certain - * actions in the new polys. After these operations are finished, all polys should have a valid - * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility - * correctly. */ - /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new - * objects, like moving the transform pivot position to the new area or masking existing - * geometry. */ SculptSession *ss = ob->sculpt; - const int new_face_set = SCULPT_face_set_next_available_get(ss); - for (int i = 0; i < ss->totfaces; i++) { - if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) { - ss->face_sets[i] = new_face_set; + if (ss->face_sets) { + /* Here we can detect geometry that was just added to Sculpt Mode as it has the + * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */ + /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not + * initialized, which is used is some operators that modify the mesh topology to perform + * certain actions in the new polys. After these operations are finished, all polys should have + * a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their + * visibility correctly. */ + /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new + * objects, like moving the transform pivot position to the new area or masking existing + * geometry. */ + const int new_face_set = SCULPT_face_set_next_available_get(ss); + for (int i = 0; i < ss->totfaces; i++) { + if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) { + ss->face_sets[i] = new_face_set; + } } } } @@ -574,49 +576,48 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float float brush_co[3]; copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss)); - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_verts"); /* Assuming an average of 6 edges per vertex in a triangulated mesh. */ - const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2; + const int max_preview_verts = SCULPT_vertex_count_get(ss) * 3 * 2; if (ss->preview_vert_list == NULL) { - ss->preview_vert_list = MEM_callocN(max_preview_vertices * sizeof(PBVHVertRef), - "preview lines"); + ss->preview_vert_list = MEM_callocN(max_preview_verts * sizeof(PBVHVertRef), "preview lines"); } - GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(PBVHVertRef)); + GSQueue *non_visited_verts = BLI_gsqueue_new(sizeof(PBVHVertRef)); PBVHVertRef active_v = SCULPT_active_vertex_get(ss); - BLI_gsqueue_push(not_visited_vertices, &active_v); + BLI_gsqueue_push(non_visited_verts, &active_v); - while (!BLI_gsqueue_is_empty(not_visited_vertices)) { + while (!BLI_gsqueue_is_empty(non_visited_verts)) { PBVHVertRef from_v; - BLI_gsqueue_pop(not_visited_vertices, &from_v); + BLI_gsqueue_pop(non_visited_verts, &from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - if (totpoints + (ni.size * 2) < max_preview_vertices) { + if (totpoints + (ni.size * 2) < max_preview_verts) { PBVHVertRef to_v = ni.vertex; int to_v_i = ni.index; ss->preview_vert_list[totpoints] = from_v; totpoints++; ss->preview_vert_list[totpoints] = to_v; totpoints++; - if (BLI_BITMAP_TEST(visited_vertices, to_v_i)) { + if (BLI_BITMAP_TEST(visited_verts, to_v_i)) { continue; } - BLI_BITMAP_ENABLE(visited_vertices, to_v_i); + BLI_BITMAP_ENABLE(visited_verts, to_v_i); const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v); if (len_squared_v3v3(brush_co, co) < radius * radius) { - BLI_gsqueue_push(not_visited_vertices, &to_v); + BLI_gsqueue_push(non_visited_verts, &to_v); } } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } - BLI_gsqueue_free(not_visited_vertices); + BLI_gsqueue_free(non_visited_verts); - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); ss->preview_vert_count = totpoints; } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 035531b2f35..51af8f878e5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -168,9 +168,9 @@ struct PartialUpdateData { PBVH *pbvh; bool rebuild; char *modified_grids; - bool *modified_hidden_vertices; - bool *modified_mask_vertices; - bool *modified_color_vertices; + bool *modified_hidden_verts; + bool *modified_mask_verts; + bool *modified_color_verts; }; /** @@ -201,25 +201,25 @@ static void update_cb_partial(PBVHNode *node, void *userdata) const int *vert_indices; BKE_pbvh_node_num_verts(data->pbvh, node, NULL, &verts_num); BKE_pbvh_node_get_verts(data->pbvh, node, &vert_indices, NULL); - if (data->modified_mask_vertices != NULL) { + if (data->modified_mask_verts != NULL) { for (int i = 0; i < verts_num; i++) { - if (data->modified_mask_vertices[vert_indices[i]]) { + if (data->modified_mask_verts[vert_indices[i]]) { BKE_pbvh_node_mark_update_mask(node); break; } } } - if (data->modified_color_vertices != NULL) { + if (data->modified_color_verts != NULL) { for (int i = 0; i < verts_num; i++) { - if (data->modified_color_vertices[vert_indices[i]]) { + if (data->modified_color_verts[vert_indices[i]]) { BKE_pbvh_node_mark_update_color(node); break; } } } - if (data->modified_hidden_vertices != NULL) { + if (data->modified_hidden_verts != NULL) { for (int i = 0; i < verts_num; i++) { - if (data->modified_hidden_vertices[vert_indices[i]]) { + if (data->modified_hidden_verts[vert_indices[i]]) { if (data->rebuild) { BKE_pbvh_node_mark_update_visibility(node); } @@ -486,9 +486,10 @@ static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode) BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); Mesh *me = BKE_object_get_original_mesh(ob); - int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + int *face_sets = CustomData_add_layer( + &me->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, NULL, me->totpoly); for (int i = 0; i < me->totpoly; i++) { - face_sets[i] = unode->face_sets[i]; + SWAP(int, face_sets[i], unode->face_sets[i]); } return false; } @@ -766,7 +767,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false); - SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); @@ -801,9 +802,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase /* The PBVH already keeps track of which vertices need updated normals, but it doesn't keep track * of other updated. In order to tell the corresponding PBVH nodes to update, keep track of which * elements were updated for specific layers. */ - bool *modified_hidden_vertices = NULL; - bool *modified_mask_vertices = NULL; - bool *modified_color_vertices = NULL; + bool *modified_hidden_verts = NULL; + bool *modified_mask_verts = NULL; + bool *modified_color_verts = NULL; char *undo_modified_grids = NULL; bool use_multires_undo = false; @@ -836,19 +837,19 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } break; case SCULPT_UNDO_HIDDEN: - if (modified_hidden_vertices == NULL) { - modified_hidden_vertices = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); + if (modified_hidden_verts == NULL) { + modified_hidden_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); } - if (sculpt_undo_restore_hidden(C, unode, modified_hidden_vertices)) { + if (sculpt_undo_restore_hidden(C, unode, modified_hidden_verts)) { rebuild = true; update_visibility = true; } break; case SCULPT_UNDO_MASK: - if (modified_mask_vertices == NULL) { - modified_mask_vertices = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); + if (modified_mask_verts == NULL) { + modified_mask_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); } - if (sculpt_undo_restore_mask(C, unode, modified_mask_vertices)) { + if (sculpt_undo_restore_mask(C, unode, modified_mask_verts)) { update = true; update_mask = true; } @@ -856,10 +857,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_FACE_SETS: break; case SCULPT_UNDO_COLOR: - if (modified_color_vertices == NULL) { - modified_color_vertices = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); + if (modified_color_verts == NULL) { + modified_color_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); } - if (sculpt_undo_restore_color(C, unode, modified_color_vertices)) { + if (sculpt_undo_restore_color(C, unode, modified_color_verts)) { update = true; } @@ -910,9 +911,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase .rebuild = rebuild, .pbvh = ss->pbvh, .modified_grids = undo_modified_grids, - .modified_hidden_vertices = modified_hidden_vertices, - .modified_mask_vertices = modified_mask_vertices, - .modified_color_vertices = modified_color_vertices, + .modified_hidden_verts = modified_hidden_verts, + .modified_mask_verts = modified_mask_verts, + .modified_color_verts = modified_color_verts, }; BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); @@ -958,9 +959,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } - MEM_SAFE_FREE(modified_hidden_vertices); - MEM_SAFE_FREE(modified_mask_vertices); - MEM_SAFE_FREE(modified_color_vertices); + MEM_SAFE_FREE(modified_hidden_verts); + MEM_SAFE_FREE(modified_mask_verts); + MEM_SAFE_FREE(modified_color_verts); MEM_SAFE_FREE(undo_modified_grids); } @@ -1365,8 +1366,13 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets"); const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); - for (int i = 0; i < me->totpoly; i++) { - unode->face_sets[i] = face_sets[i]; + if (face_sets) { + for (int i = 0; i < me->totpoly; i++) { + unode->face_sets[i] = face_sets[i]; + } + } + else { + memset(unode->face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * me->totpoly); } BLI_addtail(&usculpt->nodes, unode); @@ -1524,7 +1530,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType sculpt_undo_store_hidden(ob, unode); break; case SCULPT_UNDO_MASK: - sculpt_undo_store_mask(ob, unode); + if (pbvh_has_mask(ss->pbvh)) { + sculpt_undo_store_mask(ob, unode); + } break; case SCULPT_UNDO_COLOR: sculpt_undo_store_color(ob, unode); diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 8b9776cf94d..4739fa52674 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -686,9 +686,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm /* Winding was added to island detection in 5197aa04c6bd * However the sculpt tools can flip faces, potentially creating orphaned islands. * See T100132 */ - bool use_winding = false; + const bool use_winding = false; + const bool use_seams = true; data->elementMap = BM_uv_element_map_create( - bm, scene, false, use_winding, do_island_optimization); + bm, scene, false, use_winding, use_seams, do_island_optimization); if (!data->elementMap) { uv_sculpt_stroke_exit(C, op); diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 23c92cbdaa0..6d880f338f6 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -1364,7 +1364,7 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op) /* set handle type */ ANIM_animdata_keyframe_callback(&ac, - (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | + (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY), ANIM_editkeyframes_ipo(mode)); @@ -1414,7 +1414,7 @@ static int actkeys_easing_exec(bContext *C, wmOperator *op) /* set handle type */ ANIM_animdata_keyframe_callback(&ac, - (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | + (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY), ANIM_editkeyframes_easing(mode)); diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index fc0588dbab5..98782ca15a8 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -840,7 +840,7 @@ void ED_spacetype_action(void) ARegionType *art; st->spaceid = SPACE_ACTION; - strncpy(st->name, "Action", BKE_ST_MAXNAME); + STRNCPY(st->name, "Action"); st->create = action_create; st->free = action_free; diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 2bee60557b7..74b7fa3719a 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -917,7 +917,7 @@ void ED_spacetype_buttons(void) ARegionType *art; st->spaceid = SPACE_PROPERTIES; - strncpy(st->name, "Buttons", BKE_ST_MAXNAME); + STRNCPY(st->name, "Buttons"); st->create = buttons_create; st->free = buttons_free; diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index f8bf1893d89..ab952470757 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -1251,7 +1251,7 @@ void ED_spacetype_clip(void) ARegionType *art; st->spaceid = SPACE_CLIP; - strncpy(st->name, "Clip", BKE_ST_MAXNAME); + STRNCPY(st->name, "Clip"); st->create = clip_create; st->free = clip_free; diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 417c65eb01a..8af0c1fc6ab 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -121,6 +121,9 @@ static void console_main_region_init(wmWindowManager *wm, ARegion *region) region->v2d.cur.ymax = prev_y_min + cur_y_range; } + keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + /* own keymap */ keymap = WM_keymap_ensure(wm->defaultconf, "Console", SPACE_CONSOLE, 0); WM_event_add_keymap_handler_v2d_mask(®ion->handlers, keymap); @@ -286,7 +289,7 @@ void ED_spacetype_console(void) ARegionType *art; st->spaceid = SPACE_CONSOLE; - strncpy(st->name, "Console", BKE_ST_MAXNAME); + STRNCPY(st->name, "Console"); st->create = console_create; st->free = console_free; diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 59d9a15fbab..721c58fc34e 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -367,6 +367,39 @@ static FileSelect file_select( /** \} */ /* -------------------------------------------------------------------- */ +/** \name Bookmark Utilities + * \{ */ + +/** + * Local utility to write #BLENDER_BOOKMARK_FILE, reporting an error on failure. + */ +static bool fsmenu_write_file_and_refresh_or_report_error(struct FSMenu *fsmenu, + ScrArea *area, + ReportList *reports) +{ + /* NOTE: use warning instead of error here, because the bookmark operation may be part of + * other actions which should not cause the operator to fail entirely. */ + const char *cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL); + if (UNLIKELY(!cfgdir)) { + BKE_report(reports, RPT_ERROR, "Unable to create configuration directory to write bookmarks"); + return false; + } + + char filepath[FILE_MAX]; + BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_BOOKMARK_FILE); + if (UNLIKELY(!fsmenu_write_file(fsmenu, filepath))) { + BKE_reportf(reports, RPT_ERROR, "Unable to open or write bookmark file \"%s\"", filepath); + return false; + } + + ED_area_tag_refresh(area); + ED_area_tag_redraw(area); + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Box Select Operator * \{ */ @@ -1053,19 +1086,17 @@ static int bookmark_select_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceFile *sfile = CTX_wm_space_file(C); - PropertyRNA *prop; - if ((prop = RNA_struct_find_property(op->ptr, "dir"))) { - FileSelectParams *params = ED_fileselect_get_active_params(sfile); - char entry[256]; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "dir"); + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + char entry[256]; - RNA_property_string_get(op->ptr, prop, entry); - BLI_strncpy(params->dir, entry, sizeof(params->dir)); - BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir); - ED_file_change_dir(C); + RNA_property_string_get(op->ptr, prop, entry); + BLI_strncpy(params->dir, entry, sizeof(params->dir)); + BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir); + ED_file_change_dir(C); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); - } + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); return OPERATOR_FINISHED; } @@ -1095,7 +1126,7 @@ void FILE_OT_select_bookmark(wmOperatorType *ot) /** \name Add Bookmark Operator * \{ */ -static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op)) +static int bookmark_add_exec(bContext *C, wmOperator *op) { ScrArea *area = CTX_wm_area(C); SpaceFile *sfile = CTX_wm_space_file(C); @@ -1103,19 +1134,11 @@ static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op)) struct FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params->dir[0] != '\0') { - char name[FILE_MAX]; fsmenu_insert_entry( fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, NULL, ICON_FILE_FOLDER, FS_INSERT_SAVE); - BLI_join_dirfile(name, - sizeof(name), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); - fsmenu_write_file(fsmenu, name); + fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports); } - - ED_area_tag_refresh(area); - ED_area_tag_redraw(area); return OPERATOR_FINISHED; } @@ -1146,27 +1169,11 @@ static int bookmark_delete_exec(bContext *C, wmOperator *op) int nentries = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS); PropertyRNA *prop = RNA_struct_find_property(op->ptr, "index"); - - if (prop) { - int index; - if (RNA_property_is_set(op->ptr, prop)) { - index = RNA_property_int_get(op->ptr, prop); - } - else { /* if index unset, use active bookmark... */ - index = sfile->bookmarknr; - } - if ((index > -1) && (index < nentries)) { - char name[FILE_MAX]; - - fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index); - BLI_join_dirfile(name, - sizeof(name), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); - fsmenu_write_file(fsmenu, name); - ED_area_tag_refresh(area); - ED_area_tag_redraw(area); - } + const int index = RNA_property_is_set(op->ptr, prop) ? RNA_property_int_get(op->ptr, prop) : + sfile->bookmarknr; + if ((index > -1) && (index < nentries)) { + fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index); + fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports); } return OPERATOR_FINISHED; @@ -1197,7 +1204,7 @@ void FILE_OT_bookmark_delete(wmOperatorType *ot) /** \name Cleanup Bookmark Operator * \{ */ -static int bookmark_cleanup_exec(bContext *C, wmOperator *UNUSED(op)) +static int bookmark_cleanup_exec(bContext *C, wmOperator *op) { ScrArea *area = CTX_wm_area(C); struct FSMenu *fsmenu = ED_fsmenu_get(); @@ -1218,16 +1225,8 @@ static int bookmark_cleanup_exec(bContext *C, wmOperator *UNUSED(op)) } if (changed) { - char name[FILE_MAX]; - - BLI_join_dirfile(name, - sizeof(name), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); - fsmenu_write_file(fsmenu, name); + fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports); fsmenu_refresh_bookmarks_status(CTX_wm_manager(C), fsmenu); - ED_area_tag_refresh(area); - ED_area_tag_redraw(area); } return OPERATOR_FINISHED; @@ -1269,8 +1268,6 @@ static int bookmark_move_exec(bContext *C, wmOperator *op) struct FSMenuEntry *fsmentry = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); const struct FSMenuEntry *fsmentry_org = fsmentry; - char fname[FILE_MAX]; - const int direction = RNA_enum_get(op->ptr, "direction"); const int totitems = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS); const int act_index = sfile->bookmarknr; @@ -1306,13 +1303,8 @@ static int bookmark_move_exec(bContext *C, wmOperator *op) /* Need to update active bookmark number. */ sfile->bookmarknr = new_index; - BLI_join_dirfile(fname, - sizeof(fname), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); - fsmenu_write_file(fsmenu, fname); + fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports); - ED_area_tag_redraw(area); return OPERATOR_FINISHED; } @@ -1352,21 +1344,16 @@ void FILE_OT_bookmark_move(wmOperatorType *ot) /** \name Reset Recent Blend Files Operator * \{ */ -static int reset_recent_exec(bContext *C, wmOperator *UNUSED(op)) +static int reset_recent_exec(bContext *C, wmOperator *op) { ScrArea *area = CTX_wm_area(C); - char name[FILE_MAX]; struct FSMenu *fsmenu = ED_fsmenu_get(); while (ED_fsmenu_get_entry(fsmenu, FS_CATEGORY_RECENT, 0) != NULL) { fsmenu_remove_entry(fsmenu, FS_CATEGORY_RECENT, 0); } - BLI_join_dirfile(name, - sizeof(name), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); - fsmenu_write_file(fsmenu, name); - ED_area_tag_redraw(area); + + fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports); return OPERATOR_FINISHED; } @@ -1795,6 +1782,8 @@ static bool file_execute(bContext *C, SpaceFile *sfile) } /* Opening file, sends events now, so things get handled on window-queue level. */ else if (sfile->op) { + ScrArea *area = CTX_wm_area(C); + struct FSMenu *fsmenu = ED_fsmenu_get(); wmOperator *op = sfile->op; char filepath[FILE_MAX]; @@ -1803,7 +1792,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) file_sfile_to_operator_ex(bmain, op, sfile, filepath); if (BLI_exists(params->dir)) { - fsmenu_insert_entry(ED_fsmenu_get(), + fsmenu_insert_entry(fsmenu, FS_CATEGORY_RECENT, params->dir, NULL, @@ -1811,11 +1800,8 @@ static bool file_execute(bContext *C, SpaceFile *sfile) FS_INSERT_SAVE | FS_INSERT_FIRST); } - BLI_join_dirfile(filepath, - sizeof(filepath), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); - fsmenu_write_file(ED_fsmenu_get(), filepath); + fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports); + WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC); } @@ -2327,7 +2313,6 @@ static int file_directory_new_exec(bContext *C, wmOperator *op) char name[FILE_MAXFILE]; char path[FILE_MAX]; bool generate_name = true; - PropertyRNA *prop; wmWindowManager *wm = CTX_wm_manager(C); SpaceFile *sfile = CTX_wm_space_file(C); @@ -2341,7 +2326,8 @@ static int file_directory_new_exec(bContext *C, wmOperator *op) path[0] = '\0'; - if ((prop = RNA_struct_find_property(op->ptr, "directory"))) { + { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "directory"); RNA_property_string_get(op->ptr, prop, path); if (path[0] != '\0') { generate_name = false; diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 30e13235f45..35ce7ef364c 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -519,7 +519,7 @@ void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx } } -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath) +bool fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath) { FSMenuEntry *fsm_iter = NULL; char fsm_name[FILE_MAX]; @@ -527,33 +527,36 @@ void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath) FILE *fp = BLI_fopen(filepath, "w"); if (!fp) { - return; + return false; } - fprintf(fp, "[Bookmarks]\n"); + bool has_error = false; + has_error |= (fprintf(fp, "[Bookmarks]\n") < 0); for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsm_iter; fsm_iter = fsm_iter->next) { if (fsm_iter->path && fsm_iter->save) { fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name)); if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) { - fprintf(fp, "!%s\n", fsm_iter->name); + has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0); } - fprintf(fp, "%s\n", fsm_iter->path); + has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0); } } - fprintf(fp, "[Recent]\n"); + has_error = (fprintf(fp, "[Recent]\n") < 0); for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT); fsm_iter && (nwritten < FSMENU_RECENT_MAX); fsm_iter = fsm_iter->next, nwritten++) { if (fsm_iter->path && fsm_iter->save) { fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name)); if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) { - fprintf(fp, "!%s\n", fsm_iter->name); + has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0); } - fprintf(fp, "%s\n", fsm_iter->path); + has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0); } } fclose(fp); + + return !has_error; } void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath) diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h index 6e980a326fc..f4f0dafbc73 100644 --- a/source/blender/editors/space_file/fsmenu.h +++ b/source/blender/editors/space_file/fsmenu.h @@ -37,8 +37,11 @@ short fsmenu_can_save(struct FSMenu *fsmenu, enum FSMenuCategory category, int i /** Removes the fsmenu entry at the given \a index. */ void fsmenu_remove_entry(struct FSMenu *fsmenu, enum FSMenuCategory category, int idx); -/** saves the 'bookmarks' to the specified file */ -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath); +/** + * Saves the 'bookmarks' to the specified file. + * \return true on success. + */ +bool fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath); /** reads the 'bookmarks' from the specified file */ void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index b5cad0f6ff8..bba0c27bb4d 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -992,7 +992,7 @@ void ED_spacetype_file(void) ARegionType *art; st->spaceid = SPACE_FILE; - strncpy(st->name, "File", BKE_ST_MAXNAME); + STRNCPY(st->name, "File"); st->create = file_create; st->free = file_free; diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 3594c65c1cb..1434f204ee5 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -810,7 +810,7 @@ void ED_spacetype_ipo(void) ARegionType *art; st->spaceid = SPACE_GRAPH; - strncpy(st->name, "Graph", BKE_ST_MAXNAME); + STRNCPY(st->name, "Graph"); st->create = graph_create; st->free = graph_free; diff --git a/source/blender/editors/space_image/image_undo.cc b/source/blender/editors/space_image/image_undo.cc index 065641c4051..8f144264824 100644 --- a/source/blender/editors/space_image/image_undo.cc +++ b/source/blender/editors/space_image/image_undo.cc @@ -522,7 +522,7 @@ static void ubuf_ensure_compat_ibuf(const UndoImageBuf *ubuf, ImBuf *ibuf) IMB_rect_size_set(ibuf, ubuf->image_dims); if (ubuf->image_state.use_float) { - imb_addrectfloatImBuf(ibuf); + imb_addrectfloatImBuf(ibuf, 4); } else { imb_addrectImBuf(ibuf); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 8137752847b..096ae4fd328 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -1033,7 +1033,7 @@ void ED_spacetype_image(void) ARegionType *art; st->spaceid = SPACE_IMAGE; - strncpy(st->name, "Image", BKE_ST_MAXNAME); + STRNCPY(st->name, "Image"); st->create = image_create; st->free = image_free; diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index e34f74f92f1..9b29ae737c5 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -130,7 +130,7 @@ static void stats_object(Object *ob, SceneStats *stats, GSet *objects_gset) { - if ((ob->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) { + if ((ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) { return; } @@ -345,7 +345,7 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats) stats->tottri = ob->sculpt->bm->totface; break; case PBVH_GRIDS: - stats->totvertsculpt = BKE_pbvh_get_grid_num_vertices(ss->pbvh); + stats->totvertsculpt = BKE_pbvh_get_grid_num_verts(ss->pbvh); stats->totfacesculpt = BKE_pbvh_get_grid_num_faces(ss->pbvh); break; } @@ -353,7 +353,7 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats) /* Statistics displayed in info header. Called regularly on scene changes. */ static void stats_update(Depsgraph *depsgraph, - Scene *scene, + const Scene *scene, ViewLayer *view_layer, View3D *v3d_local, SceneStats *stats) @@ -367,7 +367,7 @@ static void stats_update(Depsgraph *depsgraph, if (obedit) { /* Edit Mode. */ FOREACH_OBJECT_BEGIN (scene, view_layer, ob_iter) { - if (ob_iter->base_flag & BASE_VISIBLE_VIEWLAYER) { + if (ob_iter->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) { if (ob_iter->mode & OB_MODE_EDIT) { stats_object_edit(ob_iter, stats); stats->totobjsel++; @@ -387,7 +387,7 @@ static void stats_update(Depsgraph *depsgraph, else if (ob && (ob->mode & OB_MODE_POSE)) { /* Pose Mode. */ FOREACH_OBJECT_BEGIN (scene, view_layer, ob_iter) { - if (ob_iter->base_flag & BASE_VISIBLE_VIEWLAYER) { + if (ob_iter->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) { if (ob_iter->mode & OB_MODE_POSE) { stats_object_pose(ob_iter, stats); stats->totobjsel++; diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index 1513ba5e892..63c8d74c684 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -254,7 +254,7 @@ void ED_spacetype_info(void) ARegionType *art; st->spaceid = SPACE_INFO; - strncpy(st->name, "Info", BKE_ST_MAXNAME); + STRNCPY(st->name, "Info"); st->create = info_create; st->free = info_free; diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 9652819404e..72b2eb20f8f 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -213,7 +213,8 @@ static bool nla_panel_poll(const bContext *C, PanelType *pt) static bool nla_animdata_panel_poll(const bContext *C, PanelType *UNUSED(pt)) { PointerRNA ptr; - return (nla_panel_context(C, &ptr, NULL, NULL) && (ptr.data != NULL)); + PointerRNA strip_ptr; + return (nla_panel_context(C, &ptr, NULL, &strip_ptr) && (ptr.data != NULL) && (ptr.owner_id != strip_ptr.owner_id)); } static bool nla_strip_panel_poll(const bContext *C, PanelType *UNUSED(pt)) @@ -265,13 +266,18 @@ static bool nla_strip_eval_panel_poll(const bContext *C, PanelType *UNUSED(pt)) static void nla_panel_animdata(const bContext *C, Panel *panel) { PointerRNA adt_ptr; + PointerRNA strip_ptr; /* AnimData *adt; */ uiLayout *layout = panel->layout; uiLayout *row; uiBlock *block; /* check context and also validity of pointer */ - if (!nla_panel_context(C, &adt_ptr, NULL, NULL)) { + if (!nla_panel_context(C, &adt_ptr, NULL, &strip_ptr)) { + return; + } + + if(adt_ptr.owner_id == strip_ptr.owner_id){ return; } diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 801d032a861..bcdbbb00d1c 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -606,6 +606,36 @@ void NLA_OT_view_frame(wmOperatorType *ot) * (or the active block if no space in the track). * \{ */ +/* Get a list of the editable tracks being shown in the NLA. */ +static int nlaedit_get_editable_tracks(bAnimContext *ac, ListBase *anim_data) +{ + const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); + return ANIM_animdata_filter(ac, anim_data, filter, ac->data, ac->datatype); +} + +static int nlaedit_add_actionclip_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + /* Get editor data. */ + bAnimContext ac; + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + ListBase anim_data = {NULL, NULL}; + const size_t items = nlaedit_get_editable_tracks(&ac, &anim_data); + + if (items == 0) { + BKE_report(op->reports, + RPT_ERROR, + "No active track(s) to add strip to, select an existing track or add one before " + "trying again"); + return OPERATOR_CANCELLED; + } + + return WM_enum_search_invoke(C, op, event); +} + /* add the specified action as new strip */ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op) { @@ -615,8 +645,6 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op) ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - size_t items; - int filter; bAction *act; @@ -654,20 +682,7 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op) */ nlaedit_add_tracks_empty(&ac); - /* get a list of the editable tracks being shown in the NLA - * - this is limited to active ones for now, but could be expanded to - */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | - ANIMFILTER_FCURVESONLY); - items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - if (items == 0) { - BKE_report(op->reports, - RPT_ERROR, - "No active track(s) to add strip to, select an existing track or add one before " - "trying again"); - return OPERATOR_CANCELLED; - } + nlaedit_get_editable_tracks(&ac, &anim_data); /* for every active track, * try to add strip to free space in track or to the top of the stack if no space */ @@ -736,7 +751,7 @@ void NLA_OT_actionclip_add(wmOperatorType *ot) "Add an Action-Clip strip (i.e. an NLA Strip referencing an Action) to the active track"; /* api callbacks */ - ot->invoke = WM_enum_search_invoke; + ot->invoke = nlaedit_add_actionclip_invoke; ot->exec = nlaedit_add_actionclip_exec; ot->poll = nlaop_poll_tweakmode_off; diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index ba7e8987dd5..c71e63e9dcd 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -568,7 +568,7 @@ void ED_spacetype_nla(void) ARegionType *art; st->spaceid = SPACE_NLA; - strncpy(st->name, "NLA", BKE_ST_MAXNAME); + STRNCPY(st->name, "NLA"); st->create = nla_create; st->free = nla_free; diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 553d4013324..f1387da97b5 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -227,12 +227,10 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2) ED_node_tree_propagate_change(C, &bmain, snode.edittree); /* Start translation operator with the new node. */ - wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_translate", true); + wmOperatorType *ot = WM_operatortype_find("NODE_OT_translate_attach_remove_on_cancel", true); BLI_assert(ot); PointerRNA ptr; WM_operator_properties_create_ptr(&ptr, ot); - RNA_boolean_set(&ptr, "view2d_edge_pan", true); - RNA_boolean_set(&ptr, "remove_on_cancel", true); WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr); WM_operator_properties_free(&ptr); } @@ -247,8 +245,8 @@ static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *ar { LinkDragSearchStorage &storage = *(LinkDragSearchStorage *)arg_op; - bNodeTree *node_tree = CTX_wm_space_node(C)->nodetree; - gather_socket_link_operations(*node_tree, storage.from_socket, storage.search_link_ops); + bNodeTree &node_tree = *CTX_wm_space_node(C)->edittree; + gather_socket_link_operations(node_tree, storage.from_socket, storage.search_link_ops); uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS); UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 3da799d0fd5..3a8e5d0aed6 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -13,6 +13,7 @@ #include "DNA_light_types.h" #include "DNA_linestyle_types.h" #include "DNA_material_types.h" +#include "DNA_modifier_types.h" #include "DNA_node_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -29,11 +30,13 @@ #include "BLT_translation.h" +#include "BKE_compute_contexts.hh" #include "BKE_context.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_object.h" @@ -65,7 +68,8 @@ #include "RNA_access.h" #include "RNA_prototypes.h" -#include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_geometry_exec.hh" +#include "NOD_geometry_nodes_log.hh" #include "NOD_node_declaration.hh" #include "NOD_socket_declarations_geometry.hh" @@ -74,10 +78,11 @@ #include "node_intern.hh" /* own include */ +namespace geo_log = blender::nodes::geo_eval_log; + using blender::GPointer; +using blender::Vector; using blender::fn::GField; -namespace geo_log = blender::nodes::geometry_nodes_eval_log; -using geo_log::eNamedAttrUsage; extern "C" { /* XXX interface.h */ @@ -85,6 +90,17 @@ extern void ui_draw_dropshadow( const rctf *rct, float radius, float aspect, float alpha, int select); } +/** + * This is passed to many functions which draw the node editor. + */ +struct TreeDrawContext { + /** + * Geometry nodes logs various data during execution. The logged data that corresponds to the + * currently drawn node tree can be retrieved from the log below. + */ + geo_log::GeoTreeLog *geo_tree_log = nullptr; +}; + float ED_node_grid_size() { return U.widget_unit; @@ -157,6 +173,12 @@ void ED_node_tag_update_id(ID *id) namespace blender::ed::space_node { +static void node_socket_add_tooltip_in_node_editor(TreeDrawContext * /*tree_draw_ctx*/, + const bNodeTree *ntree, + const bNode *node, + const bNodeSocket *sock, + uiLayout *layout); + static bool compare_nodes(const bNode *a, const bNode *b) { /* These tell if either the node or any of the parent nodes is selected. @@ -313,7 +335,11 @@ float2 node_from_view(const bNode &node, const float2 &co) /** * Based on settings and sockets in node, set drawing rect info. */ -static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block) +static void node_update_basis(const bContext &C, + TreeDrawContext &tree_draw_ctx, + bNodeTree &ntree, + bNode &node, + uiBlock &block) { PointerRNA nodeptr; RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); @@ -374,7 +400,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, const char *socket_label = nodeSocketLabel(socket); socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); - node_socket_add_tooltip(ntree, node, *socket, *row); + node_socket_add_tooltip_in_node_editor(&tree_draw_ctx, &ntree, &node, socket, row); UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -506,7 +532,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, const char *socket_label = nodeSocketLabel(socket); socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); - node_socket_add_tooltip(ntree, node, *socket, *row); + node_socket_add_tooltip_in_node_editor(&tree_draw_ctx, &ntree, &node, socket, row); UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -823,25 +849,16 @@ static void create_inspection_string_for_generic_value(const GPointer value, std } } -static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &value_log, - std::stringstream &ss) +static void create_inspection_string_for_field_info(const geo_log::FieldInfoLog &value_log, + std::stringstream &ss) { - const CPPType &type = value_log.type(); - const GField &field = value_log.field(); - const Span<std::string> input_tooltips = value_log.input_tooltips(); + const CPPType &type = value_log.type; + const Span<std::string> input_tooltips = value_log.input_tooltips; if (input_tooltips.is_empty()) { - if (field) { - BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - blender::fn::evaluate_constant_field(field, buffer); - create_inspection_string_for_generic_value({type, buffer}, ss); - type.destruct(buffer); - } - else { - /* Constant values should always be logged. */ - BLI_assert_unreachable(); - ss << "Value has not been logged"; - } + /* Should have been logged as constant value. */ + BLI_assert_unreachable(); + ss << "Value has not been logged"; } else { if (type.is<int>()) { @@ -874,11 +891,11 @@ static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &v } } -static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log, - std::stringstream &ss, - const nodes::decl::Geometry *geometry) +static void create_inspection_string_for_geometry_info(const geo_log::GeometryInfoLog &value_log, + std::stringstream &ss, + const nodes::decl::Geometry *socket_decl) { - Span<GeometryComponentType> component_types = value_log.component_types(); + Span<GeometryComponentType> component_types = value_log.component_types; if (component_types.is_empty()) { ss << TIP_("Empty Geometry"); return; @@ -895,7 +912,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo const char *line_end = (type == component_types.last()) ? "" : ".\n"; switch (type) { case GEO_COMPONENT_TYPE_MESH: { - const geo_log::GeometryValueLog::MeshInfo &mesh_info = *value_log.mesh_info; + const geo_log::GeometryInfoLog::MeshInfo &mesh_info = *value_log.mesh_info; char line[256]; BLI_snprintf(line, sizeof(line), @@ -907,7 +924,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo break; } case GEO_COMPONENT_TYPE_POINT_CLOUD: { - const geo_log::GeometryValueLog::PointCloudInfo &pointcloud_info = + const geo_log::GeometryInfoLog::PointCloudInfo &pointcloud_info = *value_log.pointcloud_info; char line[256]; BLI_snprintf(line, @@ -918,7 +935,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo break; } case GEO_COMPONENT_TYPE_CURVE: { - const geo_log::GeometryValueLog::CurveInfo &curve_info = *value_log.curve_info; + const geo_log::GeometryInfoLog::CurveInfo &curve_info = *value_log.curve_info; char line[256]; BLI_snprintf(line, sizeof(line), @@ -928,7 +945,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo break; } case GEO_COMPONENT_TYPE_INSTANCES: { - const geo_log::GeometryValueLog::InstancesInfo &instances_info = *value_log.instances_info; + const geo_log::GeometryInfoLog::InstancesInfo &instances_info = *value_log.instances_info; char line[256]; BLI_snprintf(line, sizeof(line), @@ -943,7 +960,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo } case GEO_COMPONENT_TYPE_EDIT: { if (value_log.edit_data_info.has_value()) { - const geo_log::GeometryValueLog::EditDataInfo &edit_info = *value_log.edit_data_info; + const geo_log::GeometryInfoLog::EditDataInfo &edit_info = *value_log.edit_data_info; char line[256]; BLI_snprintf(line, sizeof(line), @@ -959,11 +976,11 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo /* If the geometry declaration is null, as is the case for input to group output, * or it is an output socket don't show supported types. */ - if (geometry == nullptr || geometry->in_out() == SOCK_OUT) { + if (socket_decl == nullptr || socket_decl->in_out() == SOCK_OUT) { return; } - Span<GeometryComponentType> supported_types = geometry->supported_types(); + Span<GeometryComponentType> supported_types = socket_decl->supported_types(); if (supported_types.is_empty()) { ss << ".\n\n" << TIP_("Supported: All Types"); return; @@ -1000,43 +1017,37 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo } } -static std::optional<std::string> create_socket_inspection_string(const bContext &C, - const bNode &node, +static std::optional<std::string> create_socket_inspection_string(TreeDrawContext &tree_draw_ctx, const bNodeSocket &socket) { - const SpaceNode *snode = CTX_wm_space_node(&C); - if (snode == nullptr) { - return {}; - }; - - const geo_log::SocketLog *socket_log = geo_log::ModifierLog::find_socket_by_node_editor_context( - *snode, node, socket); - if (socket_log == nullptr) { - return {}; - } - const geo_log::ValueLog *value_log = socket_log->value(); + using namespace blender::nodes::geo_eval_log; + tree_draw_ctx.geo_tree_log->ensure_socket_values(); + ValueLog *value_log = tree_draw_ctx.geo_tree_log->find_socket_value_log(socket); if (value_log == nullptr) { - return {}; + return std::nullopt; } - std::stringstream ss; if (const geo_log::GenericValueLog *generic_value_log = dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { - create_inspection_string_for_generic_value(generic_value_log->value(), ss); + create_inspection_string_for_generic_value(generic_value_log->value, ss); } - if (const geo_log::GFieldValueLog *gfield_value_log = - dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) { - create_inspection_string_for_gfield(*gfield_value_log, ss); + else if (const geo_log::FieldInfoLog *gfield_value_log = + dynamic_cast<const geo_log::FieldInfoLog *>(value_log)) { + create_inspection_string_for_field_info(*gfield_value_log, ss); } - else if (const geo_log::GeometryValueLog *geo_value_log = - dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) { - create_inspection_string_for_geometry( + else if (const geo_log::GeometryInfoLog *geo_value_log = + dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) { + create_inspection_string_for_geometry_info( *geo_value_log, ss, dynamic_cast<const nodes::decl::Geometry *>(socket.runtime->declaration)); } - return ss.str(); + std::string str = ss.str(); + if (str.empty()) { + return std::nullopt; + } + return str; } static bool node_socket_has_tooltip(const bNodeTree &ntree, const bNodeSocket &socket) @@ -1046,34 +1057,42 @@ static bool node_socket_has_tooltip(const bNodeTree &ntree, const bNodeSocket &s } if (socket.runtime->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration; + const nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration; return !socket_decl.description().is_empty(); } return false; } -static char *node_socket_get_tooltip(const bContext &C, - const bNodeTree &ntree, - const bNode &node, - const bNodeSocket &socket) +static char *node_socket_get_tooltip(const bContext *C, + const bNodeTree *ntree, + const bNode *UNUSED(node), + const bNodeSocket *socket) { + SpaceNode *snode = CTX_wm_space_node(C); + TreeDrawContext tree_draw_ctx; + if (snode != nullptr) { + if (ntree->type == NTREE_GEOMETRY) { + tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode); + } + } + std::stringstream output; - if (socket.runtime->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration; + if (socket->runtime->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration; blender::StringRef description = socket_decl.description(); if (!description.is_empty()) { output << TIP_(description.data()); } } - if (ntree.type == NTREE_GEOMETRY) { + if (ntree->type == NTREE_GEOMETRY && tree_draw_ctx.geo_tree_log != nullptr) { if (!output.str().empty()) { output << ".\n\n"; } std::optional<std::string> socket_inspection_str = create_socket_inspection_string( - C, node, socket); + tree_draw_ctx, *socket); if (socket_inspection_str.has_value()) { output << *socket_inspection_str; } @@ -1083,37 +1102,46 @@ static char *node_socket_get_tooltip(const bContext &C, } if (output.str().empty()) { - output << nodeSocketLabel(&socket); + output << nodeSocketLabel(socket); } return BLI_strdup(output.str().c_str()); } -void node_socket_add_tooltip(const bNodeTree &ntree, - const bNode &node, - const bNodeSocket &sock, - uiLayout &layout) +static void node_socket_add_tooltip_in_node_editor(TreeDrawContext *UNUSED(tree_draw_ctx), + const bNodeTree *ntree, + const bNode *node, + const bNodeSocket *sock, + uiLayout *layout) { - if (!node_socket_has_tooltip(ntree, sock)) { + if (!node_socket_has_tooltip(*ntree, *sock)) { return; } - SocketTooltipData *data = MEM_new<SocketTooltipData>(__func__); - data->ntree = &ntree; - data->node = &node; - data->socket = &sock; + SocketTooltipData *data = MEM_cnew<SocketTooltipData>(__func__); + data->ntree = ntree; + data->node = node; + data->socket = sock; uiLayoutSetTooltipFunc( - &layout, + layout, [](bContext *C, void *argN, const char *UNUSED(tip)) { - const SocketTooltipData *data = static_cast<SocketTooltipData *>(argN); - return node_socket_get_tooltip(*C, *data->ntree, *data->node, *data->socket); + SocketTooltipData *data = static_cast<SocketTooltipData *>(argN); + return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); }, data, MEM_dupallocN, MEM_freeN); } +void node_socket_add_tooltip(const bNodeTree &ntree, + const bNode &node, + const bNodeSocket &sock, + uiLayout &layout) +{ + node_socket_add_tooltip_in_node_editor(nullptr, &ntree, &node, &sock, &layout); +} + static void node_socket_draw_nested(const bContext &C, bNodeTree &ntree, PointerRNA &node_ptr, @@ -1178,7 +1206,7 @@ static void node_socket_draw_nested(const bContext &C, but, [](bContext *C, void *argN, const char *UNUSED(tip)) { SocketTooltipData *data = (SocketTooltipData *)argN; - return node_socket_get_tooltip(*C, *data->ntree, *data->node, *data->socket); + return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); }, data, MEM_freeN); @@ -1607,27 +1635,26 @@ static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char #define NODE_HEADER_ICON_SIZE (0.8f * U.widget_unit) -static void node_add_error_message_button( - const bContext &C, bNode &node, uiBlock &block, const rctf &rect, float &icon_offset) +static void node_add_error_message_button(TreeDrawContext &tree_draw_ctx, + bNode &node, + uiBlock &block, + const rctf &rect, + float &icon_offset) { - SpaceNode *snode = CTX_wm_space_node(&C); - const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(*snode, - node); - if (node_log == nullptr) { - return; + Span<geo_log::NodeWarning> warnings; + if (tree_draw_ctx.geo_tree_log) { + geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.name); + if (node_log != nullptr) { + warnings = node_log->warnings; + } } - - Span<geo_log::NodeWarning> warnings = node_log->warnings(); - if (warnings.is_empty()) { return; } - NodeErrorsTooltipData *tooltip_data = (NodeErrorsTooltipData *)MEM_mallocN( - sizeof(NodeErrorsTooltipData), __func__); - tooltip_data->warnings = warnings; - const geo_log::NodeWarningType display_type = node_error_highest_priority(warnings); + NodeErrorsTooltipData *tooltip_data = MEM_new<NodeErrorsTooltipData>(__func__); + tooltip_data->warnings = warnings; icon_offset -= NODE_HEADER_ICON_SIZE; UI_block_emboss_set(&block, UI_EMBOSS_NONE); @@ -1645,90 +1672,70 @@ static void node_add_error_message_button( 0, 0, nullptr); - UI_but_func_tooltip_set(but, node_errors_tooltip_fn, tooltip_data, MEM_freeN); + UI_but_func_tooltip_set(but, node_errors_tooltip_fn, tooltip_data, [](void *arg) { + MEM_delete(static_cast<NodeErrorsTooltipData *>(arg)); + }); UI_block_emboss_set(&block, UI_EMBOSS); } -static void get_exec_time_other_nodes(const bNode &node, - const SpaceNode &snode, - std::chrono::microseconds &exec_time, - int &node_count) +static std::optional<std::chrono::nanoseconds> node_get_execution_time( + TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node) { - if (node.type == NODE_GROUP) { - const geo_log::TreeLog *root_tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context( - snode); - if (root_tree_log == nullptr) { - return; - } - const geo_log::TreeLog *tree_log = root_tree_log->lookup_child_log(node.name); - if (tree_log == nullptr) { - return; - } - tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { - exec_time += node_log.execution_time(); - node_count++; - }); + const geo_log::GeoTreeLog *tree_log = tree_draw_ctx.geo_tree_log; + if (tree_log == nullptr) { + return std::nullopt; } - else { - const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context( - snode, node); - if (node_log) { - exec_time += node_log->execution_time(); - node_count++; - } - } -} - -static std::chrono::microseconds node_get_execution_time(const bNodeTree &ntree, - const bNode &node, - const SpaceNode &snode, - int &node_count) -{ - std::chrono::microseconds exec_time = std::chrono::microseconds::zero(); if (node.type == NODE_GROUP_OUTPUT) { - const geo_log::TreeLog *tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context( - snode); - - if (tree_log == nullptr) { - return exec_time; - } - tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { - exec_time += node_log.execution_time(); - node_count++; - }); + return tree_log->run_time_sum; } - else if (node.type == NODE_FRAME) { + if (node.type == NODE_FRAME) { /* Could be cached in the future if this recursive code turns out to be slow. */ + std::chrono::nanoseconds run_time{0}; + bool found_node = false; LISTBASE_FOREACH (bNode *, tnode, &ntree.nodes) { if (tnode->parent != &node) { continue; } if (tnode->type == NODE_FRAME) { - exec_time += node_get_execution_time(ntree, *tnode, snode, node_count); + std::optional<std::chrono::nanoseconds> sub_frame_run_time = node_get_execution_time( + tree_draw_ctx, ntree, *tnode); + if (sub_frame_run_time.has_value()) { + run_time += *sub_frame_run_time; + found_node = true; + } } else { - get_exec_time_other_nodes(*tnode, snode, exec_time, node_count); + if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr_as(tnode->name)) { + found_node = true; + run_time += node_log->run_time; + } } } + if (found_node) { + return run_time; + } + return std::nullopt; } - else { - get_exec_time_other_nodes(node, snode, exec_time, node_count); + if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.name)) { + return node_log->run_time; } - return exec_time; + return std::nullopt; } -static std::string node_get_execution_time_label(const SpaceNode &snode, const bNode &node) +static std::string node_get_execution_time_label(TreeDrawContext &tree_draw_ctx, + const SpaceNode &snode, + const bNode &node) { - int node_count = 0; - std::chrono::microseconds exec_time = node_get_execution_time( - *snode.edittree, node, snode, node_count); + const std::optional<std::chrono::nanoseconds> exec_time = node_get_execution_time( + tree_draw_ctx, *snode.edittree, node); - if (node_count == 0) { + if (!exec_time.has_value()) { return std::string(""); } - uint64_t exec_time_us = exec_time.count(); + const uint64_t exec_time_us = + std::chrono::duration_cast<std::chrono::microseconds>(*exec_time).count(); /* Don't show time if execution time is 0 microseconds. */ if (exec_time_us == 0) { @@ -1763,7 +1770,7 @@ struct NodeExtraInfoRow { }; struct NamedAttributeTooltipArg { - Map<std::string, eNamedAttrUsage> usage_by_attribute; + Map<std::string, geo_log::NamedAttributeUsage> usage_by_attribute; }; static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char *UNUSED(tip)) @@ -1775,7 +1782,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char struct NameWithUsage { StringRefNull name; - eNamedAttrUsage usage; + geo_log::NamedAttributeUsage usage; }; Vector<NameWithUsage> sorted_used_attribute; @@ -1790,16 +1797,16 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char for (const NameWithUsage &attribute : sorted_used_attribute) { const StringRefNull name = attribute.name; - const eNamedAttrUsage usage = attribute.usage; + const geo_log::NamedAttributeUsage usage = attribute.usage; ss << " \u2022 \"" << name << "\": "; Vector<std::string> usages; - if ((usage & eNamedAttrUsage::Read) != eNamedAttrUsage::None) { + if ((usage & geo_log::NamedAttributeUsage::Read) != geo_log::NamedAttributeUsage::None) { usages.append(TIP_("read")); } - if ((usage & eNamedAttrUsage::Write) != eNamedAttrUsage::None) { + if ((usage & geo_log::NamedAttributeUsage::Write) != geo_log::NamedAttributeUsage::None) { usages.append(TIP_("write")); } - if ((usage & eNamedAttrUsage::Remove) != eNamedAttrUsage::None) { + if ((usage & geo_log::NamedAttributeUsage::Remove) != geo_log::NamedAttributeUsage::None) { usages.append(TIP_("remove")); } for (const int i : usages.index_range()) { @@ -1817,7 +1824,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char } static NodeExtraInfoRow row_from_used_named_attribute( - const Map<std::string, eNamedAttrUsage> &usage_by_attribute_name) + const Map<std::string, geo_log::NamedAttributeUsage> &usage_by_attribute_name) { const int attributes_num = usage_by_attribute_name.size(); @@ -1831,32 +1838,11 @@ static NodeExtraInfoRow row_from_used_named_attribute( return row; } -static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const SpaceNode &snode, - const bNode &node) +static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row( + TreeDrawContext &tree_draw_ctx, const bNode &node) { - if (node.type == NODE_GROUP) { - const geo_log::TreeLog *root_tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context( - snode); - if (root_tree_log == nullptr) { - return std::nullopt; - } - const geo_log::TreeLog *tree_log = root_tree_log->lookup_child_log(node.name); - if (tree_log == nullptr) { - return std::nullopt; - } - - Map<std::string, eNamedAttrUsage> usage_by_attribute; - tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { - for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) { - usage_by_attribute.lookup_or_add_as(used_attribute.name, - used_attribute.usage) |= used_attribute.usage; - } - }); - if (usage_by_attribute.is_empty()) { - return std::nullopt; - } - - return row_from_used_named_attribute(usage_by_attribute); + if (tree_draw_ctx.geo_tree_log == nullptr) { + return std::nullopt; } if (ELEM(node.type, GEO_NODE_STORE_NAMED_ATTRIBUTE, @@ -1865,31 +1851,26 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const Sp /* Only show the overlay when the name is passed in from somewhere else. */ LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { if (STREQ(socket->name, "Name")) { - if ((socket->flag & SOCK_IN_USE) == 0) { + if (!socket->is_directly_linked()) { return std::nullopt; } } } - const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context( - snode, node.name); - if (node_log == nullptr) { - return std::nullopt; - } - Map<std::string, eNamedAttrUsage> usage_by_attribute; - for (const geo_log::UsedNamedAttribute &used_attribute : node_log->used_named_attributes()) { - usage_by_attribute.lookup_or_add_as(used_attribute.name, - used_attribute.usage) |= used_attribute.usage; - } - if (usage_by_attribute.is_empty()) { - return std::nullopt; - } - return row_from_used_named_attribute(usage_by_attribute); } - - return std::nullopt; + tree_draw_ctx.geo_tree_log->ensure_used_named_attributes(); + geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.name); + if (node_log == nullptr) { + return std::nullopt; + } + if (node_log->used_named_attributes.is_empty()) { + return std::nullopt; + } + return row_from_used_named_attribute(node_log->used_named_attributes); } -static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, const bNode &node) +static Vector<NodeExtraInfoRow> node_get_extra_info(TreeDrawContext &tree_draw_ctx, + const SpaceNode &snode, + const bNode &node) { Vector<NodeExtraInfoRow> rows; if (!(snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS)) { @@ -1898,7 +1879,8 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons if (snode.overlay.flag & SN_OVERLAY_SHOW_NAMED_ATTRIBUTES && snode.edittree->type == NTREE_GEOMETRY) { - if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(snode, node)) { + if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(tree_draw_ctx, + node)) { rows.append(std::move(*row)); } } @@ -1907,7 +1889,7 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons (ELEM(node.typeinfo->nclass, NODE_CLASS_GEOMETRY, NODE_CLASS_GROUP, NODE_CLASS_ATTRIBUTE) || ELEM(node.type, NODE_FRAME, NODE_GROUP_OUTPUT))) { NodeExtraInfoRow row; - row.text = node_get_execution_time_label(snode, node); + row.text = node_get_execution_time_label(tree_draw_ctx, snode, node); if (!row.text.empty()) { row.tooltip = TIP_( "The execution time from the node tree's latest evaluation. For frame and group nodes, " @@ -1916,14 +1898,17 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons rows.append(std::move(row)); } } - const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(snode, - node); - if (node_log != nullptr) { - for (const std::string &message : node_log->debug_messages()) { - NodeExtraInfoRow row; - row.text = message; - row.icon = ICON_INFO; - rows.append(std::move(row)); + + if (snode.edittree->type == NTREE_GEOMETRY && tree_draw_ctx.geo_tree_log != nullptr) { + tree_draw_ctx.geo_tree_log->ensure_debug_messages(); + const geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.name); + if (node_log != nullptr) { + for (const StringRef message : node_log->debug_messages) { + NodeExtraInfoRow row; + row.text = message; + row.icon = ICON_INFO; + rows.append(std::move(row)); + } } } @@ -1988,9 +1973,12 @@ static void node_draw_extra_info_row(const bNode &node, } } -static void node_draw_extra_info_panel(const SpaceNode &snode, const bNode &node, uiBlock &block) +static void node_draw_extra_info_panel(TreeDrawContext &tree_draw_ctx, + const SpaceNode &snode, + const bNode &node, + uiBlock &block) { - Vector<NodeExtraInfoRow> extra_info_rows = node_get_extra_info(snode, node); + Vector<NodeExtraInfoRow> extra_info_rows = node_get_extra_info(tree_draw_ctx, snode, node); if (extra_info_rows.size() == 0) { return; @@ -2046,6 +2034,7 @@ static void node_draw_extra_info_panel(const SpaceNode &snode, const bNode &node } static void node_draw_basis(const bContext &C, + TreeDrawContext &tree_draw_ctx, const View2D &v2d, const SpaceNode &snode, bNodeTree &ntree, @@ -2070,7 +2059,7 @@ static void node_draw_basis(const bContext &C, GPU_line_width(1.0f); - node_draw_extra_info_panel(snode, node, block); + node_draw_extra_info_panel(tree_draw_ctx, snode, node, block); /* Header. */ { @@ -2165,7 +2154,7 @@ static void node_draw_basis(const bContext &C, UI_block_emboss_set(&block, UI_EMBOSS); } - node_add_error_message_button(C, node, block, rct, iconofs); + node_add_error_message_button(tree_draw_ctx, node, block, rct, iconofs); /* Title. */ if (node.flag & SELECT) { @@ -2338,6 +2327,7 @@ static void node_draw_basis(const bContext &C, } static void node_draw_hidden(const bContext &C, + TreeDrawContext &tree_draw_ctx, const View2D &v2d, const SpaceNode &snode, bNodeTree &ntree, @@ -2353,7 +2343,7 @@ static void node_draw_hidden(const bContext &C, const int color_id = node_get_colorid(node); - node_draw_extra_info_panel(snode, node, block); + node_draw_extra_info_panel(tree_draw_ctx, snode, node, block); /* Shadow. */ node_draw_shadow(snode, node, hiddenrad, 1.0f); @@ -2668,6 +2658,7 @@ static void reroute_node_prepare_for_draw(bNode &node) } static void node_update_nodetree(const bContext &C, + TreeDrawContext &tree_draw_ctx, bNodeTree &ntree, Span<bNode *> nodes, Span<uiBlock *> blocks) @@ -2694,7 +2685,7 @@ static void node_update_nodetree(const bContext &C, node_update_hidden(node, block); } else { - node_update_basis(C, ntree, node, block); + node_update_basis(C, tree_draw_ctx, ntree, node, block); } } } @@ -2795,6 +2786,7 @@ static void frame_node_draw_label(const bNodeTree &ntree, } static void frame_node_draw(const bContext &C, + TreeDrawContext &tree_draw_ctx, const ARegion ®ion, const SpaceNode &snode, bNodeTree &ntree, @@ -2841,7 +2833,7 @@ static void frame_node_draw(const bContext &C, /* label and text */ frame_node_draw_label(ntree, node, snode); - node_draw_extra_info_panel(snode, node, block); + node_draw_extra_info_panel(tree_draw_ctx, snode, node, block); UI_block_end(&C, &block); UI_block_draw(&C, &block); @@ -2895,6 +2887,7 @@ static void reroute_node_draw( } static void node_draw(const bContext &C, + TreeDrawContext &tree_draw_ctx, ARegion ®ion, const SpaceNode &snode, bNodeTree &ntree, @@ -2903,7 +2896,7 @@ static void node_draw(const bContext &C, bNodeInstanceKey key) { if (node.type == NODE_FRAME) { - frame_node_draw(C, region, snode, ntree, node, block); + frame_node_draw(C, tree_draw_ctx, region, snode, ntree, node, block); } else if (node.type == NODE_REROUTE) { reroute_node_draw(C, region, ntree, node, block); @@ -2911,10 +2904,10 @@ static void node_draw(const bContext &C, else { const View2D &v2d = region.v2d; if (node.flag & NODE_HIDDEN) { - node_draw_hidden(C, v2d, snode, ntree, node, block); + node_draw_hidden(C, tree_draw_ctx, v2d, snode, ntree, node, block); } else { - node_draw_basis(C, v2d, snode, ntree, node, block, key); + node_draw_basis(C, tree_draw_ctx, v2d, snode, ntree, node, block, key); } } } @@ -2922,6 +2915,7 @@ static void node_draw(const bContext &C, #define USE_DRAW_TOT_UPDATE static void node_draw_nodetree(const bContext &C, + TreeDrawContext &tree_draw_ctx, ARegion ®ion, SpaceNode &snode, bNodeTree &ntree, @@ -2946,7 +2940,7 @@ static void node_draw_nodetree(const bContext &C, } bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]); - node_draw(C, region, snode, ntree, *nodes[i], *blocks[i], key); + node_draw(C, tree_draw_ctx, region, snode, ntree, *nodes[i], *blocks[i], key); } /* Node lines. */ @@ -2976,7 +2970,7 @@ static void node_draw_nodetree(const bContext &C, } bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]); - node_draw(C, region, snode, ntree, *nodes[i], *blocks[i], key); + node_draw(C, tree_draw_ctx, region, snode, ntree, *nodes[i], *blocks[i], key); } } @@ -3035,8 +3029,17 @@ static void draw_nodetree(const bContext &C, Array<uiBlock *> blocks = node_uiblocks_init(C, nodes); - node_update_nodetree(C, ntree, nodes, blocks); - node_draw_nodetree(C, region, *snode, ntree, nodes, blocks, parent_key); + TreeDrawContext tree_draw_ctx; + if (ntree.type == NTREE_GEOMETRY) { + tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode); + if (tree_draw_ctx.geo_tree_log != nullptr) { + tree_draw_ctx.geo_tree_log->ensure_node_warnings(); + tree_draw_ctx.geo_tree_log->ensure_node_run_time(); + } + } + + node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks); + node_draw_nodetree(C, tree_draw_ctx, region, *snode, ntree, nodes, blocks, parent_key); } /** diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index e328a86b0fd..809c4b2fe59 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -14,6 +14,7 @@ #include "DNA_space_types.h" #include "BKE_context.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_object.h" @@ -30,12 +31,11 @@ #include "UI_interface.hh" #include "UI_resources.h" -#include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_geometry_nodes_log.hh" #include "node_intern.hh" -namespace geo_log = blender::nodes::geometry_nodes_eval_log; -using geo_log::GeometryAttributeInfo; +using blender::nodes::geo_eval_log::GeometryAttributeInfo; namespace blender::ed::space_node { @@ -50,6 +50,8 @@ BLI_STATIC_ASSERT(std::is_trivially_destructible_v<AttributeSearchData>, ""); static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context( const bContext &C, AttributeSearchData &data) { + using namespace nodes::geo_eval_log; + SpaceNode *snode = CTX_wm_space_node(&C); if (!snode) { BLI_assert_unreachable(); @@ -65,41 +67,48 @@ static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context( BLI_assert_unreachable(); return {}; } + GeoTreeLog *tree_log = GeoModifierLog::get_tree_log_for_node_editor(*snode); + if (tree_log == nullptr) { + return {}; + } + tree_log->ensure_socket_values(); /* For the attribute input node, collect attribute information from all nodes in the group. */ if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE) { - const geo_log::TreeLog *tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context( - *snode); - if (tree_log == nullptr) { - return {}; - } - + tree_log->ensure_existing_attributes(); Vector<const GeometryAttributeInfo *> attributes; - Set<StringRef> names; - tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { - for (const geo_log::SocketLog &socket_log : node_log.input_logs()) { - const geo_log::ValueLog *value_log = socket_log.value(); - if (const geo_log::GeometryValueLog *geo_value_log = - dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) { - for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) { - if (bke::allow_procedural_attribute_access(attribute.name)) { - if (names.add(attribute.name)) { - attributes.append(&attribute); - } - } - } - } + for (const GeometryAttributeInfo *attribute : tree_log->existing_attributes) { + if (bke::allow_procedural_attribute_access(attribute->name)) { + attributes.append(attribute); } - }); + } return attributes; } - - const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context( - *snode, data.node_name); + GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node->name); if (node_log == nullptr) { return {}; } - return node_log->lookup_available_attributes(); + Set<StringRef> names; + Vector<const GeometryAttributeInfo *> attributes; + for (const bNodeSocket *input_socket : node->input_sockets()) { + if (input_socket->type != SOCK_GEOMETRY) { + continue; + } + const ValueLog *value_log = tree_log->find_socket_value_log(*input_socket); + if (value_log == nullptr) { + continue; + } + if (const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(value_log)) { + for (const GeometryAttributeInfo &attribute : geo_log->attributes) { + if (bke::allow_procedural_attribute_access(attribute.name)) { + if (names.add(attribute.name)) { + attributes.append(&attribute); + } + } + } + } + } + return attributes; } static void attribute_search_update_fn( diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index 3b3189983e2..a208370a6f9 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -144,7 +144,7 @@ void ED_operatormacros_node() WM_operatortype_macro_define(ot, "NODE_OT_attach"); WM_operatortype_macro_define(ot, "NODE_OT_insert_offset"); - /* NODE_OT_translate_attach with remove_on_canel set to true */ + /* NODE_OT_translate_attach with remove_on_cancel set to true. */ ot = WM_operatortype_append_macro("NODE_OT_translate_attach_remove_on_cancel", "Move and Attach", "Move nodes and attach to frame", diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 40f5d20d06d..929fb64bd70 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -1728,7 +1728,8 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent bNodeTree &ntree = *snode.edittree; bNode *frame = node_find_frame_to_attach(region, ntree, event->mval); if (frame == nullptr) { - return OPERATOR_CANCELLED; + /* Return "finished" so that auto offset operator macros can work. */ + return OPERATOR_FINISHED; } LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) { diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 82aaa2c3cc6..d93b205b1b7 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -14,7 +14,6 @@ #include "BLI_lasso_2d.h" #include "BLI_listbase.h" #include "BLI_rect.h" -#include "BLI_set.hh" #include "BLI_string.h" #include "BLI_string_search.h" #include "BLI_string_utf8.h" diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 17fc02e98a8..fae3eb1a143 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -1021,7 +1021,7 @@ void ED_spacetype_node() ARegionType *art; st->spaceid = SPACE_NODE; - strncpy(st->name, "Node", BKE_ST_MAXNAME); + STRNCPY(st->name, "Node"); st->create = node_create; st->free = node_free; diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index a9ce8a52def..48e7aa381ef 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -377,10 +377,8 @@ void outliner_collection_delete( if (parent->flag & COLLECTION_IS_MASTER) { BLI_assert(parent->id.flag & LIB_EMBEDDED_DATA); - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); - BLI_assert(id_type->owner_get != nullptr); - - ID *scene_owner = id_type->owner_get(&parent->id); + ID *scene_owner = BKE_id_owner_get(&parent->id); + BLI_assert(scene_owner != nullptr); BLI_assert(GS(scene_owner->name) == ID_SCE); if (ID_IS_LINKED(scene_owner) || ID_IS_OVERRIDE_LIBRARY(scene_owner)) { skip = true; @@ -612,10 +610,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) else if (parent != nullptr && (parent->flag & COLLECTION_IS_MASTER) != 0) { BLI_assert(parent->id.flag & LIB_EMBEDDED_DATA); - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); - BLI_assert(id_type->owner_get != nullptr); - - Scene *scene_owner = (Scene *)id_type->owner_get(&parent->id); + Scene *scene_owner = reinterpret_cast<Scene *>(BKE_id_owner_get(&parent->id)); BLI_assert(scene_owner != nullptr); BLI_assert(GS(scene_owner->id.name) == ID_SCE); diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 6de0e5d55bd..912dc436a95 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -286,7 +286,7 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, else { BKE_view_layer_synced_ensure(scene, view_layer); Base *base_iter = BKE_view_layer_base_find(view_layer, ob_iter); - /* Child can be in a collection excluded from viewlayer. */ + /* Child can be in a collection excluded from view-layer. */ if (base_iter == nullptr) { continue; } @@ -3220,7 +3220,8 @@ static bool element_should_draw_faded(const TreeViewContext *tvc, const Base *base = (te->directdata) ? (const Base *)te->directdata : BKE_view_layer_base_find( (ViewLayer *)tvc->view_layer, (Object *)ob); - const bool is_visible = (base != nullptr) && (base->flag & BASE_VISIBLE_VIEWLAYER); + const bool is_visible = (base != nullptr) && + (base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT); if (!is_visible) { return true; } diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index 0c49602b5b8..15079448317 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -193,7 +193,8 @@ void outliner_item_mode_toggle(bContext *C, Base *base = BKE_view_layer_base_find(tvc->view_layer, ob); /* Hidden objects can be removed from the mode. */ - if (!base || (!(base->flag & BASE_VISIBLE_DEPSGRAPH) && (ob->mode != tvc->obact->mode))) { + if (!base || (!(base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) && + (ob->mode != tvc->obact->mode))) { return; } @@ -243,7 +244,7 @@ static void do_outliner_object_select_recursive(const Scene *scene, BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { Object *ob = base->object; - if ((((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0) && + if ((((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0) && BKE_object_is_child_recursive(ob_parent, ob))) { ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); } @@ -489,13 +490,13 @@ static void tree_element_posegroup_activate(bContext *C, TreeElement *te, TreeSt } static void tree_element_posechannel_activate(bContext *C, + const Scene *scene, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive) { - Scene *scene = CTX_data_scene(C); Object *ob = (Object *)tselem->id; bArmature *arm = static_cast<bArmature *>(ob->data); bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); @@ -597,13 +598,13 @@ static void tree_element_active_ebone__sel(bContext *C, bArmature *arm, EditBone WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, CTX_data_edit_object(C)); } static void tree_element_ebone_activate(bContext *C, + const Scene *scene, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive) { - Scene *scene = CTX_data_scene(C); bArmature *arm = (bArmature *)tselem->id; EditBone *ebone = static_cast<EditBone *>(te->directdata); @@ -663,6 +664,7 @@ static void tree_element_psys_activate(bContext *C, TreeStoreElem *tselem) } static void tree_element_constraint_activate(bContext *C, + const Scene *scene, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, @@ -675,7 +677,7 @@ static void tree_element_constraint_activate(bContext *C, while (te) { tselem = TREESTORE(te); if (tselem->type == TSE_POSE_CHANNEL) { - tree_element_posechannel_activate(C, view_layer, te, tselem, set, false); + tree_element_posechannel_activate(C, scene, view_layer, te, tselem, set, false); return; } te = te->parent; @@ -810,7 +812,7 @@ void tree_element_type_active_set(bContext *C, tree_element_bone_activate(C, tvc->scene, tvc->view_layer, te, tselem, set, recursive); break; case TSE_EBONE: - tree_element_ebone_activate(C, tvc->view_layer, te, tselem, set, recursive); + tree_element_ebone_activate(C, tvc->scene, tvc->view_layer, te, tselem, set, recursive); break; case TSE_MODIFIER: tree_element_modifier_activate(C, te, tselem, set); @@ -824,11 +826,12 @@ void tree_element_type_active_set(bContext *C, case TSE_POSE_BASE: return; case TSE_POSE_CHANNEL: - tree_element_posechannel_activate(C, tvc->view_layer, te, tselem, set, recursive); + tree_element_posechannel_activate( + C, tvc->scene, tvc->view_layer, te, tselem, set, recursive); break; case TSE_CONSTRAINT_BASE: case TSE_CONSTRAINT: - tree_element_constraint_activate(C, tvc->view_layer, te, tselem, set); + tree_element_constraint_activate(C, tvc->scene, tvc->view_layer, te, tselem, set); break; case TSE_R_LAYER: tree_element_viewlayer_activate(C, te); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index bfebd03769c..1628945c4cd 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -104,97 +104,96 @@ using blender::Vector; * \{ */ static void get_element_operation_type( - TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel) + const TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel) { - TreeStoreElem *tselem = TREESTORE(te); - if (tselem->flag & TSE_SELECTED) { - /* Layer collection points to collection ID. */ - if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) { - if (*datalevel == 0) { - *datalevel = tselem->type; - } - else if (*datalevel != tselem->type) { - *datalevel = -1; - } - } - else { - const int idcode = (int)GS(tselem->id->name); - bool is_standard_id = false; - switch ((ID_Type)idcode) { - case ID_SCE: - *scenelevel = 1; - break; - case ID_OB: - *objectlevel = 1; - break; + *scenelevel = *objectlevel = *idlevel = *datalevel = 0; - case ID_ME: - case ID_CU_LEGACY: - case ID_MB: - case ID_LT: - case ID_LA: - case ID_AR: - case ID_CA: - case ID_SPK: - case ID_MA: - case ID_TE: - case ID_IP: - case ID_IM: - case ID_SO: - case ID_KE: - case ID_WO: - case ID_AC: - case ID_TXT: - case ID_GR: - case ID_LS: - case ID_LI: - case ID_VF: - case ID_NT: - case ID_BR: - case ID_PA: - case ID_GD: - case ID_MC: - case ID_MSK: - case ID_PAL: - case ID_PC: - case ID_CF: - case ID_WS: - case ID_LP: - case ID_CV: - case ID_PT: - case ID_VO: - case ID_SIM: - is_standard_id = true; - break; - case ID_WM: - case ID_SCR: - /* Those are ignored here. */ - /* NOTE: while Screens should be manageable here, deleting a screen used by a workspace - * will cause crashes when trying to use that workspace, so for now let's play minimal, - * safe change. */ - break; - } - if (idcode == ID_NLA) { - /* Fake one, not an actual ID type... */ + const TreeStoreElem *tselem = TREESTORE(te); + if ((tselem->flag & TSE_SELECTED) == 0) { + return; + } + + /* Layer collection points to collection ID. */ + if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) { + *datalevel = tselem->type; + } + else { + const int idcode = (int)GS(tselem->id->name); + bool is_standard_id = false; + switch ((ID_Type)idcode) { + case ID_SCE: + *scenelevel = 1; + break; + case ID_OB: + *objectlevel = 1; + break; + + case ID_ME: + case ID_CU_LEGACY: + case ID_MB: + case ID_LT: + case ID_LA: + case ID_AR: + case ID_CA: + case ID_SPK: + case ID_MA: + case ID_TE: + case ID_IP: + case ID_IM: + case ID_SO: + case ID_KE: + case ID_WO: + case ID_AC: + case ID_TXT: + case ID_GR: + case ID_LS: + case ID_LI: + case ID_VF: + case ID_NT: + case ID_BR: + case ID_PA: + case ID_GD: + case ID_MC: + case ID_MSK: + case ID_PAL: + case ID_PC: + case ID_CF: + case ID_WS: + case ID_LP: + case ID_CV: + case ID_PT: + case ID_VO: + case ID_SIM: is_standard_id = true; - } + break; + case ID_WM: + case ID_SCR: + /* Those are ignored here. */ + /* NOTE: while Screens should be manageable here, deleting a screen used by a workspace + * will cause crashes when trying to use that workspace, so for now let's play minimal, + * safe change. */ + break; + } + if (idcode == ID_NLA) { + /* Fake one, not an actual ID type... */ + is_standard_id = true; + } - if (is_standard_id) { - if (*idlevel == 0) { - *idlevel = idcode; - } - else if (*idlevel != idcode) { - *idlevel = -1; - } - if (ELEM(*datalevel, TSE_VIEW_COLLECTION_BASE, TSE_SCENE_COLLECTION_BASE)) { - *datalevel = 0; - } - } + if (is_standard_id) { + *idlevel = idcode; } } + + /* Return values are exclusive, only one may be non-null. */ + BLI_assert(((*scenelevel != 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel == 0)) || + ((*scenelevel == 0) && (*objectlevel != 0) && (*idlevel == 0) && (*datalevel == 0)) || + ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel != 0) && (*datalevel == 0)) || + ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel != 0)) || + /* All null. */ + ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel == 0))); } -static TreeElement *get_target_element(SpaceOutliner *space_outliner) +static TreeElement *get_target_element(const SpaceOutliner *space_outliner) { TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE); @@ -1704,16 +1703,12 @@ static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; /* check for invalid states */ if (space_outliner == nullptr) { return OPERATOR_CANCELLED; } - TreeElement *te = get_target_element(space_outliner); - get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); - const eOutlinerLibOpSelectionSet selection_set = static_cast<eOutlinerLibOpSelectionSet>( RNA_enum_get(op->ptr, "selection_set")); const eOutlinerLibOverrideOpTypes event = static_cast<eOutlinerLibOverrideOpTypes>( @@ -2815,16 +2810,12 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; /* check for invalid states */ if (space_outliner == nullptr) { return OPERATOR_CANCELLED; } - TreeElement *te = get_target_element(space_outliner); - get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); - eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { case OL_LIB_DELETE: { @@ -3203,6 +3194,24 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot) /** \name Data Menu Operator * \{ */ +static bool outliner_data_operation_poll(bContext *C) +{ + if (!ED_operator_outliner_active(C)) { + return false; + } + const SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + const TreeElement *te = get_target_element(space_outliner); + int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); + return ELEM(datalevel, + TSE_POSE_CHANNEL, + TSE_BONE, + TSE_EBONE, + TSE_SEQUENCE, + TSE_GP_LAYER, + TSE_RNA_STRUCT); +} + static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -3313,7 +3322,7 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_data_operation_exec; - ot->poll = outliner_operation_tree_element_poll; + ot->poll = outliner_data_operation_poll; ot->flag = 0; @@ -3335,9 +3344,12 @@ static int outliner_operator_menu(bContext *C, const char *opname) /* set this so the default execution context is the same as submenus */ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN); - uiItemsEnumO(layout, ot->idname, RNA_property_identifier(ot->prop)); - uiItemS(layout); + if (WM_operator_poll(C, ot)) { + uiItemsEnumO(layout, ot->idname, RNA_property_identifier(ot->prop)); + + uiItemS(layout); + } uiItemMContents(layout, "OUTLINER_MT_context_menu"); @@ -3347,7 +3359,6 @@ static int outliner_operator_menu(bContext *C, const char *opname) } static int do_outliner_operation_event(bContext *C, - ReportList *reports, ARegion *region, SpaceOutliner *space_outliner, TreeElement *te) @@ -3370,10 +3381,6 @@ static int do_outliner_operation_event(bContext *C, get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); if (scenelevel) { - if (objectlevel || datalevel || idlevel) { - BKE_report(reports, RPT_WARNING, "Mixed selection"); - return OPERATOR_CANCELLED; - } return outliner_operator_menu(C, "OUTLINER_OT_scene_operation"); } if (objectlevel) { @@ -3381,11 +3388,6 @@ static int do_outliner_operation_event(bContext *C, return OPERATOR_FINISHED; } if (idlevel) { - if (idlevel == -1 || datalevel) { - BKE_report(reports, RPT_WARNING, "Mixed selection"); - return OPERATOR_CANCELLED; - } - switch (idlevel) { case ID_GR: WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN); @@ -3400,10 +3402,6 @@ static int do_outliner_operation_event(bContext *C, } } else if (datalevel) { - if (datalevel == -1) { - BKE_report(reports, RPT_WARNING, "Mixed selection"); - return OPERATOR_CANCELLED; - } if (datalevel == TSE_ANIM_DATA) { return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation"); } @@ -3435,7 +3433,7 @@ static int do_outliner_operation_event(bContext *C, return OPERATOR_CANCELLED; } -static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event) +static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { ARegion *region = CTX_wm_region(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -3456,7 +3454,7 @@ static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_PASS_THROUGH; } - return do_outliner_operation_event(C, op->reports, region, space_outliner, hovered_te); + return do_outliner_operation_event(C, region, space_outliner, hovered_te); } void OUTLINER_OT_operation(wmOperatorType *ot) diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index e3b5e3ab3f3..8a2ff8c2ece 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -1461,7 +1461,7 @@ static bool outliner_element_visible_get(const Scene *scene, bool is_visible = true; if (exclude_filter & SO_FILTER_OB_STATE_VISIBLE) { - if ((base->flag & BASE_VISIBLE_VIEWLAYER) == 0) { + if ((base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) { is_visible = false; } } diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index 76b7197b86a..365bcae3f5d 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -445,7 +445,7 @@ void ED_spacetype_outliner(void) ARegionType *art; st->spaceid = SPACE_OUTLINER; - strncpy(st->name, "Outliner", BKE_ST_MAXNAME); + STRNCPY(st->name, "Outliner"); st->create = outliner_create; st->free = outliner_free; diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c index a623b98f1b1..c35b1e00184 100644 --- a/source/blender/editors/space_script/space_script.c +++ b/source/blender/editors/space_script/space_script.c @@ -152,7 +152,7 @@ void ED_spacetype_script(void) ARegionType *art; st->spaceid = SPACE_SCRIPT; - strncpy(st->name, "Script", BKE_ST_MAXNAME); + STRNCPY(st->name, "Script"); st->create = script_create; st->free = script_free; diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 201f132052d..538cfad14f5 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -997,7 +997,7 @@ void ED_spacetype_sequencer(void) ARegionType *art; st->spaceid = SPACE_SEQ; - strncpy(st->name, "Sequencer", BKE_ST_MAXNAME); + STRNCPY(st->name, "Sequencer"); st->create = sequencer_create; st->free = sequencer_free; diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 5c0f69905fa..435436611c5 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -619,7 +619,7 @@ void ED_spacetype_spreadsheet() ARegionType *art; st->spaceid = SPACE_SPREADSHEET; - strncpy(st->name, "Spreadsheet", BKE_ST_MAXNAME); + STRNCPY(st->name, "Spreadsheet"); st->create = spreadsheet_create; st->free = spreadsheet_free; 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 61782186402..4703eacdcb9 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -4,6 +4,7 @@ #include "BLI_virtual_array.hh" #include "BKE_attribute.hh" +#include "BKE_compute_contexts.hh" #include "BKE_context.h" #include "BKE_curves.hh" #include "BKE_editmesh.h" @@ -26,7 +27,8 @@ #include "ED_curves_sculpt.h" #include "ED_spreadsheet.h" -#include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_geometry_nodes_lazy_function.hh" +#include "NOD_geometry_nodes_log.hh" #include "BLT_translation.h" @@ -40,8 +42,8 @@ #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" -namespace geo_log = blender::nodes::geometry_nodes_eval_log; using blender::fn::GField; +using blender::nodes::geo_eval_log::ViewerNodeLog; namespace blender::ed::spreadsheet { @@ -276,7 +278,7 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c BLI_assert(object_eval_->mode == OB_MODE_EDIT); Object *object_orig = DEG_get_original_object(object_eval_); const Mesh *mesh_eval = geometry_set_.get_mesh_for_read(); - const bke::AttributeAccessor attributes_eval = bke::mesh_attributes(*mesh_eval); + const bke::AttributeAccessor attributes_eval = mesh_eval->attributes(); Mesh *mesh_orig = (Mesh *)object_orig->data; BMesh *bm = mesh_orig->edit_mesh->bm; BM_mesh_elem_table_ensure(bm, BM_VERT); @@ -465,19 +467,10 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread } } else { - const geo_log::NodeLog *node_log = - geo_log::ModifierLog::find_node_by_spreadsheet_editor_context(*sspreadsheet); - if (node_log != nullptr) { - for (const geo_log::SocketLog &input_log : node_log->input_logs()) { - if (const geo_log::GeometryValueLog *geo_value_log = - dynamic_cast<const geo_log::GeometryValueLog *>(input_log.value())) { - const GeometrySet *full_geometry = geo_value_log->full_geometry(); - if (full_geometry != nullptr) { - geometry_set = *full_geometry; - break; - } - } - } + if (const ViewerNodeLog *viewer_log = + nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet( + *sspreadsheet)) { + geometry_set = viewer_log->geometry; } } } @@ -495,27 +488,11 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, /* No viewer is currently referenced by the context path. */ return; } - const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context( - *sspreadsheet); - if (node_log == nullptr) { - return; - } - for (const geo_log::SocketLog &socket_log : node_log->input_logs()) { - const geo_log::ValueLog *value_log = socket_log.value(); - if (value_log == nullptr) { - continue; - } - if (const geo_log::GFieldValueLog *field_value_log = - dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) { - const GField &field = field_value_log->field(); - if (field) { - r_fields.add("Viewer", std::move(field)); - } - } - if (const geo_log::GenericValueLog *generic_value_log = - dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { - GPointer value = generic_value_log->value(); - r_fields.add("Viewer", fn::make_constant_field(*value.type(), value.get())); + if (const ViewerNodeLog *viewer_log = + nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet( + *sspreadsheet)) { + if (viewer_log->field) { + r_fields.add("Viewer", viewer_log->field); } } } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index 6806e185cfe..03cf0116dce 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -71,6 +71,14 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, } } } + else if (column_data.type().is<bool>()) { + const bool value = (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) != 0; + apply_filter_operation( + column_data.typed<bool>(), + [&](const bool cell) { return cell == value; }, + prev_mask, + new_indices); + } else if (column_data.type().is<int8_t>()) { const int value = row_filter.value_int; switch (row_filter.operation) { @@ -274,7 +282,6 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, } else if (column_data.type().is<InstanceReference>()) { const StringRef value = row_filter.value_string; - apply_filter_operation( column_data.typed<InstanceReference>(), [&](const InstanceReference cell) { diff --git a/source/blender/editors/space_statusbar/space_statusbar.c b/source/blender/editors/space_statusbar/space_statusbar.c index 9c64235870c..e99e8f21364 100644 --- a/source/blender/editors/space_statusbar/space_statusbar.c +++ b/source/blender/editors/space_statusbar/space_statusbar.c @@ -136,7 +136,7 @@ void ED_spacetype_statusbar(void) ARegionType *art; st->spaceid = SPACE_STATUSBAR; - strncpy(st->name, "Status Bar", BKE_ST_MAXNAME); + STRNCPY(st->name, "Status Bar"); st->create = statusbar_create; st->free = statusbar_free; diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 45cf557c4b4..be9bbdf109e 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -403,7 +403,7 @@ void ED_spacetype_text(void) ARegionType *art; st->spaceid = SPACE_TEXT; - strncpy(st->name, "Text", BKE_ST_MAXNAME); + STRNCPY(st->name, "Text"); st->create = text_create; st->free = text_free; diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 0f0ecb0e4bf..46c459dd0bc 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -706,7 +706,7 @@ static void text_update_drawcache(SpaceText *st, ARegion *region) drawcache->showlinenrs = st->showlinenrs; drawcache->tabnumber = st->tabnumber; - strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME); + STRNCPY(drawcache->text_id, txt->id.name); /* clear update flag */ drawcache->update_flag = 0; diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index ee0e0c3ef46..e4826ed5964 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -288,7 +288,7 @@ void ED_spacetype_topbar(void) ARegionType *art; st->spaceid = SPACE_TOPBAR; - strncpy(st->name, "Top Bar", BKE_ST_MAXNAME); + STRNCPY(st->name, "Top Bar"); st->create = topbar_create; st->free = topbar_free; diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c index 1cda9cc0f0c..06a4c1d8702 100644 --- a/source/blender/editors/space_userpref/space_userpref.c +++ b/source/blender/editors/space_userpref/space_userpref.c @@ -189,7 +189,7 @@ void ED_spacetype_userpref(void) ARegionType *art; st->spaceid = SPACE_USERPREF; - strncpy(st->name, "Userpref", BKE_ST_MAXNAME); + STRNCPY(st->name, "Userpref"); st->create = userpref_create; st->free = userpref_free; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a44f19b69e2..1c5cb475721 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1899,7 +1899,8 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (base) { Object *ob = base->object; /* if hidden but in edit mode, we still display, can happen with animation */ - if ((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0 || (ob->mode != OB_MODE_OBJECT)) { + if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0 || + (ob->mode != OB_MODE_OBJECT)) { CTX_data_id_pointer_set(result, &ob->id); } } @@ -1978,7 +1979,7 @@ void ED_spacetype_view3d(void) ARegionType *art; st->spaceid = SPACE_VIEW3D; - strncpy(st->name, "View3D", BKE_ST_MAXNAME); + STRNCPY(st->name, "View3D"); st->create = view3d_create; st->free = view3d_free; diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 07c83d8decc..04824097e05 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -995,7 +995,9 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float if (apply_vcos || median->bv_weight || median->v_crease || median->skin[0] || median->skin[1]) { if (median->bv_weight) { - BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT); + if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { + BM_data_layer_add(bm, &bm->vdata, CD_BWEIGHT); + } cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); BLI_assert(cd_vert_bweight_offset != -1); @@ -1061,7 +1063,9 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float if (median->be_weight || median->e_crease) { if (median->be_weight) { - BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT); + if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { + BM_data_layer_add(bm, &bm->edata, CD_BWEIGHT); + } cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); BLI_assert(cd_edge_bweight_offset != -1); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index dc070d1370c..e0939b714a0 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2155,7 +2155,7 @@ static void validate_object_select_id(struct Depsgraph *depsgraph, return; } - if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0)) { + if (obact_eval && ((obact_eval->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0)) { BKE_view_layer_synced_ensure(scene, view_layer); Base *base = BKE_view_layer_base_find(view_layer, obact); DRW_select_buffer_context_create(&base, 1, -1); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index 9688fdd9380..d0f6ca4c922 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -125,7 +125,7 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int }; { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); BKE_view_layer_synced_ensure(scene, view_layer); @@ -354,7 +354,7 @@ static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const }; { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); BKE_view_layer_synced_ensure(scene, view_layer); @@ -494,7 +494,7 @@ void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C, Base **r_base, BMElem **r_ele) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int object_index = RNA_int_get(gz->ptr, "object_index"); diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index cd6597f5c5d..b27c65c42ef 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -865,7 +865,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) RegionView3D *rv3d = CTX_wm_region_view3d(C); Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + const Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); BKE_view_layer_synced_ensure(scene_eval, view_layer_eval); Object *ob_eval = BKE_view_layer_active_object_get(view_layer_eval); @@ -944,14 +944,14 @@ static int viewselected_exec(bContext *C, wmOperator *op) else if (obedit) { /* only selected */ FOREACH_OBJECT_IN_MODE_BEGIN ( - scene, view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) { + scene_eval, view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) { ok |= ED_view3d_minmax_verts(ob_eval_iter, min, max); } FOREACH_OBJECT_IN_MODE_END; } else if (ob_eval && (ob_eval->mode & OB_MODE_POSE)) { FOREACH_OBJECT_IN_MODE_BEGIN ( - scene, view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) { + scene_eval, view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) { ok |= BKE_pose_minmax(ob_eval_iter, min, max, true, true); } FOREACH_OBJECT_IN_MODE_END; diff --git a/source/blender/editors/space_view3d/view3d_select.cc b/source/blender/editors/space_view3d/view3d_select.cc index 1182cb8875f..e76696f31cf 100644 --- a/source/blender/editors/space_view3d/view3d_select.cc +++ b/source/blender/editors/space_view3d/view3d_select.cc @@ -367,22 +367,22 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me, EditSelectBuf_Cache *esel, const eSelectOp sel_op) { - MPoly *polygons = BKE_mesh_polys_for_write(me); + MPoly *polys = BKE_mesh_polys_for_write(me); bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; - if (polygons) { + if (polys) { const bool *hide_poly = (const bool *)CustomData_get_layer_named( &me->vdata, CD_PROP_BOOL, ".hide_poly"); for (int index = 0; index < me->totpoly; index++) { if (!(hide_poly && hide_poly[index])) { - const bool is_select = polygons[index].flag & ME_FACE_SEL; + const bool is_select = polys[index].flag & ME_FACE_SEL; const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { - SET_FLAG_FROM_TEST(polygons[index].flag, sel_op_result, ME_FACE_SEL); + SET_FLAG_FROM_TEST(polys[index].flag, sel_op_result, ME_FACE_SEL); changed = true; } } diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index ad261fc6513..d0db4de0c47 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -825,7 +825,7 @@ static bool view3d_localview_init(const Depsgraph *depsgraph, wmWindowManager *wm, wmWindow *win, Main *bmain, - Scene *scene, + const Scene *scene, ViewLayer *view_layer, ScrArea *area, const bool frame_selected, @@ -1280,9 +1280,8 @@ void ED_view3d_local_collections_reset(struct bContext *C, const bool reset_all) else if (reset_all && (do_reset || (local_view_bit != ~(0)))) { view3d_local_collections_reset(bmain, ~(0)); View3D v3d = {.local_collections_uuid = ~(0)}; - Scene *scene = CTX_data_scene(C); - BKE_layer_collection_local_sync(scene, CTX_data_view_layer(C), &v3d); - DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); + BKE_layer_collection_local_sync(CTX_data_scene(C), CTX_data_view_layer(C), &v3d); + DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS); } } diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index e287ef91224..8c6f2baf84a 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -904,18 +904,18 @@ static void special_aftertrans_update__actedit(bContext *C, TransInfo *t) if (ELEM(t->frame_side, 'L', 'R')) { /* TFM_TIME_EXTEND */ /* same as below */ ED_markers_post_apply_transform( - ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); + ED_context_get_markers(C), t->scene, t->mode, t->values_final[0], t->frame_side); } else /* TFM_TIME_TRANSLATE */ #endif { ED_markers_post_apply_transform( - ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); + ED_context_get_markers(C), t->scene, t->mode, t->values_final[0], t->frame_side); } } else if (t->mode == TFM_TIME_SCALE) { ED_markers_post_apply_transform( - ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); + ED_context_get_markers(C), t->scene, t->mode, t->values_final[0], t->frame_side); } } diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c index becf3c7ce5a..b1627e62f8c 100644 --- a/source/blender/editors/transform/transform_convert_mesh_edge.c +++ b/source/blender/editors/transform/transform_convert_mesh_edge.c @@ -67,7 +67,9 @@ static void createTransEdge(bContext *UNUSED(C), TransInfo *t) /* create data we need */ if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_EDGE_BWEIGHT); + if (!CustomData_has_layer(&em->bm->edata, CD_BWEIGHT)) { + BM_data_layer_add(em->bm, &em->bm->edata, CD_BWEIGHT); + } cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_BWEIGHT); } else { /* if (t->mode == TFM_EDGE_CREASE) { */ diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index f3bef2c283b..27f12137e3a 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -265,7 +265,7 @@ static void createTransUVs(bContext *C, TransInfo *t) /* count */ if (is_island_center) { /* create element map with island information */ - elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true); + elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true, true); if (elementmap == NULL) { continue; } diff --git a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c index f05688f3325..39705f87a0d 100644 --- a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c +++ b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c @@ -84,7 +84,9 @@ static void createTransMeshVertCData(bContext *UNUSED(C), TransInfo *t) int cd_offset = -1; if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT); + if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { + BM_data_layer_add(bm, &bm->vdata, CD_BWEIGHT); + } cd_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); } else { diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 507c7f7e411..caa11fa5db4 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -356,7 +356,7 @@ static void set_trans_object_base_flags(TransInfo *t) return; } /* Makes sure base flags and object flags are identical. */ - BKE_scene_base_flag_to_objects(scene, t->view_layer); + BKE_scene_base_flag_to_objects(t->scene, t->view_layer); /* Make sure depsgraph is here. */ DEG_graph_relations_update(depsgraph); /* Clear all flags we need. It will be used to detect dependencies. */ diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index eefc9d0cc2a..ddc99caeef5 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -708,12 +708,12 @@ static void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo if (t->mode == TFM_SEQ_SLIDE) { if (t->frame_side == 'B') { ED_markers_post_apply_transform( - &t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values[0], t->frame_side); + &t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values_final[0], t->frame_side); } } else if (ELEM(t->frame_side, 'L', 'R')) { ED_markers_post_apply_transform( - &t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values[0], t->frame_side); + &t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values_final[0], t->frame_side); } } } diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 640e89a3966..a9e6adc6e60 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -82,7 +82,6 @@ set(SRC ../include/ED_transform.h ../include/ED_transform_snap_object_context.h ../include/ED_transverts.h - ../include/ED_types.h ../include/ED_undo.h ../include/ED_userpref.h ../include/ED_util.h diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 30c58f0ec05..2eeeacf694b 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -374,7 +374,7 @@ void unpack_menu(bContext *C, char local_name[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX]; BLI_split_file_part(abs_name, fi, sizeof(fi)); - BLI_snprintf(local_name, sizeof(local_name), "//%s/%s", folder, fi); + BLI_path_join(local_name, sizeof(local_name), "//", folder, fi, NULL); if (!STREQ(abs_name, local_name)) { switch (BKE_packedfile_compare_to_file(blendfile_path, local_name, pf)) { case PF_CMP_NOFILE: diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 795e212fb0c..5e2d9097abd 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -535,7 +535,7 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool) return false; } - UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true); + UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true, true); if (element_map == NULL) { return false; } diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 00c3b494d43..6c8fb9360bd 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2665,7 +2665,7 @@ static bool uv_mouse_select_multi(bContext *C, } static bool uv_mouse_select(bContext *C, const float co[2], const struct SelectPick_Params *params) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -2818,7 +2818,7 @@ static int uv_mouse_select_loop_generic(bContext *C, const bool extend, enum eUVLoopGenericType loop_type) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); 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( @@ -5386,7 +5386,7 @@ static void uv_isolate_selected_islands(const Scene *scene, BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0); BMFace *efa; BMIter iter, liter; - UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true); + UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true, true); if (elementmap == NULL) { return; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index f56c63f47b5..05b98ab9627 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1855,7 +1855,7 @@ static StitchState *stitch_init(bContext *C, * for stitch this isn't useful behavior, see T86924. */ const int selectmode_orig = scene->toolsettings->selectmode; scene->toolsettings->selectmode = SCE_SELECT_VERTEX; - state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true); + state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true, true); scene->toolsettings->selectmode = selectmode_orig; if (!state->element_map) { diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index ab0e54eedbb..1fe9b5b6c78 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -2688,7 +2688,7 @@ void UV_OT_project_from_view(wmOperatorType *ot) static int reset_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index b01c04471ae..c4a633e920e 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -505,9 +505,8 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id) FrsMaterial tmpMat; - const blender::VArray<int> material_indices = - blender::bke::mesh_attributes(*me).lookup_or_default<int>( - "material_index", ATTR_DOMAIN_FACE, 0); + const blender::VArray<int> material_indices = me->attributes().lookup_or_default<int>( + "material_index", ATTR_DOMAIN_FACE, 0); // We parse the vlak nodes again and import meshes while applying the clipping // by the near and far view planes. diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 152b68b7fb1..a3085768ea3 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -577,7 +577,7 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex) mesh->totloop = group->totloop; mesh->totcol = group->materials.size(); - MVert *vertices = (MVert *)CustomData_add_layer( + MVert *verts = (MVert *)CustomData_add_layer( &mesh->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, mesh->totvert); MEdge *edges = (MEdge *)CustomData_add_layer( &mesh->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, mesh->totedge); @@ -664,19 +664,19 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex) else { if (!visible) { // first vertex - vertices->co[0] = svRep[0]->point2d()[0]; - vertices->co[1] = svRep[0]->point2d()[1]; - vertices->co[2] = get_stroke_vertex_z(); + verts->co[0] = svRep[0]->point2d()[0]; + verts->co[1] = svRep[0]->point2d()[1]; + verts->co[2] = get_stroke_vertex_z(); - ++vertices; + ++verts; ++vertex_index; // second vertex - vertices->co[0] = svRep[1]->point2d()[0]; - vertices->co[1] = svRep[1]->point2d()[1]; - vertices->co[2] = get_stroke_vertex_z(); + verts->co[0] = svRep[1]->point2d()[0]; + verts->co[1] = svRep[1]->point2d()[1]; + verts->co[2] = get_stroke_vertex_z(); - ++vertices; + ++verts; ++vertex_index; // first edge @@ -688,10 +688,10 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex) visible = true; // vertex - vertices->co[0] = svRep[2]->point2d()[0]; - vertices->co[1] = svRep[2]->point2d()[1]; - vertices->co[2] = get_stroke_vertex_z(); - ++vertices; + verts->co[0] = svRep[2]->point2d()[0]; + verts->co[1] = svRep[2]->point2d()[1]; + verts->co[2] = get_stroke_vertex_z(); + ++verts; ++vertex_index; // edges diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index f1298a7f5b7..3d153813425 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -13,6 +13,10 @@ set(INC_SYS set(SRC intern/cpp_types.cc intern/field.cc + intern/lazy_function.cc + intern/lazy_function_execute.cc + intern/lazy_function_graph.cc + intern/lazy_function_graph_executor.cc intern/multi_function.cc intern/multi_function_builder.cc intern/multi_function_params.cc @@ -23,6 +27,10 @@ set(SRC FN_field.hh FN_field_cpp_type.hh + FN_lazy_function.hh + FN_lazy_function_execute.hh + FN_lazy_function_graph.hh + FN_lazy_function_graph_executor.hh FN_multi_function.hh FN_multi_function_builder.hh FN_multi_function_context.hh @@ -61,6 +69,7 @@ blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/FN_field_test.cc + tests/FN_lazy_function_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 bc42cab8db5..ca12f407e49 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -565,6 +565,17 @@ template<typename T> struct ValueOrField { } return this->value; } + + friend std::ostream &operator<<(std::ostream &stream, const ValueOrField<T> &value_or_field) + { + if (value_or_field.field) { + stream << "ValueOrField<T>"; + } + else { + stream << value_or_field.value; + } + return stream; + } }; /** \} */ diff --git a/source/blender/functions/FN_field_cpp_type.hh b/source/blender/functions/FN_field_cpp_type.hh index 63a648f3202..6900a093dc6 100644 --- a/source/blender/functions/FN_field_cpp_type.hh +++ b/source/blender/functions/FN_field_cpp_type.hh @@ -59,7 +59,7 @@ class ValueOrFieldCPPType : public CPPType { public: template<typename T> ValueOrFieldCPPType(FieldCPPTypeParam<ValueOrField<T>> /* unused */, StringRef debug_name) - : CPPType(CPPTypeParam<ValueOrField<T>, CPPTypeFlags::None>(), debug_name), + : CPPType(CPPTypeParam<ValueOrField<T>, CPPTypeFlags::Printable>(), debug_name), base_type_(CPPType::get<T>()) { construct_from_value_ = [](void *dst, const void *value_or_field) { diff --git a/source/blender/functions/FN_lazy_function.hh b/source/blender/functions/FN_lazy_function.hh new file mode 100644 index 00000000000..59a3a90b0b0 --- /dev/null +++ b/source/blender/functions/FN_lazy_function.hh @@ -0,0 +1,384 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup fn + * + * A `LazyFunction` encapsulates a computation which has inputs, outputs and potentially side + * effects. Most importantly, a `LazyFunction` supports laziness in its inputs and outputs: + * - Only outputs that are actually used have to be computed. + * - Inputs can be requested lazily based on which outputs are used or what side effects the + * function has. + * + * A lazy-function that uses laziness may be executed more than once. The most common example is + * the geometry nodes switch node. Depending on a condition input, it decides which one of the + * other inputs is actually used. From the perspective of the switch node, its execution works as + * follows: + * 1. The switch node is first executed. It sees that the output is used. Now it requests the + * condition input from the caller and exits. + * 2. Once the caller is able to provide the condition input the switch node is executed again. + * This time it retrieves the condition and requests one of the other inputs. Then the node + * exits again, giving back control to the caller. + * 3. When the caller computed the second requested input the switch node executes a last time. + * This time it retrieves the new input and forwards it to the output. + * + * In some sense, a lazy-function can be thought of like a state machine. Every time it is + * executed, it advances its state until all required outputs are ready. + * + * The lazy-function interface is designed to support composition of many such functions into a new + * lazy-functions, all while keeping the laziness working. For example, in geometry nodes a switch + * node in a node group should still be able to decide whether a node in the parent group will be + * executed or not. This is essential to avoid doing unnecessary work. + * + * The lazy-function system consists of multiple core components: + * - The interface of a lazy-function itself including its calling convention. + * - A graph data structure that allows composing many lazy-functions by connecting their inputs + * and outputs. + * - An executor that allows multi-threaded execution or such a graph. + */ + +#include "BLI_cpp_type.hh" +#include "BLI_generic_pointer.hh" +#include "BLI_linear_allocator.hh" +#include "BLI_vector.hh" + +namespace blender::fn::lazy_function { + +enum class ValueUsage { + /** + * The value is definitely used and therefore has to be computed. + */ + Used, + /** + * It's unknown whether this value will be used or not. Computing it is ok but the result may be + * discarded. + */ + Maybe, + /** + * The value will definitely not be used. It can still be computed but the result will be + * discarded in all cases. + */ + Unused, +}; + +class LazyFunction; + +/** + * This allows passing arbitrary data into a lazy-function during execution. For that, #UserData + * has to be subclassed. This mainly exists because it's more type safe than passing a `void *` + * with no type information attached. + * + * Some lazy-functions may expect to find a certain type of user data when executed. + */ +class UserData { + public: + virtual ~UserData() = default; +}; + +/** + * Passed to the lazy-function when it is executed. + */ +struct Context { + /** + * If the lazy-function has some state (which only makes sense when it is executed more than once + * to finish its job), the state is stored here. This points to memory returned from + * #LazyFunction::init_storage. + */ + void *storage; + /** + * Custom user data that can be used in the function. + */ + UserData *user_data; +}; + +/** + * Defines the calling convention for a lazy-function. During execution, a lazy-function retrieves + * its inputs and sets the outputs through #Params. + */ +class Params { + public: + /** + * The lazy-function this #Params has been prepared for. + */ + const LazyFunction &fn_; + + public: + Params(const LazyFunction &fn); + + /** + * Get a pointer to an input value if the value is available already. Otherwise null is returned. + * + * The #LazyFunction must leave returned object in an initialized state, but can move from it. + */ + void *try_get_input_data_ptr(int index) const; + + /** + * Same as #try_get_input_data_ptr, but if the data is not yet available, request it. This makes + * sure that the data will be available in a future execution of the #LazyFunction. + */ + void *try_get_input_data_ptr_or_request(int index); + + /** + * Get a pointer to where the output value should be stored. + * The value at the pointer is in an uninitialized state at first. + * The #LazyFunction is responsible for initializing the value. + * After the output has been initialized to its final value, #output_set has to be called. + */ + void *get_output_data_ptr(int index); + + /** + * Call this after the output value is initialized. After this is called, the value must not be + * touched anymore. It may be moved or destructed immediately. + */ + void output_set(int index); + + /** + * Allows the #LazyFunction to check whether an output was computed already without keeping + * track of it itself. + */ + bool output_was_set(int index) const; + + /** + * Can be used to detect which outputs have to be computed. + */ + ValueUsage get_output_usage(int index) const; + + /** + * Tell the caller of the #LazyFunction that a specific input will definitely not be used. + * Only an input that was not #ValueUsage::Used can become unused. + */ + void set_input_unused(int index); + + /** + * Typed utility methods that wrap the methods above. + */ + template<typename T> T extract_input(int index); + template<typename T> const T &get_input(int index); + template<typename T> T *try_get_input_data_ptr_or_request(int index); + template<typename T> void set_output(int index, T &&value); + + /** + * Utility to initialize all outputs that haven't been set yet. + */ + void set_default_remaining_outputs(); + + private: + /** + * Methods that need to be implemented by subclasses. Those are separate from the non-virtual + * methods above to make it easy to insert additional debugging logic on top of the + * implementations. + */ + virtual void *try_get_input_data_ptr_impl(int index) const = 0; + virtual void *try_get_input_data_ptr_or_request_impl(int index) = 0; + virtual void *get_output_data_ptr_impl(int index) = 0; + virtual void output_set_impl(int index) = 0; + virtual bool output_was_set_impl(int index) const = 0; + virtual ValueUsage get_output_usage_impl(int index) const = 0; + virtual void set_input_unused_impl(int index) = 0; +}; + +/** + * Describes an input of a #LazyFunction. + */ +struct Input { + /** + * Name used for debugging purposes. The string has to be static or has to be owned by something + * else. + */ + const char *debug_name; + /** + * Data type of this input. + */ + const CPPType *type; + /** + * Can be used to indicate a caller or this function if this input is used statically before + * executing it the first time. This is technically not needed but can improve efficiency because + * a round-trip through the `execute` method can be avoided. + * + * When this is #ValueUsage::Used, the caller has to ensure that the input is definitely + * available when the #execute method is first called. The #execute method does not have to check + * whether the value is actually available. + */ + ValueUsage usage; + + Input(const char *debug_name, const CPPType &type, const ValueUsage usage = ValueUsage::Used) + : debug_name(debug_name), type(&type), usage(usage) + { + } +}; + +struct Output { + /** + * Name used for debugging purposes. The string has to be static or has to be owned by something + * else. + */ + const char *debug_name; + /** + * Data type of this output. + */ + const CPPType *type = nullptr; + + Output(const char *debug_name, const CPPType &type) : debug_name(debug_name), type(&type) + { + } +}; + +/** + * A function that can compute outputs and request inputs lazily. For more details see the comment + * at the top of the file. + */ +class LazyFunction { + protected: + const char *debug_name_ = "<unknown>"; + Vector<Input> inputs_; + Vector<Output> outputs_; + + public: + virtual ~LazyFunction() = default; + + /** + * Get a name of the function or an input or output. This is mainly used for debugging. + * These are virtual functions because the names are often not used outside of debugging + * workflows. This way the names are only generated when they are actually needed. + */ + virtual std::string name() const; + virtual std::string input_name(int index) const; + virtual std::string output_name(int index) const; + + /** + * Allocates storage for this function. The storage will be passed to every call to #execute. + * If the function does not keep track of any state, this does not have to be implemented. + */ + virtual void *init_storage(LinearAllocator<> &allocator) const; + + /** + * Destruct the storage created in #init_storage. + */ + virtual void destruct_storage(void *storage) const; + + /** + * Inputs of the function. + */ + Span<Input> inputs() const; + /** + * Outputs of the function. + */ + Span<Output> outputs() const; + + /** + * During execution the function retrieves inputs and sets outputs in #params. For some + * functions, this method is called more than once. After execution, the function either has + * computed all required outputs or is waiting for more inputs. + */ + void execute(Params ¶ms, const Context &context) const; + + /** + * Utility to check that the guarantee by #Input::usage is followed. + */ + bool always_used_inputs_available(const Params ¶ms) const; + + private: + /** + * Needs to be implemented by subclasses. This is separate from #execute so that additional + * debugging logic can be implemented in #execute. + */ + virtual void execute_impl(Params ¶ms, const Context &context) const = 0; +}; + +/* -------------------------------------------------------------------- */ +/** \name #LazyFunction Inline Methods + * \{ */ + +inline Span<Input> LazyFunction::inputs() const +{ + return inputs_; +} + +inline Span<Output> LazyFunction::outputs() const +{ + return outputs_; +} + +inline void LazyFunction::execute(Params ¶ms, const Context &context) const +{ + BLI_assert(this->always_used_inputs_available(params)); + this->execute_impl(params, context); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Params Inline Methods + * \{ */ + +inline Params::Params(const LazyFunction &fn) : fn_(fn) +{ +} + +inline void *Params::try_get_input_data_ptr(const int index) const +{ + return this->try_get_input_data_ptr_impl(index); +} + +inline void *Params::try_get_input_data_ptr_or_request(const int index) +{ + return this->try_get_input_data_ptr_or_request_impl(index); +} + +inline void *Params::get_output_data_ptr(const int index) +{ + return this->get_output_data_ptr_impl(index); +} + +inline void Params::output_set(const int index) +{ + this->output_set_impl(index); +} + +inline bool Params::output_was_set(const int index) const +{ + return this->output_was_set_impl(index); +} + +inline ValueUsage Params::get_output_usage(const int index) const +{ + return this->get_output_usage_impl(index); +} + +inline void Params::set_input_unused(const int index) +{ + this->set_input_unused_impl(index); +} + +template<typename T> inline T Params::extract_input(const int index) +{ + void *data = this->try_get_input_data_ptr(index); + BLI_assert(data != nullptr); + T return_value = std::move(*static_cast<T *>(data)); + return return_value; +} + +template<typename T> inline const T &Params::get_input(const int index) +{ + const void *data = this->try_get_input_data_ptr(index); + BLI_assert(data != nullptr); + return *static_cast<const T *>(data); +} + +template<typename T> inline T *Params::try_get_input_data_ptr_or_request(const int index) +{ + return static_cast<T *>(this->try_get_input_data_ptr_or_request(index)); +} + +template<typename T> inline void Params::set_output(const int index, T &&value) +{ + using DecayT = std::decay_t<T>; + void *data = this->get_output_data_ptr(index); + new (data) DecayT(std::forward<T>(value)); + this->output_set(index); +} + +/** \} */ + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/FN_lazy_function_execute.hh b/source/blender/functions/FN_lazy_function_execute.hh new file mode 100644 index 00000000000..a59d363a9d5 --- /dev/null +++ b/source/blender/functions/FN_lazy_function_execute.hh @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup fn + * + * This file contains common utilities for actually executing a lazy-function. + */ + +#include "BLI_parameter_pack_utils.hh" + +#include "FN_lazy_function.hh" + +namespace blender::fn::lazy_function { + +/** + * Most basic implementation of #Params. It does not actually implement any logic for how to + * retrieve inputs or set outputs. Instead, code using #BasicParams has to implement that. + */ +class BasicParams : public Params { + private: + const Span<GMutablePointer> inputs_; + const Span<GMutablePointer> outputs_; + MutableSpan<std::optional<ValueUsage>> input_usages_; + Span<ValueUsage> output_usages_; + MutableSpan<bool> set_outputs_; + + public: + BasicParams(const LazyFunction &fn, + const Span<GMutablePointer> inputs, + const Span<GMutablePointer> outputs, + MutableSpan<std::optional<ValueUsage>> input_usages, + Span<ValueUsage> output_usages, + MutableSpan<bool> set_outputs); + + void *try_get_input_data_ptr_impl(const int index) const override; + void *try_get_input_data_ptr_or_request_impl(const int index) override; + void *get_output_data_ptr_impl(const int index) override; + void output_set_impl(const int index) override; + bool output_was_set_impl(const int index) const override; + ValueUsage get_output_usage_impl(const int index) const override; + void set_input_unused_impl(const int index) override; +}; + +namespace detail { + +/** + * Utility to implement #execute_lazy_function_eagerly. + */ +template<typename... Inputs, typename... Outputs, size_t... InIndices, size_t... OutIndices> +inline void execute_lazy_function_eagerly_impl( + const LazyFunction &fn, + UserData *user_data, + std::tuple<Inputs...> &inputs, + std::tuple<Outputs *...> &outputs, + std::index_sequence<InIndices...> /* in_indices */, + std::index_sequence<OutIndices...> /* out_indices */) +{ + constexpr size_t InputsNum = sizeof...(Inputs); + constexpr size_t OutputsNum = sizeof...(Outputs); + std::array<GMutablePointer, InputsNum> input_pointers; + std::array<GMutablePointer, OutputsNum> output_pointers; + std::array<std::optional<ValueUsage>, InputsNum> input_usages; + std::array<ValueUsage, OutputsNum> output_usages; + std::array<bool, OutputsNum> set_outputs; + ( + [&]() { + constexpr size_t I = InIndices; + using T = Inputs; + const CPPType &type = CPPType::get<T>(); + input_pointers[I] = {type, &std::get<I>(inputs)}; + }(), + ...); + ( + [&]() { + constexpr size_t I = OutIndices; + using T = Outputs; + const CPPType &type = CPPType::get<T>(); + output_pointers[I] = {type, std::get<I>(outputs)}; + }(), + ...); + output_usages.fill(ValueUsage::Used); + set_outputs.fill(false); + LinearAllocator<> allocator; + Context context; + context.user_data = user_data; + context.storage = fn.init_storage(allocator); + BasicParams params{ + fn, input_pointers, output_pointers, input_usages, output_usages, set_outputs}; + fn.execute(params, context); + fn.destruct_storage(context.storage); +} + +} // namespace detail + +/** + * In some cases (mainly for tests), the set of inputs and outputs for a lazy-function is known at + * compile time and one just wants to compute the outputs based on the inputs, without any + * laziness. + * + * This function does exactly that. It takes all inputs in a tuple and writes the outputs to points + * provided in a second tuple. Since all inputs have to be provided, the lazy-function has to + * compute all outputs. + */ +template<typename... Inputs, typename... Outputs> +inline void execute_lazy_function_eagerly(const LazyFunction &fn, + UserData *user_data, + std::tuple<Inputs...> inputs, + std::tuple<Outputs *...> outputs) +{ + BLI_assert(fn.inputs().size() == sizeof...(Inputs)); + BLI_assert(fn.outputs().size() == sizeof...(Outputs)); + detail::execute_lazy_function_eagerly_impl(fn, + user_data, + inputs, + outputs, + std::make_index_sequence<sizeof...(Inputs)>(), + std::make_index_sequence<sizeof...(Outputs)>()); +} + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/FN_lazy_function_graph.hh b/source/blender/functions/FN_lazy_function_graph.hh new file mode 100644 index 00000000000..4ede28c4f26 --- /dev/null +++ b/source/blender/functions/FN_lazy_function_graph.hh @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup fn + * + * This file contains a graph data structure that allows composing multiple lazy-functions into a + * combined lazy-function. + * + * There are two types of nodes in the graph: + * - #FunctionNode: Corresponds to a #LazyFunction. The inputs and outputs of the function become + * input and output sockets of the node. + * - #DummyNode: Is used to indicate inputs and outputs of the entire graph. It can have an + * arbitrary number of sockets. + */ + +#include "BLI_linear_allocator.hh" + +#include "FN_lazy_function.hh" + +namespace blender::fn::lazy_function { + +class Socket; +class InputSocket; +class OutputSocket; +class Node; +class Graph; + +/** + * A #Socket is the interface of a #Node. Every #Socket is either an #InputSocket or #OutputSocket. + * Links can be created from output sockets to input sockets. + */ +class Socket : NonCopyable, NonMovable { + protected: + /** + * The node the socket belongs to. + */ + Node *node_; + /** + * Data type of the socket. Only sockets with the same type can be linked. + */ + const CPPType *type_; + /** + * Indicates whether this is an #InputSocket or #OutputSocket. + */ + bool is_input_; + /** + * Index of the socket. E.g. 0 for the first input and the first output socket. + */ + int index_in_node_; + + friend Graph; + + public: + bool is_input() const; + bool is_output() const; + + int index() const; + + InputSocket &as_input(); + OutputSocket &as_output(); + const InputSocket &as_input() const; + const OutputSocket &as_output() const; + + const Node &node() const; + Node &node(); + + const CPPType &type() const; + + std::string name() const; +}; + +class InputSocket : public Socket { + private: + /** + * An input can have at most one link connected to it. The linked socket is the "origin" because + * it's where the data is coming from. The type of the origin must be the same as the type of + * this socket. + */ + OutputSocket *origin_; + /** + * Can be null or a non-owning pointer to a value of the type of the socket. This value will be + * used when the input is used but not linked. + * + * This is technically not needed, because one could just create a separate node that just + * outputs the value, but that would have more overhead. Especially because it's commonly the + * case that most inputs are unlinked. + */ + const void *default_value_ = nullptr; + + friend Graph; + + public: + OutputSocket *origin(); + const OutputSocket *origin() const; + + const void *default_value() const; + void set_default_value(const void *value); +}; + +class OutputSocket : public Socket { + private: + /** + * An output can be linked to an arbitrary number of inputs of the same type. + */ + Vector<InputSocket *> targets_; + + friend Graph; + + public: + Span<InputSocket *> targets(); + Span<const InputSocket *> targets() const; +}; + +/** + * A #Node has input and output sockets. Every node is either a #FunctionNode or a #DummyNode. + */ +class Node : NonCopyable, NonMovable { + protected: + /** + * The function this node corresponds to. If this is null, the node is a #DummyNode. + * The function is not owned by this #Node nor by the #Graph. + */ + const LazyFunction *fn_ = nullptr; + /** + * Input sockets of the node. + */ + Span<InputSocket *> inputs_; + /** + * Output sockets of the node. + */ + Span<OutputSocket *> outputs_; + /** + * An index that is set when calling #Graph::update_node_indices. This can be used to create + * efficient mappings from nodes to other data using just an array instead of a hash map. + * + * This is technically not necessary but has better performance than always using hash maps. + */ + int index_in_graph_ = -1; + + friend Graph; + + public: + bool is_dummy() const; + bool is_function() const; + int index_in_graph() const; + + Span<const InputSocket *> inputs() const; + Span<const OutputSocket *> outputs() const; + Span<InputSocket *> inputs(); + Span<OutputSocket *> outputs(); + + const InputSocket &input(int index) const; + const OutputSocket &output(int index) const; + InputSocket &input(int index); + OutputSocket &output(int index); + + std::string name() const; +}; + +/** + * A #Node that corresponds to a specific #LazyFunction. + */ +class FunctionNode : public Node { + public: + const LazyFunction &function() const; +}; + +/** + * A #Node that does *not* correspond to a #LazyFunction. Instead it can be used to indicate inputs + * and outputs of the entire graph. It can have an arbitrary number of inputs and outputs. + */ +class DummyNode : public Node { + private: + std::string name_; + + friend Node; +}; + +/** + * A container for an arbitrary number of nodes and links between their sockets. + */ +class Graph : NonCopyable, NonMovable { + private: + /** + * Used to allocate nodes and sockets in the graph. + */ + LinearAllocator<> allocator_; + /** + * Contains all nodes in the graph so that it is efficient to iterate over them. + */ + Vector<Node *> nodes_; + + public: + ~Graph(); + + /** + * Get all nodes in the graph. The index in the span corresponds to #Node::index_in_graph. + */ + Span<const Node *> nodes() const; + + /** + * Add a new function node with sockets that match the passed in #LazyFunction. + */ + FunctionNode &add_function(const LazyFunction &fn); + + /** + * Add a new dummy node with the given socket types. + */ + DummyNode &add_dummy(Span<const CPPType *> input_types, Span<const CPPType *> output_types); + + /** + * Add a link between the two given sockets. + * This has undefined behavior when the input is linked to something else already. + */ + void add_link(OutputSocket &from, InputSocket &to); + + /** + * Make sure that #Node::index_in_graph is up to date. + */ + void update_node_indices(); + + /** + * Can be used to assert that #update_node_indices has been called. + */ + bool node_indices_are_valid() const; + + /** + * Utility to generate a dot graph string for the graph. This can be used for debugging. + */ + std::string to_dot() const; +}; + +/* -------------------------------------------------------------------- */ +/** \name #Socket Inline Methods + * \{ */ + +inline bool Socket::is_input() const +{ + return is_input_; +} + +inline bool Socket::is_output() const +{ + return !is_input_; +} + +inline int Socket::index() const +{ + return index_in_node_; +} + +inline InputSocket &Socket::as_input() +{ + BLI_assert(this->is_input()); + return *static_cast<InputSocket *>(this); +} + +inline OutputSocket &Socket::as_output() +{ + BLI_assert(this->is_output()); + return *static_cast<OutputSocket *>(this); +} + +inline const InputSocket &Socket::as_input() const +{ + BLI_assert(this->is_input()); + return *static_cast<const InputSocket *>(this); +} + +inline const OutputSocket &Socket::as_output() const +{ + BLI_assert(this->is_output()); + return *static_cast<const OutputSocket *>(this); +} + +inline const Node &Socket::node() const +{ + return *node_; +} + +inline Node &Socket::node() +{ + return *node_; +} + +inline const CPPType &Socket::type() const +{ + return *type_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #InputSocket Inline Methods + * \{ */ + +inline const OutputSocket *InputSocket::origin() const +{ + return origin_; +} + +inline OutputSocket *InputSocket::origin() +{ + return origin_; +} + +inline const void *InputSocket::default_value() const +{ + return default_value_; +} + +inline void InputSocket::set_default_value(const void *value) +{ + default_value_ = value; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #OutputSocket Inline Methods + * \{ */ + +inline Span<const InputSocket *> OutputSocket::targets() const +{ + return targets_; +} + +inline Span<InputSocket *> OutputSocket::targets() +{ + return targets_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Node Inline Methods + * \{ */ + +inline bool Node::is_dummy() const +{ + return fn_ == nullptr; +} + +inline bool Node::is_function() const +{ + return fn_ != nullptr; +} + +inline int Node::index_in_graph() const +{ + return index_in_graph_; +} + +inline Span<const InputSocket *> Node::inputs() const +{ + return inputs_; +} + +inline Span<const OutputSocket *> Node::outputs() const +{ + return outputs_; +} + +inline Span<InputSocket *> Node::inputs() +{ + return inputs_; +} + +inline Span<OutputSocket *> Node::outputs() +{ + return outputs_; +} + +inline const InputSocket &Node::input(const int index) const +{ + return *inputs_[index]; +} + +inline const OutputSocket &Node::output(const int index) const +{ + return *outputs_[index]; +} + +inline InputSocket &Node::input(const int index) +{ + return *inputs_[index]; +} + +inline OutputSocket &Node::output(const int index) +{ + return *outputs_[index]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FunctionNode Inline Methods + * \{ */ + +inline const LazyFunction &FunctionNode::function() const +{ + BLI_assert(fn_ != nullptr); + return *fn_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Graph Inline Methods + * \{ */ + +inline Span<const Node *> Graph::nodes() const +{ + return nodes_; +} + +/** \} */ + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/FN_lazy_function_graph_executor.hh b/source/blender/functions/FN_lazy_function_graph_executor.hh new file mode 100644 index 00000000000..a6ae5cac967 --- /dev/null +++ b/source/blender/functions/FN_lazy_function_graph_executor.hh @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup fn + * + * This file provides means to create a #LazyFunction from #Graph (which could then e.g. be used in + * another #Graph again). + */ + +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "FN_lazy_function_graph.hh" + +namespace blender::fn::lazy_function { + +/** + * Can be implemented to log values produced during graph evaluation. + */ +class GraphExecutorLogger { + public: + virtual ~GraphExecutorLogger() = default; + + virtual void log_socket_value(const Socket &socket, + GPointer value, + const Context &context) const; + + virtual void log_before_node_execute(const FunctionNode &node, + const Params ¶ms, + const Context &context) const; + + virtual void log_after_node_execute(const FunctionNode &node, + const Params ¶ms, + const Context &context) const; + + virtual void dump_when_outputs_are_missing(const FunctionNode &node, + Span<const OutputSocket *> missing_sockets, + const Context &context) const; + virtual void dump_when_input_is_set_twice(const InputSocket &target_socket, + const OutputSocket &from_socket, + const Context &context) const; +}; + +/** + * Has to be implemented when some of the nodes in the graph may have side effects. The + * #GraphExecutor has to know about that to make sure that these nodes will be executed even though + * their outputs are not needed. + */ +class GraphExecutorSideEffectProvider { + public: + virtual ~GraphExecutorSideEffectProvider() = default; + virtual Vector<const FunctionNode *> get_nodes_with_side_effects(const Context &context) const; +}; + +class GraphExecutor : public LazyFunction { + public: + using Logger = GraphExecutorLogger; + using SideEffectProvider = GraphExecutorSideEffectProvider; + + private: + /** + * The graph that is evaluated. + */ + const Graph &graph_; + /** + * Input and output sockets of the entire graph. + */ + VectorSet<const OutputSocket *> graph_inputs_; + VectorSet<const InputSocket *> graph_outputs_; + /** + * Optional logger for events that happen during execution. + */ + const Logger *logger_; + /** + * Optional side effect provider. It knows which nodes have side effects based on the context + * during evaluation. + */ + const SideEffectProvider *side_effect_provider_; + + friend class Executor; + + public: + GraphExecutor(const Graph &graph, + Span<const OutputSocket *> graph_inputs, + Span<const InputSocket *> graph_outputs, + const Logger *logger, + const SideEffectProvider *side_effect_provider); + + void *init_storage(LinearAllocator<> &allocator) const override; + void destruct_storage(void *storage) const override; + + private: + void execute_impl(Params ¶ms, const Context &context) const override; +}; + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh index 015df179ef0..accbaf899be 100644 --- a/source/blender/functions/FN_multi_function.hh +++ b/source/blender/functions/FN_multi_function.hh @@ -157,6 +157,7 @@ namespace multi_function_types { using fn::MFContext; using fn::MFContextBuilder; using fn::MFDataType; +using fn::MFParamCategory; using fn::MFParams; using fn::MFParamsBuilder; using fn::MFParamType; diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 5c43fffdd61..f046da30994 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -16,3 +16,6 @@ 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); +BLI_CPP_TYPE_MAKE(StringValueOrFieldVector, + blender::Vector<blender::fn::ValueOrField<std::string>>, + CPPTypeFlags::None); diff --git a/source/blender/functions/intern/lazy_function.cc b/source/blender/functions/intern/lazy_function.cc new file mode 100644 index 00000000000..46572283e9b --- /dev/null +++ b/source/blender/functions/intern/lazy_function.cc @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup fn + */ + +#include "BLI_array.hh" + +#include "FN_lazy_function.hh" + +namespace blender::fn::lazy_function { + +std::string LazyFunction::name() const +{ + return debug_name_; +} + +std::string LazyFunction::input_name(int index) const +{ + return inputs_[index].debug_name; +} + +std::string LazyFunction::output_name(int index) const +{ + return outputs_[index].debug_name; +} + +void *LazyFunction::init_storage(LinearAllocator<> &UNUSED(allocator)) const +{ + return nullptr; +} + +void LazyFunction::destruct_storage(void *storage) const +{ + BLI_assert(storage == nullptr); + UNUSED_VARS_NDEBUG(storage); +} + +bool LazyFunction::always_used_inputs_available(const Params ¶ms) const +{ + for (const int i : inputs_.index_range()) { + const Input &fn_input = inputs_[i]; + if (fn_input.usage == ValueUsage::Used) { + if (params.try_get_input_data_ptr(i) == nullptr) { + return false; + } + } + } + return true; +} + +void Params::set_default_remaining_outputs() +{ + for (const int i : fn_.outputs().index_range()) { + if (this->output_was_set(i)) { + continue; + } + const Output &fn_output = fn_.outputs()[i]; + const CPPType &type = *fn_output.type; + void *data_ptr = this->get_output_data_ptr(i); + type.value_initialize(data_ptr); + this->output_set(i); + } +} + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/intern/lazy_function_execute.cc b/source/blender/functions/intern/lazy_function_execute.cc new file mode 100644 index 00000000000..279056afa99 --- /dev/null +++ b/source/blender/functions/intern/lazy_function_execute.cc @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup fn + */ + +#include "FN_lazy_function_execute.hh" + +namespace blender::fn::lazy_function { + +BasicParams::BasicParams(const LazyFunction &fn, + const Span<GMutablePointer> inputs, + const Span<GMutablePointer> outputs, + MutableSpan<std::optional<ValueUsage>> input_usages, + Span<ValueUsage> output_usages, + MutableSpan<bool> set_outputs) + : Params(fn), + inputs_(inputs), + outputs_(outputs), + input_usages_(input_usages), + output_usages_(output_usages), + set_outputs_(set_outputs) +{ +} + +void *BasicParams::try_get_input_data_ptr_impl(const int index) const +{ + return inputs_[index].get(); +} + +void *BasicParams::try_get_input_data_ptr_or_request_impl(const int index) +{ + void *value = inputs_[index].get(); + if (value == nullptr) { + input_usages_[index] = ValueUsage::Used; + } + return value; +} + +void *BasicParams::get_output_data_ptr_impl(const int index) +{ + return outputs_[index].get(); +} + +void BasicParams::output_set_impl(const int index) +{ + set_outputs_[index] = true; +} + +bool BasicParams::output_was_set_impl(const int index) const +{ + return set_outputs_[index]; +} + +ValueUsage BasicParams::get_output_usage_impl(const int index) const +{ + return output_usages_[index]; +} + +void BasicParams::set_input_unused_impl(const int index) +{ + input_usages_[index] = ValueUsage::Unused; +} + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/intern/lazy_function_graph.cc b/source/blender/functions/intern/lazy_function_graph.cc new file mode 100644 index 00000000000..cc55b70d166 --- /dev/null +++ b/source/blender/functions/intern/lazy_function_graph.cc @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_dot_export.hh" + +#include "FN_lazy_function_graph.hh" + +namespace blender::fn::lazy_function { + +Graph::~Graph() +{ + for (Node *node : nodes_) { + for (InputSocket *socket : node->inputs_) { + std::destroy_at(socket); + } + for (OutputSocket *socket : node->outputs_) { + std::destroy_at(socket); + } + std::destroy_at(node); + } +} + +FunctionNode &Graph::add_function(const LazyFunction &fn) +{ + const Span<Input> inputs = fn.inputs(); + const Span<Output> outputs = fn.outputs(); + + FunctionNode &node = *allocator_.construct<FunctionNode>().release(); + node.fn_ = &fn; + node.inputs_ = allocator_.construct_elements_and_pointer_array<InputSocket>(inputs.size()); + node.outputs_ = allocator_.construct_elements_and_pointer_array<OutputSocket>(outputs.size()); + + for (const int i : inputs.index_range()) { + InputSocket &socket = *node.inputs_[i]; + socket.index_in_node_ = i; + socket.is_input_ = true; + socket.node_ = &node; + socket.type_ = inputs[i].type; + } + for (const int i : outputs.index_range()) { + OutputSocket &socket = *node.outputs_[i]; + socket.index_in_node_ = i; + socket.is_input_ = false; + socket.node_ = &node; + socket.type_ = outputs[i].type; + } + + nodes_.append(&node); + return node; +} + +DummyNode &Graph::add_dummy(Span<const CPPType *> input_types, Span<const CPPType *> output_types) +{ + DummyNode &node = *allocator_.construct<DummyNode>().release(); + node.fn_ = nullptr; + node.inputs_ = allocator_.construct_elements_and_pointer_array<InputSocket>(input_types.size()); + node.outputs_ = allocator_.construct_elements_and_pointer_array<OutputSocket>( + output_types.size()); + + for (const int i : input_types.index_range()) { + InputSocket &socket = *node.inputs_[i]; + socket.index_in_node_ = i; + socket.is_input_ = true; + socket.node_ = &node; + socket.type_ = input_types[i]; + } + for (const int i : output_types.index_range()) { + OutputSocket &socket = *node.outputs_[i]; + socket.index_in_node_ = i; + socket.is_input_ = false; + socket.node_ = &node; + socket.type_ = output_types[i]; + } + + nodes_.append(&node); + return node; +} + +void Graph::add_link(OutputSocket &from, InputSocket &to) +{ + BLI_assert(to.origin_ == nullptr); + BLI_assert(from.type_ == to.type_); + to.origin_ = &from; + from.targets_.append(&to); +} + +void Graph::update_node_indices() +{ + for (const int i : nodes_.index_range()) { + nodes_[i]->index_in_graph_ = i; + } +} + +bool Graph::node_indices_are_valid() const +{ + for (const int i : nodes_.index_range()) { + if (nodes_[i]->index_in_graph_ != i) { + return false; + } + } + return true; +} + +std::string Socket::name() const +{ + if (node_->is_function()) { + const FunctionNode &fn_node = static_cast<const FunctionNode &>(*node_); + const LazyFunction &fn = fn_node.function(); + if (is_input_) { + return fn.input_name(index_in_node_); + } + return fn.output_name(index_in_node_); + } + return "Unnamed"; +} + +std::string Node::name() const +{ + if (fn_ == nullptr) { + return static_cast<const DummyNode *>(this)->name_; + } + return fn_->name(); +} + +std::string Graph::to_dot() const +{ + dot::DirectedGraph digraph; + digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); + + Map<const Node *, dot::NodeWithSocketsRef> dot_nodes; + + for (const Node *node : nodes_) { + dot::Node &dot_node = digraph.new_node(""); + if (node->is_dummy()) { + dot_node.set_background_color("lightblue"); + } + else { + dot_node.set_background_color("white"); + } + + Vector<std::string> input_names; + Vector<std::string> output_names; + for (const InputSocket *socket : node->inputs()) { + input_names.append(socket->name()); + } + for (const OutputSocket *socket : node->outputs()) { + output_names.append(socket->name()); + } + + dot_nodes.add_new(node, + dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names)); + } + + for (const Node *node : nodes_) { + for (const InputSocket *socket : node->inputs()) { + const dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&socket->node()); + const dot::NodePort to_dot_port = to_dot_node.input(socket->index()); + + if (const OutputSocket *origin = socket->origin()) { + dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&origin->node()); + digraph.new_edge(from_dot_node.output(origin->index()), to_dot_port); + } + else if (const void *default_value = socket->default_value()) { + const CPPType &type = socket->type(); + std::string value_string; + if (type.is_printable()) { + value_string = type.to_string(default_value); + } + else { + value_string = "<" + type.name() + ">"; + } + dot::Node &default_value_dot_node = digraph.new_node(value_string); + default_value_dot_node.set_shape(dot::Attr_shape::Ellipse); + digraph.new_edge(default_value_dot_node, to_dot_port); + } + } + } + + return digraph.to_dot_string(); +} + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/intern/lazy_function_graph_executor.cc b/source/blender/functions/intern/lazy_function_graph_executor.cc new file mode 100644 index 00000000000..176509bd687 --- /dev/null +++ b/source/blender/functions/intern/lazy_function_graph_executor.cc @@ -0,0 +1,1163 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * This file implements the evaluation of a lazy-function graph. It's main objectives are: + * - Only compute values that are actually used. + * - Allow spreading the work over an arbitrary number of CPU cores. + * + * Other (simpler) executors with different main objectives could be implemented in the future. For + * some scenarios those could be simpler when many nodes do very little work or most nodes have to + * be processed sequentially. Those assumptions make the first and second objective less important + * respectively. + * + * The design implemented in this executor requires *no* main thread that coordinates everything. + * Instead, one thread will trigger some initial work and then many threads coordinate themselves + * in a distributed fashion. In an ideal situation, every thread ends up processing a separate part + * of the graph which results in less communication overhead. The way TBB schedules tasks helps + * with that: a thread will next process the task that it added to a task pool just before. + * + * Communication between threads is synchronized by using a mutex in every node. When a thread + * wants to access the state of a node, its mutex has to be locked first (with some documented + * exceptions). The assumption here is that most nodes are only ever touched by a single thread and + * therefore the lock contention is reduced the more nodes there are. + * + * Similar to how a #LazyFunction can be thought of as a state machine (see `FN_lazy_function.hh`), + * each node can also be thought of as a state machine. The state of a node contains the evaluation + * state of its inputs and outputs. Every time a node is executed, it has to advance its state in + * some way (e.g. it requests a new input or computes a new output). + * + * At the core of the executor is a task pool. Every task in that pool represents a node execution. + * When a node is executed it may send notifications to other nodes which may in turn add those + * nodes to the task pool. For example, the current node has computed one of its outputs, then the + * computed value is forwarded to all linked inputs, changing their node states in the process. If + * this input was the last missing required input, the node will be added to the task pool so that + * it is executed next. + * + * When the task pool is empty, the executor gives back control to the caller which may later + * provide new inputs to the graph which in turn adds new nodes to the task pool and the process + * starts again. + */ + +#include <mutex> + +#include "BLI_compute_context.hh" +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_function_ref.hh" +#include "BLI_task.h" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "FN_lazy_function_graph_executor.hh" + +namespace blender::fn::lazy_function { + +enum class NodeScheduleState { + /** + * Default state of every node. + */ + NotScheduled, + /** + * The node has been added to the task pool or is otherwise scheduled to be executed in the + * future. + */ + Scheduled, + /** + * The node is currently running. + */ + Running, + /** + * The node is running and has been rescheduled while running. In this case the node run again. + * This state exists, because we don't want to add the node to the task pool twice, because then + * the node might run twice at the same time, which is not allowed. Instead, once the node is + * done running, it will reschedule itself. + */ + RunningAndRescheduled, +}; + +struct InputState { + /** + * Value of this input socket. By default, the value is empty. When other nodes are done + * computing their outputs, the computed values will be forwarded to linked input sockets. The + * value will then live here until it is found that it is not needed anymore. + * + * If #was_ready_for_execution is true, access does not require holding the node lock. + */ + void *value = nullptr; + /** + * How the node intends to use this input. By default, all inputs may be used. Based on which + * outputs are used, a node can decide that an input will definitely be used or is never used. + * This allows freeing values early and avoids unnecessary computations. + */ + ValueUsage usage = ValueUsage::Maybe; + /** + * Set to true once #value is set and will stay true afterwards. Access during execution of a + * node, does not require holding the node lock. + */ + bool was_ready_for_execution = false; +}; + +struct OutputState { + /** + * Keeps track of how the output value is used. If a connected input becomes used, this output + * has to become used as well. The output becomes unused when it is used by no input socket + * anymore and it's not an output of the graph. + */ + ValueUsage usage = ValueUsage::Maybe; + /** + * This is a copy of #usage that is done right before node execution starts. This is done so that + * the node gets a consistent view of what outputs are used, even when this changes while the + * node is running (the node might be reevaluated in that case). Access during execution of a + * node, does not require holding the node lock. + */ + ValueUsage usage_for_execution = ValueUsage::Maybe; + /** + * Number of linked sockets that might still use the value of this output. + */ + int potential_target_sockets = 0; + /** + * Is set to true once the output has been computed and then stays true. Access does not require + * holding the node lock. + */ + bool has_been_computed = false; + /** + * Holds the output value for a short period of time while the node is initializing it and before + * it's forwarded to input sockets. Access does not require holding the node lock. + */ + void *value = nullptr; +}; + +struct NodeState { + /** + * Needs to be locked when any data in this state is accessed that is not explicitly marked as + * not needing the lock. + */ + mutable std::mutex mutex; + /** + * States of the individual input and output sockets. One can index into these arrays without + * locking. However, to access data inside, a lock is needed unless noted otherwise. + */ + MutableSpan<InputState> inputs; + MutableSpan<OutputState> outputs; + /** + * Counts the number of inputs that still have to be provided to this node, until it should run + * again. This is used as an optimization so that nodes are not scheduled unnecessarily in many + * cases. + */ + int missing_required_inputs = 0; + /** + * Is set to true once the node is done with its work, i.e. when all outputs that may be used + * have been computed. + */ + bool node_has_finished = false; + /** + * Set to true once the node is done running for the first time. + */ + bool had_initialization = true; + /** + * Nodes with side effects should always be executed when their required inputs have been + * computed. + */ + bool has_side_effects = false; + /** + * A node is always in one specific schedule state. This helps to ensure that the same node does + * not run twice at the same time accidentally. + */ + NodeScheduleState schedule_state = NodeScheduleState::NotScheduled; + /** + * Custom storage of the node. + */ + void *storage = nullptr; +}; + +/** + * Utility class that wraps a node whose state is locked. Having this is a separate class is useful + * because it allows methods to communicate that they expect the node to be locked. + */ +struct LockedNode { + /** + * This is the node that is currently locked. + */ + const Node &node; + NodeState &node_state; + + /** + * Used to delay notifying (and therefore locking) other nodes until the current node is not + * locked anymore. This might not be strictly necessary to avoid deadlocks in the current code, + * but is a good measure to avoid accidentally adding a deadlock later on. By not locking more + * than one node per thread at a time, deadlocks are avoided. + * + * The notifications will be send right after the node is not locked anymore. + */ + Vector<const OutputSocket *> delayed_required_outputs; + Vector<const OutputSocket *> delayed_unused_outputs; + Vector<const FunctionNode *> delayed_scheduled_nodes; + + LockedNode(const Node &node, NodeState &node_state) : node(node), node_state(node_state) + { + } +}; + +struct CurrentTask { + /** + * The node that should be run on the same thread after the current node is done. This avoids + * some overhead by skipping a round trip through the task pool. + */ + std::atomic<const FunctionNode *> next_node = nullptr; + /** + * Indicates that some node has been added to the task pool. + */ + std::atomic<bool> added_node_to_pool = false; +}; + +class GraphExecutorLFParams; + +class Executor { + private: + const GraphExecutor &self_; + /** + * Remembers which inputs have been loaded from the caller already, to avoid loading them twice. + * Atomics are used to make sure that every input is only retrieved once. + */ + Array<std::atomic<uint8_t>> loaded_inputs_; + /** + * State of every node, indexed by #Node::index_in_graph. + */ + Array<NodeState *> node_states_; + /** + * Parameters provided by the caller. This is always non-null, while a node is running. + */ + Params *params_ = nullptr; + const Context *context_ = nullptr; + /** + * Used to distribute work on separate nodes to separate threads. + */ + TaskPool *task_pool_ = nullptr; + /** + * A separate linear allocator for every thread. We could potentially reuse some memory, but that + * doesn't seem worth it yet. + */ + threading::EnumerableThreadSpecific<LinearAllocator<>> local_allocators_; + /** + * Set to false when the first execution ends. + */ + bool is_first_execution_ = true; + + friend GraphExecutorLFParams; + + public: + Executor(const GraphExecutor &self) : self_(self), loaded_inputs_(self.graph_inputs_.size()) + { + /* The indices are necessary, because they are used as keys in #node_states_. */ + BLI_assert(self_.graph_.node_indices_are_valid()); + } + + ~Executor() + { + BLI_task_pool_free(task_pool_); + threading::parallel_for(node_states_.index_range(), 1024, [&](const IndexRange range) { + for (const int node_index : range) { + const Node &node = *self_.graph_.nodes()[node_index]; + NodeState &node_state = *node_states_[node_index]; + this->destruct_node_state(node, node_state); + } + }); + } + + /** + * Main entry point to the execution of this graph. + */ + void execute(Params ¶ms, const Context &context) + { + params_ = ¶ms; + context_ = &context; + BLI_SCOPED_DEFER([&]() { + /* Make sure the #params_ pointer is not dangling, even when it shouldn't be accessed by + * anyone. */ + params_ = nullptr; + context_ = nullptr; + is_first_execution_ = false; + }); + + CurrentTask current_task; + if (is_first_execution_) { + this->initialize_node_states(); + task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH); + + /* Initialize atomics to zero. */ + memset(static_cast<void *>(loaded_inputs_.data()), 0, loaded_inputs_.size() * sizeof(bool)); + + this->set_always_unused_graph_inputs(); + this->set_defaulted_graph_outputs(); + this->schedule_side_effect_nodes(current_task); + } + + this->schedule_newly_requested_outputs(current_task); + this->forward_newly_provided_inputs(current_task); + + /* Avoid using task pool when there is no parallel work to do. */ + while (!current_task.added_node_to_pool) { + if (current_task.next_node == nullptr) { + /* Nothing to do. */ + return; + } + const FunctionNode &node = *current_task.next_node; + current_task.next_node = nullptr; + this->run_node_task(node, current_task); + } + if (current_task.next_node != nullptr) { + this->add_node_to_task_pool(*current_task.next_node); + } + + BLI_task_pool_work_and_wait(task_pool_); + } + + private: + void initialize_node_states() + { + Span<const Node *> nodes = self_.graph_.nodes(); + node_states_.reinitialize(nodes.size()); + + /* Construct all node states in parallel. */ + threading::parallel_for(nodes.index_range(), 256, [&](const IndexRange range) { + LinearAllocator<> &allocator = local_allocators_.local(); + for (const int i : range) { + const Node &node = *nodes[i]; + NodeState &node_state = *allocator.construct<NodeState>().release(); + node_states_[i] = &node_state; + this->construct_initial_node_state(allocator, node, node_state); + } + }); + } + + void construct_initial_node_state(LinearAllocator<> &allocator, + const Node &node, + NodeState &node_state) + { + const Span<const InputSocket *> node_inputs = node.inputs(); + const Span<const OutputSocket *> node_outputs = node.outputs(); + + node_state.inputs = allocator.construct_array<InputState>(node_inputs.size()); + node_state.outputs = allocator.construct_array<OutputState>(node_outputs.size()); + + for (const int i : node_outputs.index_range()) { + OutputState &output_state = node_state.outputs[i]; + const OutputSocket &output_socket = *node_outputs[i]; + output_state.potential_target_sockets = output_socket.targets().size(); + if (output_state.potential_target_sockets == 0) { + output_state.usage = ValueUsage::Unused; + } + } + } + + void destruct_node_state(const Node &node, NodeState &node_state) + { + if (node.is_function()) { + const LazyFunction &fn = static_cast<const FunctionNode &>(node).function(); + if (node_state.storage != nullptr) { + fn.destruct_storage(node_state.storage); + } + } + for (const int i : node.inputs().index_range()) { + InputState &input_state = node_state.inputs[i]; + const InputSocket &input_socket = node.input(i); + this->destruct_input_value_if_exists(input_state, input_socket.type()); + } + std::destroy_at(&node_state); + } + + void schedule_newly_requested_outputs(CurrentTask ¤t_task) + { + for (const int graph_output_index : self_.graph_outputs_.index_range()) { + if (params_->get_output_usage(graph_output_index) != ValueUsage::Used) { + continue; + } + if (params_->output_was_set(graph_output_index)) { + continue; + } + const InputSocket &socket = *self_.graph_outputs_[graph_output_index]; + const Node &node = socket.node(); + NodeState &node_state = *node_states_[node.index_in_graph()]; + this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) { + this->set_input_required(locked_node, socket); + }); + } + } + + void set_defaulted_graph_outputs() + { + for (const int graph_output_index : self_.graph_outputs_.index_range()) { + const InputSocket &socket = *self_.graph_outputs_[graph_output_index]; + if (socket.origin() != nullptr) { + continue; + } + const CPPType &type = socket.type(); + const void *default_value = socket.default_value(); + BLI_assert(default_value != nullptr); + + if (self_.logger_ != nullptr) { + self_.logger_->log_socket_value(socket, {type, default_value}, *context_); + } + + void *output_ptr = params_->get_output_data_ptr(graph_output_index); + type.copy_construct(default_value, output_ptr); + params_->output_set(graph_output_index); + } + } + + void set_always_unused_graph_inputs() + { + for (const int i : self_.graph_inputs_.index_range()) { + const OutputSocket &socket = *self_.graph_inputs_[i]; + const Node &node = socket.node(); + const NodeState &node_state = *node_states_[node.index_in_graph()]; + const OutputState &output_state = node_state.outputs[socket.index()]; + if (output_state.usage == ValueUsage::Unused) { + params_->set_input_unused(i); + } + } + } + + void schedule_side_effect_nodes(CurrentTask ¤t_task) + { + if (self_.side_effect_provider_ != nullptr) { + const Vector<const FunctionNode *> side_effect_nodes = + self_.side_effect_provider_->get_nodes_with_side_effects(*context_); + for (const FunctionNode *node : side_effect_nodes) { + NodeState &node_state = *node_states_[node->index_in_graph()]; + node_state.has_side_effects = true; + this->with_locked_node(*node, node_state, current_task, [&](LockedNode &locked_node) { + this->schedule_node(locked_node); + }); + } + } + } + + void forward_newly_provided_inputs(CurrentTask ¤t_task) + { + LinearAllocator<> &allocator = local_allocators_.local(); + for (const int graph_input_index : self_.graph_inputs_.index_range()) { + std::atomic<uint8_t> &was_loaded = loaded_inputs_[graph_input_index]; + if (was_loaded.load()) { + continue; + } + void *input_data = params_->try_get_input_data_ptr(graph_input_index); + if (input_data == nullptr) { + continue; + } + if (was_loaded.fetch_or(1)) { + /* The value was forwarded before. */ + continue; + } + this->forward_newly_provided_input(current_task, allocator, graph_input_index, input_data); + } + } + + void forward_newly_provided_input(CurrentTask ¤t_task, + LinearAllocator<> &allocator, + const int graph_input_index, + void *input_data) + { + const OutputSocket &socket = *self_.graph_inputs_[graph_input_index]; + const CPPType &type = socket.type(); + void *buffer = allocator.allocate(type.size(), type.alignment()); + type.move_construct(input_data, buffer); + this->forward_value_to_linked_inputs(socket, {type, buffer}, current_task); + } + + void notify_output_required(const OutputSocket &socket, CurrentTask ¤t_task) + { + const Node &node = socket.node(); + const int index_in_node = socket.index(); + NodeState &node_state = *node_states_[node.index_in_graph()]; + OutputState &output_state = node_state.outputs[index_in_node]; + + /* The notified output socket might be an input of the entire graph. In this case, notify the + * caller that the input is required. */ + if (node.is_dummy()) { + const int graph_input_index = self_.graph_inputs_.index_of(&socket); + std::atomic<uint8_t> &was_loaded = loaded_inputs_[graph_input_index]; + if (was_loaded.load()) { + return; + } + void *input_data = params_->try_get_input_data_ptr_or_request(graph_input_index); + if (input_data == nullptr) { + return; + } + if (was_loaded.fetch_or(1)) { + /* The value was forwarded already. */ + return; + } + this->forward_newly_provided_input( + current_task, local_allocators_.local(), graph_input_index, input_data); + return; + } + + BLI_assert(node.is_function()); + this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) { + if (output_state.usage == ValueUsage::Used) { + return; + } + output_state.usage = ValueUsage::Used; + this->schedule_node(locked_node); + }); + } + + void notify_output_unused(const OutputSocket &socket, CurrentTask ¤t_task) + { + const Node &node = socket.node(); + const int index_in_node = socket.index(); + NodeState &node_state = *node_states_[node.index_in_graph()]; + OutputState &output_state = node_state.outputs[index_in_node]; + + this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) { + output_state.potential_target_sockets -= 1; + if (output_state.potential_target_sockets == 0) { + BLI_assert(output_state.usage != ValueUsage::Unused); + if (output_state.usage == ValueUsage::Maybe) { + output_state.usage = ValueUsage::Unused; + if (node.is_dummy()) { + const int graph_input_index = self_.graph_inputs_.index_of(&socket); + params_->set_input_unused(graph_input_index); + } + else { + this->schedule_node(locked_node); + } + } + } + }); + } + + void schedule_node(LockedNode &locked_node) + { + BLI_assert(locked_node.node.is_function()); + switch (locked_node.node_state.schedule_state) { + case NodeScheduleState::NotScheduled: { + /* Don't add the node to the task pool immediately, because the task pool might start + * executing it immediately (when Blender is started with a single thread). + * That would often result in a deadlock, because we are still holding the mutex of the + * current node. Also see comments in #LockedNode. */ + locked_node.node_state.schedule_state = NodeScheduleState::Scheduled; + locked_node.delayed_scheduled_nodes.append( + &static_cast<const FunctionNode &>(locked_node.node)); + break; + } + case NodeScheduleState::Scheduled: { + break; + } + case NodeScheduleState::Running: { + locked_node.node_state.schedule_state = NodeScheduleState::RunningAndRescheduled; + break; + } + case NodeScheduleState::RunningAndRescheduled: { + break; + } + } + } + + void with_locked_node(const Node &node, + NodeState &node_state, + CurrentTask ¤t_task, + const FunctionRef<void(LockedNode &)> f) + { + BLI_assert(&node_state == node_states_[node.index_in_graph()]); + + LockedNode locked_node{node, node_state}; + { + std::lock_guard lock{node_state.mutex}; + threading::isolate_task([&]() { f(locked_node); }); + } + + this->send_output_required_notifications(locked_node.delayed_required_outputs, current_task); + this->send_output_unused_notifications(locked_node.delayed_unused_outputs, current_task); + this->schedule_new_nodes(locked_node.delayed_scheduled_nodes, current_task); + } + + void send_output_required_notifications(const Span<const OutputSocket *> sockets, + CurrentTask ¤t_task) + { + for (const OutputSocket *socket : sockets) { + this->notify_output_required(*socket, current_task); + } + } + + void send_output_unused_notifications(const Span<const OutputSocket *> sockets, + CurrentTask ¤t_task) + { + for (const OutputSocket *socket : sockets) { + this->notify_output_unused(*socket, current_task); + } + } + + void schedule_new_nodes(const Span<const FunctionNode *> nodes, CurrentTask ¤t_task) + { + for (const FunctionNode *node_to_schedule : nodes) { + /* Avoid a round trip through the task pool for the first node that is scheduled by the + * current node execution. Other nodes are added to the pool so that other threads can pick + * them up. */ + const FunctionNode *expected = nullptr; + if (current_task.next_node.compare_exchange_strong( + expected, node_to_schedule, std::memory_order_relaxed)) { + continue; + } + this->add_node_to_task_pool(*node_to_schedule); + current_task.added_node_to_pool.store(true, std::memory_order_relaxed); + } + } + + void add_node_to_task_pool(const Node &node) + { + BLI_task_pool_push( + task_pool_, Executor::run_node_from_task_pool, (void *)&node, false, nullptr); + } + + static void run_node_from_task_pool(TaskPool *task_pool, void *task_data) + { + void *user_data = BLI_task_pool_user_data(task_pool); + Executor &executor = *static_cast<Executor *>(user_data); + const FunctionNode &node = *static_cast<const FunctionNode *>(task_data); + + /* This loop reduces the number of round trips through the task pool as long as the current + * node is scheduling more nodes. */ + CurrentTask current_task; + current_task.next_node = &node; + while (current_task.next_node != nullptr) { + const FunctionNode &node_to_run = *current_task.next_node; + current_task.next_node = nullptr; + executor.run_node_task(node_to_run, current_task); + } + } + + void run_node_task(const FunctionNode &node, CurrentTask ¤t_task) + { + NodeState &node_state = *node_states_[node.index_in_graph()]; + LinearAllocator<> &allocator = local_allocators_.local(); + const LazyFunction &fn = node.function(); + + bool node_needs_execution = false; + this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) { + BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled); + node_state.schedule_state = NodeScheduleState::Running; + + if (node_state.node_has_finished) { + return; + } + + bool required_uncomputed_output_exists = false; + for (OutputState &output_state : node_state.outputs) { + output_state.usage_for_execution = output_state.usage; + if (output_state.usage == ValueUsage::Used && !output_state.has_been_computed) { + required_uncomputed_output_exists = true; + } + } + if (!required_uncomputed_output_exists && !node_state.has_side_effects) { + return; + } + + if (node_state.had_initialization) { + /* Initialize storage. */ + node_state.storage = fn.init_storage(allocator); + + /* Load unlinked inputs. */ + for (const int input_index : node.inputs().index_range()) { + const InputSocket &input_socket = node.input(input_index); + if (input_socket.origin() != nullptr) { + continue; + } + InputState &input_state = node_state.inputs[input_index]; + const CPPType &type = input_socket.type(); + const void *default_value = input_socket.default_value(); + BLI_assert(default_value != nullptr); + if (self_.logger_ != nullptr) { + self_.logger_->log_socket_value(input_socket, {type, default_value}, *context_); + } + void *buffer = allocator.allocate(type.size(), type.alignment()); + type.copy_construct(default_value, buffer); + this->forward_value_to_input(locked_node, input_state, {type, buffer}); + } + + /* Request linked inputs that are always needed. */ + const Span<Input> fn_inputs = fn.inputs(); + for (const int input_index : fn_inputs.index_range()) { + const Input &fn_input = fn_inputs[input_index]; + if (fn_input.usage == ValueUsage::Used) { + const InputSocket &input_socket = node.input(input_index); + this->set_input_required(locked_node, input_socket); + } + } + + node_state.had_initialization = false; + } + + for (const int input_index : node_state.inputs.index_range()) { + InputState &input_state = node_state.inputs[input_index]; + if (input_state.was_ready_for_execution) { + continue; + } + if (input_state.value != nullptr) { + input_state.was_ready_for_execution = true; + continue; + } + if (input_state.usage == ValueUsage::Used) { + return; + } + } + + node_needs_execution = true; + }); + + if (node_needs_execution) { + /* Importantly, the node must not be locked when it is executed. That would result in locks + * being hold very long in some cases and results in multiple locks being hold by the same + * thread in the same graph which can lead to deadlocks. */ + this->execute_node(node, node_state, current_task); + } + + this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) { +#ifdef DEBUG + if (node_needs_execution) { + this->assert_expected_outputs_have_been_computed(locked_node); + } +#endif + this->finish_node_if_possible(locked_node); + const bool reschedule_requested = node_state.schedule_state == + NodeScheduleState::RunningAndRescheduled; + node_state.schedule_state = NodeScheduleState::NotScheduled; + if (reschedule_requested && !node_state.node_has_finished) { + this->schedule_node(locked_node); + } + }); + } + + void assert_expected_outputs_have_been_computed(LockedNode &locked_node) + { + const FunctionNode &node = static_cast<const FunctionNode &>(locked_node.node); + const NodeState &node_state = locked_node.node_state; + + if (node_state.missing_required_inputs > 0) { + return; + } + if (node_state.schedule_state == NodeScheduleState::RunningAndRescheduled) { + return; + } + Vector<const OutputSocket *> missing_outputs; + for (const int i : node_state.outputs.index_range()) { + const OutputState &output_state = node_state.outputs[i]; + if (output_state.usage_for_execution == ValueUsage::Used) { + if (!output_state.has_been_computed) { + missing_outputs.append(&node.output(i)); + } + } + } + if (!missing_outputs.is_empty()) { + if (self_.logger_ != nullptr) { + self_.logger_->dump_when_outputs_are_missing(node, missing_outputs, *context_); + } + BLI_assert_unreachable(); + } + } + + void finish_node_if_possible(LockedNode &locked_node) + { + const Node &node = locked_node.node; + NodeState &node_state = locked_node.node_state; + + if (node_state.node_has_finished) { + /* Was finished already. */ + return; + } + /* If there are outputs that may still be used, the node is not done yet. */ + for (const OutputState &output_state : node_state.outputs) { + if (output_state.usage != ValueUsage::Unused && !output_state.has_been_computed) { + return; + } + } + /* If the node is still waiting for inputs, it is not done yet. */ + for (const InputState &input_state : node_state.inputs) { + if (input_state.usage == ValueUsage::Used && !input_state.was_ready_for_execution) { + return; + } + } + + node_state.node_has_finished = true; + + for (const int input_index : node_state.inputs.index_range()) { + const InputSocket &input_socket = node.input(input_index); + InputState &input_state = node_state.inputs[input_index]; + if (input_state.usage == ValueUsage::Maybe) { + this->set_input_unused(locked_node, input_socket); + } + else if (input_state.usage == ValueUsage::Used) { + this->destruct_input_value_if_exists(input_state, input_socket.type()); + } + } + + if (node_state.storage != nullptr) { + if (node.is_function()) { + const FunctionNode &fn_node = static_cast<const FunctionNode &>(node); + fn_node.function().destruct_storage(node_state.storage); + } + node_state.storage = nullptr; + } + } + + void destruct_input_value_if_exists(InputState &input_state, const CPPType &type) + { + if (input_state.value != nullptr) { + type.destruct(input_state.value); + input_state.value = nullptr; + } + } + + void execute_node(const FunctionNode &node, NodeState &node_state, CurrentTask ¤t_task); + + void set_input_unused_during_execution(const Node &node, + NodeState &node_state, + const int input_index, + CurrentTask ¤t_task) + { + const InputSocket &input_socket = node.input(input_index); + this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) { + this->set_input_unused(locked_node, input_socket); + }); + } + + void set_input_unused(LockedNode &locked_node, const InputSocket &input_socket) + { + NodeState &node_state = locked_node.node_state; + const int input_index = input_socket.index(); + InputState &input_state = node_state.inputs[input_index]; + + BLI_assert(input_state.usage != ValueUsage::Used); + if (input_state.usage == ValueUsage::Unused) { + return; + } + input_state.usage = ValueUsage::Unused; + + this->destruct_input_value_if_exists(input_state, input_socket.type()); + if (input_state.was_ready_for_execution) { + return; + } + const OutputSocket *origin = input_socket.origin(); + if (origin != nullptr) { + locked_node.delayed_unused_outputs.append(origin); + } + } + + void *set_input_required_during_execution(const Node &node, + NodeState &node_state, + const int input_index, + CurrentTask ¤t_task) + { + const InputSocket &input_socket = node.input(input_index); + void *result; + this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) { + result = this->set_input_required(locked_node, input_socket); + }); + return result; + } + + void *set_input_required(LockedNode &locked_node, const InputSocket &input_socket) + { + BLI_assert(&locked_node.node == &input_socket.node()); + NodeState &node_state = locked_node.node_state; + const int input_index = input_socket.index(); + InputState &input_state = node_state.inputs[input_index]; + + BLI_assert(input_state.usage != ValueUsage::Unused); + + if (input_state.value != nullptr) { + input_state.was_ready_for_execution = true; + return input_state.value; + } + if (input_state.usage == ValueUsage::Used) { + return nullptr; + } + input_state.usage = ValueUsage::Used; + node_state.missing_required_inputs += 1; + + const OutputSocket *origin_socket = input_socket.origin(); + /* Unlinked inputs are always loaded in advance. */ + BLI_assert(origin_socket != nullptr); + locked_node.delayed_required_outputs.append(origin_socket); + return nullptr; + } + + void forward_value_to_linked_inputs(const OutputSocket &from_socket, + GMutablePointer value_to_forward, + CurrentTask ¤t_task) + { + BLI_assert(value_to_forward.get() != nullptr); + LinearAllocator<> &allocator = local_allocators_.local(); + const CPPType &type = *value_to_forward.type(); + + if (self_.logger_ != nullptr) { + self_.logger_->log_socket_value(from_socket, value_to_forward, *context_); + } + + const Span<const InputSocket *> targets = from_socket.targets(); + for (const InputSocket *target_socket : targets) { + const Node &target_node = target_socket->node(); + NodeState &node_state = *node_states_[target_node.index_in_graph()]; + const int input_index = target_socket->index(); + InputState &input_state = node_state.inputs[input_index]; + const bool is_last_target = target_socket == targets.last(); +#ifdef DEBUG + if (input_state.value != nullptr) { + if (self_.logger_ != nullptr) { + self_.logger_->dump_when_input_is_set_twice(*target_socket, from_socket, *context_); + } + BLI_assert_unreachable(); + } +#endif + BLI_assert(!input_state.was_ready_for_execution); + BLI_assert(target_socket->type() == type); + BLI_assert(target_socket->origin() == &from_socket); + + if (self_.logger_ != nullptr) { + self_.logger_->log_socket_value(*target_socket, value_to_forward, *context_); + } + if (target_node.is_dummy()) { + /* Forward the value to the outside of the graph. */ + const int graph_output_index = self_.graph_outputs_.index_of_try(target_socket); + if (graph_output_index != -1 && + params_->get_output_usage(graph_output_index) != ValueUsage::Unused) { + void *dst_buffer = params_->get_output_data_ptr(graph_output_index); + if (is_last_target) { + type.move_construct(value_to_forward.get(), dst_buffer); + } + else { + type.copy_construct(value_to_forward.get(), dst_buffer); + } + params_->output_set(graph_output_index); + } + continue; + } + this->with_locked_node(target_node, node_state, current_task, [&](LockedNode &locked_node) { + if (input_state.usage == ValueUsage::Unused) { + return; + } + if (is_last_target) { + /* No need to make a copy if this is the last target. */ + this->forward_value_to_input(locked_node, input_state, value_to_forward); + value_to_forward = {}; + } + else { + void *buffer = allocator.allocate(type.size(), type.alignment()); + type.copy_construct(value_to_forward.get(), buffer); + this->forward_value_to_input(locked_node, input_state, {type, buffer}); + } + }); + } + if (value_to_forward.get() != nullptr) { + value_to_forward.destruct(); + } + } + + void forward_value_to_input(LockedNode &locked_node, + InputState &input_state, + GMutablePointer value) + { + NodeState &node_state = locked_node.node_state; + + BLI_assert(input_state.value == nullptr); + BLI_assert(!input_state.was_ready_for_execution); + input_state.value = value.get(); + + if (input_state.usage == ValueUsage::Used) { + node_state.missing_required_inputs -= 1; + if (node_state.missing_required_inputs == 0) { + this->schedule_node(locked_node); + } + } + } +}; + +class GraphExecutorLFParams final : public Params { + private: + Executor &executor_; + const Node &node_; + NodeState &node_state_; + CurrentTask ¤t_task_; + + public: + GraphExecutorLFParams(const LazyFunction &fn, + Executor &executor, + const Node &node, + NodeState &node_state, + CurrentTask ¤t_task) + : Params(fn), + executor_(executor), + node_(node), + node_state_(node_state), + current_task_(current_task) + { + } + + private: + void *try_get_input_data_ptr_impl(const int index) const override + { + const InputState &input_state = node_state_.inputs[index]; + if (input_state.was_ready_for_execution) { + return input_state.value; + } + return nullptr; + } + + void *try_get_input_data_ptr_or_request_impl(const int index) override + { + const InputState &input_state = node_state_.inputs[index]; + if (input_state.was_ready_for_execution) { + return input_state.value; + } + return executor_.set_input_required_during_execution(node_, node_state_, index, current_task_); + } + + void *get_output_data_ptr_impl(const int index) override + { + OutputState &output_state = node_state_.outputs[index]; + BLI_assert(!output_state.has_been_computed); + if (output_state.value == nullptr) { + LinearAllocator<> &allocator = executor_.local_allocators_.local(); + const CPPType &type = node_.output(index).type(); + output_state.value = allocator.allocate(type.size(), type.alignment()); + } + return output_state.value; + } + + void output_set_impl(const int index) override + { + OutputState &output_state = node_state_.outputs[index]; + BLI_assert(!output_state.has_been_computed); + BLI_assert(output_state.value != nullptr); + const OutputSocket &output_socket = node_.output(index); + executor_.forward_value_to_linked_inputs( + output_socket, {output_socket.type(), output_state.value}, current_task_); + output_state.value = nullptr; + output_state.has_been_computed = true; + } + + bool output_was_set_impl(const int index) const override + { + const OutputState &output_state = node_state_.outputs[index]; + return output_state.has_been_computed; + } + + ValueUsage get_output_usage_impl(const int index) const override + { + const OutputState &output_state = node_state_.outputs[index]; + return output_state.usage_for_execution; + } + + void set_input_unused_impl(const int index) override + { + executor_.set_input_unused_during_execution(node_, node_state_, index, current_task_); + } +}; + +/** + * Actually execute the node. + * + * Making this `inline` results in a simpler back-trace in release builds. + */ +inline void Executor::execute_node(const FunctionNode &node, + NodeState &node_state, + CurrentTask ¤t_task) +{ + const LazyFunction &fn = node.function(); + GraphExecutorLFParams node_params{fn, *this, node, node_state, current_task}; + BLI_assert(context_ != nullptr); + Context fn_context = *context_; + fn_context.storage = node_state.storage; + + if (self_.logger_ != nullptr) { + self_.logger_->log_before_node_execute(node, node_params, fn_context); + } + + fn.execute(node_params, fn_context); + + if (self_.logger_ != nullptr) { + self_.logger_->log_after_node_execute(node, node_params, fn_context); + } +} + +GraphExecutor::GraphExecutor(const Graph &graph, + const Span<const OutputSocket *> graph_inputs, + const Span<const InputSocket *> graph_outputs, + const Logger *logger, + const SideEffectProvider *side_effect_provider) + : graph_(graph), + graph_inputs_(graph_inputs), + graph_outputs_(graph_outputs), + logger_(logger), + side_effect_provider_(side_effect_provider) +{ + for (const OutputSocket *socket : graph_inputs_) { + BLI_assert(socket->node().is_dummy()); + inputs_.append({"In", socket->type(), ValueUsage::Maybe}); + } + for (const InputSocket *socket : graph_outputs_) { + BLI_assert(socket->node().is_dummy()); + outputs_.append({"Out", socket->type()}); + } +} + +void GraphExecutor::execute_impl(Params ¶ms, const Context &context) const +{ + Executor &executor = *static_cast<Executor *>(context.storage); + executor.execute(params, context); +} + +void *GraphExecutor::init_storage(LinearAllocator<> &allocator) const +{ + Executor &executor = *allocator.construct<Executor>(*this).release(); + return &executor; +} + +void GraphExecutor::destruct_storage(void *storage) const +{ + std::destroy_at(static_cast<Executor *>(storage)); +} + +void GraphExecutorLogger::log_socket_value(const Socket &socket, + const GPointer value, + const Context &context) const +{ + UNUSED_VARS(socket, value, context); +} + +void GraphExecutorLogger::log_before_node_execute(const FunctionNode &node, + const Params ¶ms, + const Context &context) const +{ + UNUSED_VARS(node, params, context); +} + +void GraphExecutorLogger::log_after_node_execute(const FunctionNode &node, + const Params ¶ms, + const Context &context) const +{ + UNUSED_VARS(node, params, context); +} + +Vector<const FunctionNode *> GraphExecutorSideEffectProvider::get_nodes_with_side_effects( + const Context &context) const +{ + UNUSED_VARS(context); + return {}; +} + +void GraphExecutorLogger::dump_when_outputs_are_missing(const FunctionNode &node, + Span<const OutputSocket *> missing_sockets, + const Context &context) const +{ + UNUSED_VARS(node, missing_sockets, context); +} + +void GraphExecutorLogger::dump_when_input_is_set_twice(const InputSocket &target_socket, + const OutputSocket &from_socket, + const Context &context) const +{ + UNUSED_VARS(target_socket, from_socket, context); +} + +} // namespace blender::fn::lazy_function diff --git a/source/blender/functions/tests/FN_lazy_function_test.cc b/source/blender/functions/tests/FN_lazy_function_test.cc new file mode 100644 index 00000000000..8df064cd8a6 --- /dev/null +++ b/source/blender/functions/tests/FN_lazy_function_test.cc @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "FN_lazy_function_execute.hh" +#include "FN_lazy_function_graph.hh" +#include "FN_lazy_function_graph_executor.hh" + +#include "BLI_task.h" +#include "BLI_timeit.hh" + +namespace blender::fn::lazy_function::tests { + +class AddLazyFunction : public LazyFunction { + public: + AddLazyFunction() + { + debug_name_ = "Add"; + inputs_.append({"A", CPPType::get<int>()}); + inputs_.append({"B", CPPType::get<int>()}); + outputs_.append({"Result", CPPType::get<int>()}); + } + + void execute_impl(Params ¶ms, const Context &UNUSED(context)) const override + { + const int a = params.get_input<int>(0); + const int b = params.get_input<int>(1); + params.set_output(0, a + b); + } +}; + +class StoreValueFunction : public LazyFunction { + private: + int *dst1_; + int *dst2_; + + public: + StoreValueFunction(int *dst1, int *dst2) : dst1_(dst1), dst2_(dst2) + { + debug_name_ = "Store Value"; + inputs_.append({"A", CPPType::get<int>()}); + inputs_.append({"B", CPPType::get<int>(), ValueUsage::Maybe}); + } + + void execute_impl(Params ¶ms, const Context &UNUSED(context)) const override + { + *dst1_ = params.get_input<int>(0); + if (int *value = params.try_get_input_data_ptr_or_request<int>(1)) { + *dst2_ = *value; + } + } +}; + +class SimpleSideEffectProvider : public GraphExecutor::SideEffectProvider { + private: + Vector<const FunctionNode *> side_effect_nodes_; + + public: + SimpleSideEffectProvider(Span<const FunctionNode *> side_effect_nodes) + : side_effect_nodes_(side_effect_nodes) + { + } + + Vector<const FunctionNode *> get_nodes_with_side_effects( + const Context &UNUSED(context)) const override + { + return side_effect_nodes_; + } +}; + +TEST(lazy_function, SimpleAdd) +{ + const AddLazyFunction add_fn; + int result = 0; + execute_lazy_function_eagerly(add_fn, nullptr, std::make_tuple(30, 5), std::make_tuple(&result)); + EXPECT_EQ(result, 35); +} + +TEST(lazy_function, SideEffects) +{ + BLI_task_scheduler_init(); + int dst1 = 0; + int dst2 = 0; + + const AddLazyFunction add_fn; + const StoreValueFunction store_fn{&dst1, &dst2}; + + Graph graph; + FunctionNode &add_node_1 = graph.add_function(add_fn); + FunctionNode &add_node_2 = graph.add_function(add_fn); + FunctionNode &store_node = graph.add_function(store_fn); + DummyNode &input_node = graph.add_dummy({}, {&CPPType::get<int>()}); + + graph.add_link(input_node.output(0), add_node_1.input(0)); + graph.add_link(input_node.output(0), add_node_2.input(0)); + graph.add_link(add_node_1.output(0), store_node.input(0)); + graph.add_link(add_node_2.output(0), store_node.input(1)); + + const int value_10 = 10; + const int value_100 = 100; + add_node_1.input(1).set_default_value(&value_10); + add_node_2.input(1).set_default_value(&value_100); + + graph.update_node_indices(); + + SimpleSideEffectProvider side_effect_provider{{&store_node}}; + + GraphExecutor executor_fn{graph, {&input_node.output(0)}, {}, nullptr, &side_effect_provider}; + execute_lazy_function_eagerly(executor_fn, nullptr, std::make_tuple(5), std::make_tuple()); + + EXPECT_EQ(dst1, 15); + EXPECT_EQ(dst2, 105); +} + +} // namespace blender::fn::lazy_function::tests diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 0f06890cbfa..9e1929b60a8 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRC intern/reverse_uv_sampler.cc intern/set_curve_type.cc intern/subdivide_curves.cc + intern/trim_curves.cc intern/uv_parametrizer.cc GEO_add_curves_on_mesh.hh @@ -41,6 +42,7 @@ set(SRC GEO_reverse_uv_sampler.hh GEO_set_curve_type.hh GEO_subdivide_curves.hh + GEO_trim_curves.hh GEO_uv_parametrizer.h ) diff --git a/source/blender/geometry/GEO_trim_curves.hh b/source/blender/geometry/GEO_trim_curves.hh new file mode 100644 index 00000000000..3c07b5628ea --- /dev/null +++ b/source/blender/geometry/GEO_trim_curves.hh @@ -0,0 +1,32 @@ +#include "BLI_span.hh" + +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_set.hh" + +namespace blender::geometry { + +/* + * Create a new Curves instance by trimming the input curves. Copying the selected splines + * between the start and end points. + */ +bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, + IndexMask selection, + Span<bke::curves::CurvePoint> start_points, + Span<bke::curves::CurvePoint> end_points); + +/** + * Find the point(s) and piecewise segment corresponding to the given distance along the length of + * the curve. Returns points on the evaluated curve for Catmull-Rom and NURBS splines. + * + * \param curves: Curve geometry to sample. + * \param lengths: Distance along the curve on form [0.0, length] to determine the point for. + * \param curve_indices: Curve index to lookup for each 'length', negative index are set to 0. + * \param is_normalized: If true, 'lengths' are normalized to the interval [0.0, 1.0]. + */ +Array<bke::curves::CurvePoint, 12> lookup_curve_points(const bke::CurvesGeometry &curves, + Span<float> lengths, + Span<int64_t> curve_indices, + bool is_normalized); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index e06ee55afa0..bb5e2a0a28a 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -372,6 +372,28 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves, curves.fill_curve_types(new_curves_range, CURVE_TYPE_CATMULL_ROM); + /* Explicitly set all other attributes besides those processed above to default values. */ + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + Set<std::string> attributes_to_skip{{"position", + "curve_type", + "surface_uv_coordinate", + ".selection_point_float", + ".selection_curve_float"}}; + attributes.for_all( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) { + if (id.is_named() && attributes_to_skip.contains(id.name())) { + return true; + } + bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id); + const int new_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? new_points_num : + new_curves_num; + const CPPType &type = attribute.span.type(); + GMutableSpan new_data = attribute.span.take_back(new_elements_num); + type.fill_assign_n(type.default_value(), new_data.data(), new_data.size()); + attribute.finish(); + return true; + }); + return outputs; } diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index 831241aa274..17318c277aa 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -1233,7 +1233,6 @@ static void customdata_weld( float no[3] = {0.0f, 0.0f, 0.0f}; #endif int crease = 0; - int bweight = 0; short flag = 0; /* interpolates a layer at a time */ @@ -1267,7 +1266,6 @@ static void customdata_weld( no[1] += mv_src_no[1]; no[2] += mv_src_no[2]; #endif - bweight += mv_src->bweight; flag |= mv_src->flag; } } @@ -1275,7 +1273,6 @@ static void customdata_weld( for (j = 0; j < count; j++) { MEdge *me_src = &((MEdge *)src_data)[src_indices[j]]; crease += me_src->crease; - bweight += me_src->bweight; flag |= me_src->flag; } } @@ -1312,8 +1309,6 @@ static void customdata_weld( if (type == CD_MVERT) { MVert *mv = &((MVert *)layer_dst->data)[dest_index]; mul_v3_fl(co, fac); - bweight *= fac; - CLAMP_MAX(bweight, 255); copy_v3_v3(mv->co, co); #ifdef USE_WELD_NORMALS @@ -1325,17 +1320,13 @@ static void customdata_weld( #endif mv->flag = (char)flag; - mv->bweight = (char)bweight; } else if (type == CD_MEDGE) { MEdge *me = &((MEdge *)layer_dst->data)[dest_index]; crease *= fac; - bweight *= fac; CLAMP_MAX(crease, 255); - CLAMP_MAX(bweight, 255); me->crease = (char)crease; - me->bweight = (char)bweight; me->flag = flag; } else if (CustomData_layer_has_interp(dest, dest_i)) { diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc index 528a9e72e9e..39571f2931e 100644 --- a/source/blender/geometry/intern/mesh_primitive_cuboid.cc +++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc @@ -54,7 +54,7 @@ struct CuboidConfig { } }; -static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) +static void calculate_verts(const CuboidConfig &config, MutableSpan<MVert> verts) { const float z_bottom = -config.size.z / 2.0f; const float z_delta = config.size.z / config.edges_z; @@ -321,7 +321,7 @@ static void calculate_polys(const CuboidConfig &config, static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id) { - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); bke::SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(uv_id, ATTR_DOMAIN_CORNER); MutableSpan<float2> uvs = uv_attribute.span; @@ -409,7 +409,7 @@ Mesh *create_cuboid_mesh(const float3 &size, MutableSpan<MPoly> polys = mesh->polys_for_write(); MutableSpan<MLoop> loops = mesh->loops_for_write(); - calculate_vertices(config, verts); + calculate_verts(config, verts); calculate_polys(config, polys, loops); BKE_mesh_calc_edges(mesh, false, false); diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index dab373f475b..22961504015 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -44,7 +44,7 @@ bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, curves.cyclic_for_write().fill(false); curves.cyclic_for_write().slice(cyclic_curves).fill(true); - const bke::AttributeAccessor mesh_attributes = bke::mesh_attributes(mesh); + const bke::AttributeAccessor mesh_attributes = mesh.attributes(); bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write(); Set<bke::AttributeIDRef> source_attribute_ids = mesh_attributes.all_ids(); diff --git a/source/blender/geometry/intern/mesh_to_volume.cc b/source/blender/geometry/intern/mesh_to_volume.cc index 0c0d550fc99..b1c7be38609 100644 --- a/source/blender/geometry/intern/mesh_to_volume.cc +++ b/source/blender/geometry/intern/mesh_to_volume.cc @@ -16,7 +16,7 @@ namespace blender::geometry { /* This class follows the MeshDataAdapter interface from openvdb. */ class OpenVDBMeshAdapter { private: - Span<MVert> vertices_; + Span<MVert> verts_; Span<MLoop> loops_; Span<MLoopTri> looptris_; float4x4 transform_; @@ -30,7 +30,7 @@ class OpenVDBMeshAdapter { }; OpenVDBMeshAdapter::OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform) - : vertices_(mesh.verts()), loops_(mesh.loops()), transform_(transform) + : verts_(mesh.verts()), loops_(mesh.loops()), transform_(transform) { /* This only updates a cache and can be considered to be logically const. */ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(&mesh); @@ -45,7 +45,7 @@ size_t OpenVDBMeshAdapter::polygonCount() const size_t OpenVDBMeshAdapter::pointCount() const { - return static_cast<size_t>(vertices_.size()); + return static_cast<size_t>(verts_.size()); } size_t OpenVDBMeshAdapter::vertexCount(size_t UNUSED(polygon_index)) const @@ -59,7 +59,7 @@ void OpenVDBMeshAdapter::getIndexSpacePoint(size_t polygon_index, openvdb::Vec3d &pos) const { const MLoopTri &looptri = looptris_[polygon_index]; - const MVert &vertex = vertices_[loops_[looptri.tri[vertex_index]].v]; + const MVert &vertex = verts_[loops_[looptri.tri[vertex_index]].v]; const float3 transformed_co = transform_ * float3(vertex.co); pos = &transformed_co.x; } diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc index 42fac849667..81f57f785a3 100644 --- a/source/blender/geometry/intern/point_merge_by_distance.cc +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -17,7 +17,7 @@ PointCloud *point_merge_by_distance(const PointCloud &src_points, const float merge_distance, const IndexMask selection) { - const bke::AttributeAccessor src_attributes = bke::pointcloud_attributes(src_points); + const bke::AttributeAccessor src_attributes = src_points.attributes(); VArraySpan<float3> positions = src_attributes.lookup_or_default<float3>( "position", ATTR_DOMAIN_POINT, float3(0)); const int src_size = positions.size(); @@ -41,8 +41,7 @@ PointCloud *point_merge_by_distance(const PointCloud &src_points, /* Create the new point cloud and add it to a temporary component for the attribute API. */ const int dst_size = src_size - duplicate_count; PointCloud *dst_pointcloud = BKE_pointcloud_new_nomain(dst_size); - bke::MutableAttributeAccessor dst_attributes = bke::pointcloud_attributes_for_write( - *dst_pointcloud); + bke::MutableAttributeAccessor dst_attributes = dst_pointcloud->attributes_for_write(); /* By default, every point is just "merged" with itself. Then fill in the results of the merge * finding, converting from indices into the selection to indices into the full input point diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index b230b938ee9..29a9f51c0a7 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -668,7 +668,7 @@ static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set pointcloud_info.pointcloud = pointcloud; /* Access attributes. */ - bke::AttributeAccessor attributes = bke::pointcloud_attributes(*pointcloud); + bke::AttributeAccessor attributes = pointcloud->attributes(); pointcloud_info.attributes.reinitialize(info.attributes.size()); for (const int attribute_index : info.attributes.index_range()) { const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index]; @@ -744,8 +744,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti PointCloudComponent &dst_component = r_realized_geometry.get_component_for_write<PointCloudComponent>(); dst_component.replace(dst_pointcloud); - bke::MutableAttributeAccessor dst_attributes = bke::pointcloud_attributes_for_write( - *dst_pointcloud); + bke::MutableAttributeAccessor dst_attributes = dst_pointcloud->attributes_for_write(); SpanAttributeWriter<float3> positions = dst_attributes.lookup_or_add_for_write_only_span<float3>( "position", ATTR_DOMAIN_POINT); @@ -883,7 +882,7 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set, } /* Access attributes. */ - bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh); + bke::AttributeAccessor attributes = mesh->attributes(); mesh_info.attributes.reinitialize(info.attributes.size()); for (const int attribute_index : info.attributes.index_range()) { const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index]; @@ -1045,7 +1044,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, Mesh *dst_mesh = BKE_mesh_new_nomain(tot_vertices, tot_edges, 0, tot_loops, tot_poly); MeshComponent &dst_component = r_realized_geometry.get_component_for_write<MeshComponent>(); dst_component.replace(dst_mesh); - bke::MutableAttributeAccessor dst_attributes = bke::mesh_attributes_for_write(*dst_mesh); + bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); MutableSpan<MVert> dst_verts = dst_mesh->verts_for_write(); MutableSpan<MEdge> dst_edges = dst_mesh->edges_for_write(); MutableSpan<MPoly> dst_polys = dst_mesh->polys_for_write(); diff --git a/source/blender/geometry/intern/trim_curves.cc b/source/blender/geometry/intern/trim_curves.cc new file mode 100644 index 00000000000..9b71a95057f --- /dev/null +++ b/source/blender/geometry/intern/trim_curves.cc @@ -0,0 +1,1285 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BLI_array_utils.hh" +#include "BLI_length_parameterize.hh" + +#include "BKE_attribute.hh" +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_set.hh" + +#include "GEO_trim_curves.hh" + +namespace blender::geometry { + +/* -------------------------------------------------------------------- */ +/** \name Curve Enums + * \{ */ + +#define CURVE_TYPE_AS_MASK(curve_type) ((CurveTypeMask)((1 << (int)(curve_type)))) + +typedef enum CurveTypeMask { + CURVE_TYPE_MASK_CATMULL_ROM = (1 << 0), + CURVE_TYPE_MASK_POLY = (1 << 1), + CURVE_TYPE_MASK_BEZIER = (1 << 2), + CURVE_TYPE_MASK_NURBS = (1 << 3), + CURVE_TYPE_MASK_ALL = (1 << 4) - 1 +} CurveTypeMask; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IndexRangeCyclic Utilities + * \{ */ + +/** + * Create a cyclical iterator for all control points within the interval [start_point, end_point] + * including any control point at the start or end point. + * + * \param start_point Point on the curve that define the starting point of the interval. + * \param end_point Point on the curve that define the end point of the interval (included). + * \param points IndexRange for the curve points. + */ +static bke::curves::IndexRangeCyclic get_range_between_endpoints( + const bke::curves::CurvePoint start_point, + const bke::curves::CurvePoint end_point, + const IndexRange points) +{ + const int64_t start_index = start_point.parameter == 0.0 ? start_point.index : + start_point.next_index; + int64_t end_index = end_point.parameter == 0.0 ? end_point.index : end_point.next_index; + int64_t cycles; + + if (end_point.is_controlpoint()) { + ++end_index; + if (end_index > points.last()) { + end_index = points.one_after_last(); + } + /* end_point < start_point but parameter is irrelevant (end_point is controlpoint), and loop + * when equal due to increment. */ + cycles = end_index <= start_index; + } + else { + cycles = end_point < start_point || end_index < start_index; + } + return bke::curves::IndexRangeCyclic(start_index, end_index, points, cycles); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lookup Curve Points + * \{ */ + +/** + * Find the point on the curve defined by the distance along the curve. Assumes curve resolution is + * constant for all curve segments and evaluated curve points are uniformly spaced between the + * segment endpoints in relation to the curve parameter. + * + * \param lengths: Accumulated lenght for the evaluated curve. + * \param sample_length: Distance along the curve to determine the CurvePoint for. + * \param cyclic: If curve is cyclic. + * \param resolution: Curve resolution (number of evaluated points per segment). + * \param num_curve_points: Total number of control points in the curve. + * \return: Point on the piecewise segment matching the given distance. + */ +static bke::curves::CurvePoint lookup_curve_point(const Span<float> lengths, + const float sample_length, + const bool cyclic, + const int resolution, + const int num_curve_points) +{ + BLI_assert(!cyclic || lengths.size() / resolution >= 2); + const int last_index = num_curve_points - 1; + if (sample_length <= 0.0f) { + return {0, 1, 0.0f}; + } + if (sample_length >= lengths.last()) { + return cyclic ? bke::curves::CurvePoint{last_index, 0, 1.0} : + bke::curves::CurvePoint{last_index - 1, last_index, 1.0}; + } + int eval_index; + float eval_factor; + length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); + + const int index = eval_index / resolution; + const int next_index = (index == last_index) ? 0 : index + 1; + const float parameter = (eval_factor + eval_index) / resolution - index; + + return bke::curves::CurvePoint{index, next_index, parameter}; +} + +/** + * Find the point on the 'evaluated' polygonal curve. + */ +static bke::curves::CurvePoint lookup_evaluated_point(const Span<float> lengths, + const float sample_length, + const bool cyclic, + const int evaluated_size) +{ + const int last_index = evaluated_size - 1; + if (sample_length <= 0.0f) { + return {0, 1, 0.0f}; + } + if (sample_length >= lengths.last()) { + return cyclic ? bke::curves::CurvePoint{last_index, 0, 1.0} : + bke::curves::CurvePoint{last_index - 1, last_index, 1.0}; + } + + int eval_index; + float eval_factor; + length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); + + const int next_eval_index = (eval_index == last_index) ? 0 : eval_index + 1; + return bke::curves::CurvePoint{eval_index, next_eval_index, eval_factor}; +} + +/** + * Find the point on a Bezier curve using the 'bezier_offsets' cache. + */ +static bke::curves::CurvePoint lookup_bezier_point(const Span<int> bezier_offsets, + const Span<float> lengths, + const float sample_length, + const bool cyclic, + const int num_curve_points) +{ + const int last_index = num_curve_points - 1; + if (sample_length <= 0.0f) { + return {0, 1, 0.0f}; + } + if (sample_length >= lengths.last()) { + return cyclic ? bke::curves::CurvePoint{last_index, 0, 1.0} : + bke::curves::CurvePoint{last_index - 1, last_index, 1.0}; + } + int eval_index; + float eval_factor; + length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); + + /* Find the segment index from the offset mapping. */ + const int *offset = std::upper_bound(bezier_offsets.begin(), bezier_offsets.end(), eval_index); + const int left = offset - bezier_offsets.begin(); + const int right = left == last_index ? 0 : left + 1; + + const int prev_offset = left == 0 ? 0 : bezier_offsets[(int64_t)left - 1]; + const float offset_in_segment = eval_factor + eval_index - prev_offset; + const int segment_resolution = bezier_offsets[left] - prev_offset; + const float parameter = std::clamp(offset_in_segment / segment_resolution, 0.0f, 1.0f); + + return {left, right, parameter}; +} + +Array<bke::curves::CurvePoint, 12> lookup_curve_points(const bke::CurvesGeometry &curves, + const Span<float> lengths, + const Span<int64_t> curve_indices, + const bool normalized_factors) +{ + BLI_assert(lengths.size() == curve_indices.size()); + BLI_assert(*std::max_element(curve_indices.begin(), curve_indices.end()) < curves.curves_num()); + + const VArray<bool> cyclic = curves.cyclic(); + const VArray<int> resolution = curves.resolution(); + const VArray<int8_t> curve_types = curves.curve_types(); + + /* Compute curve lenghts! */ + curves.ensure_evaluated_lengths(); + curves.ensure_evaluated_offsets(); + + /* Find the curve points referenced by the input! */ + Array<bke::curves::CurvePoint, 12> lookups(curve_indices.size()); + threading::parallel_for(curve_indices.index_range(), 128, [&](const IndexRange range) { + for (const int64_t lookup_index : range) { + const int64_t curve_i = curve_indices[lookup_index]; + + const int point_count = curves.points_num_for_curve(curve_i); + if (curve_i < 0 || point_count == 1) { + lookups[lookup_index] = {0, 0, 0.0f}; + continue; + } + + const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i, + cyclic[curve_i]); + BLI_assert(accumulated_lengths.size() > 0); + + const float sample_length = normalized_factors ? + lengths[lookup_index] * accumulated_lengths.last() : + lengths[lookup_index]; + + const CurveType curve_type = (CurveType)curve_types[curve_i]; + + switch (curve_type) { + case CURVE_TYPE_BEZIER: { + if (bke::curves::bezier::has_vector_handles( + point_count, + curves.evaluated_points_for_curve(curve_i).size(), + cyclic[curve_i], + resolution[curve_i])) { + const Span<int> bezier_offsets = curves.bezier_evaluated_offsets_for_curve(curve_i); + lookups[lookup_index] = lookup_bezier_point( + bezier_offsets, accumulated_lengths, sample_length, cyclic[curve_i], point_count); + } + else { + lookups[lookup_index] = lookup_curve_point(accumulated_lengths, + sample_length, + cyclic[curve_i], + resolution[curve_i], + point_count); + } + break; + } + case CURVE_TYPE_CATMULL_ROM: { + lookups[lookup_index] = lookup_curve_point(accumulated_lengths, + sample_length, + cyclic[curve_i], + resolution[curve_i], + point_count); + break; + } + case CURVE_TYPE_NURBS: + case CURVE_TYPE_POLY: + default: { + /* Handle general case as an "evaluated" or polygonal curve. */ + BLI_assert(resolution[curve_i] > 0); + lookups[lookup_index] = lookup_evaluated_point( + accumulated_lengths, + sample_length, + cyclic[curve_i], + curves.evaluated_points_for_curve(curve_i).size()); + break; + } + } + } + }); + return lookups; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transfer Curve Domain + * \{ */ + +/** + * Determine curve type(s) for the copied curves given the supported set of types and knot modes. + * If a curve type is not supported the default type is set. + */ +static void determine_copyable_curve_types(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const IndexMask selection_inverse, + const CurveTypeMask supported_curve_type_mask, + const int8_t default_curve_type = (int8_t) + CURVE_TYPE_POLY) +{ + const VArray<int8_t> src_curve_types = src_curves.curve_types(); + const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes(); + MutableSpan<int8_t> dst_curve_types = dst_curves.curve_types_for_write(); + + threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange selection_range) { + for (const int64_t curve_i : selection.slice(selection_range)) { + if (supported_curve_type_mask & CURVE_TYPE_AS_MASK(src_curve_types[curve_i])) { + dst_curve_types[curve_i] = src_curve_types[curve_i]; + } + else { + dst_curve_types[curve_i] = default_curve_type; + } + } + }); + + array_utils::copy(src_curve_types, selection_inverse, dst_curve_types); +} + +/** + * Determine if a curve is treated as an evaluated curve. Curves which inheretly do not support + * trimming are discretized (e.g. NURBS). + */ +static bool copy_as_evaluated_curve(const int8_t src_type, const int8_t dst_type) +{ + return src_type != CURVE_TYPE_POLY && dst_type == CURVE_TYPE_POLY; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Specialized Curve Constructors + * \{ */ + +static void compute_trim_result_offsets(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const IndexMask inverse_selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + const VArray<int8_t> dst_curve_types, + MutableSpan<int> dst_curve_offsets, + Vector<int64_t> &r_curve_indices, + Vector<int64_t> &r_point_curve_indices) +{ + BLI_assert(r_curve_indices.size() == 0); + BLI_assert(r_point_curve_indices.size() == 0); + const VArray<bool> cyclic = src_curves.cyclic(); + const VArray<int8_t> curve_types = src_curves.curve_types(); + r_curve_indices.reserve(selection.size()); + + for (const int64_t curve_i : selection) { + + int64_t src_point_count; + + if (copy_as_evaluated_curve(curve_types[curve_i], dst_curve_types[curve_i])) { + src_point_count = src_curves.evaluated_points_for_curve(curve_i).size(); + } + else { + src_point_count = (int64_t)src_curves.points_num_for_curve(curve_i); + } + BLI_assert(src_point_count > 0); + + if (start_points[curve_i] == end_points[curve_i]) { + dst_curve_offsets[curve_i] = 1; + r_point_curve_indices.append(curve_i); + } + else { + const bke::curves::IndexRangeCyclic point_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_point_count}); + const int count = point_range.size() + !start_points[curve_i].is_controlpoint() + + !end_points[curve_i].is_controlpoint(); + dst_curve_offsets[curve_i] = count; + r_curve_indices.append(curve_i); + } + BLI_assert(dst_curve_offsets[curve_i] > 0); + } + threading::parallel_for( + inverse_selection.index_range(), 4096, [&](const IndexRange selection_range) { + for (const int64_t curve_i : inverse_selection.slice(selection_range)) { + dst_curve_offsets[curve_i] = src_curves.points_num_for_curve(curve_i); + } + }); + bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); +} + +/* -------------------------------------------------------------------- + * Utility functions. + */ + +static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) +{ + if (dst_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + MutableSpan<float3> handle_positions_left = dst_curves.handle_positions_left_for_write(); + MutableSpan<float3> handle_positions_right = dst_curves.handle_positions_right_for_write(); + MutableSpan<int8_t> handle_types_left = dst_curves.handle_types_left_for_write(); + MutableSpan<int8_t> handle_types_right = dst_curves.handle_types_right_for_write(); + + threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange points = dst_curves.points_for_curve(curve_i); + handle_types_right.slice(points).fill((int8_t)BEZIER_HANDLE_FREE); + handle_types_left.slice(points).fill((int8_t)BEZIER_HANDLE_FREE); + handle_positions_left.slice(points).fill({0.0f, 0.0f, 0.0f}); + handle_positions_right.slice(points).fill({0.0f, 0.0f, 0.0f}); + } + }); + } +} +static void fill_nurbs_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) +{ + if (dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { + bke::curves::fill_points(dst_curves, selection, 0.0f, dst_curves.nurbs_weights_for_write()); + } +} + +template<typename T> +static int64_t copy_point_data_between_endpoints(const Span<T> src_data, + MutableSpan<T> dst_data, + const bke::curves::IndexRangeCyclic src_range, + const int64_t src_index, + int64_t dst_index) +{ + int64_t increment; + if (src_range.cycles()) { + increment = src_range.size_before_loop(); + dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_index, increment)); + dst_index += increment; + + increment = src_range.size_after_loop(); + dst_data.slice(dst_index, increment) + .copy_from(src_data.slice(src_range.curve_range().first(), increment)); + dst_index += increment; + } + else { + increment = src_range.one_after_last() - src_range.first(); + dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_index, increment)); + dst_index += increment; + } + return dst_index; +} + +/* -------------------------------------------------------------------- + * Sampling utilities. + */ + +template<typename T> +static T interpolate_catmull_rom(const Span<T> src_data, + const bke::curves::CurvePoint insertion_point, + const bool src_cyclic) +{ + BLI_assert(insertion_point.index >= 0 && insertion_point.next_index < src_data.size()); + int i0; + if (insertion_point.index == 0) { + i0 = src_cyclic ? src_data.size() - 1 : insertion_point.index; + } + else { + i0 = insertion_point.index - 1; + } + int i3 = insertion_point.next_index + 1; + if (i3 == src_data.size()) { + i3 = src_cyclic ? 0 : insertion_point.next_index; + } + return bke::curves::catmull_rom::interpolate<T>(src_data[i0], + src_data[insertion_point.index], + src_data[insertion_point.next_index], + src_data[i3], + insertion_point.parameter); +} + +static bke::curves::bezier::Insertion knot_insert_bezier( + const Span<float3> positions, + const Span<float3> handles_left, + const Span<float3> handles_right, + const bke::curves::CurvePoint insertion_point) +{ + BLI_assert( + insertion_point.index + 1 == insertion_point.next_index || + (insertion_point.next_index >= 0 && insertion_point.next_index < insertion_point.index)); + return bke::curves::bezier::insert(positions[insertion_point.index], + handles_right[insertion_point.index], + handles_left[insertion_point.next_index], + positions[insertion_point.next_index], + insertion_point.parameter); +} + +/* -------------------------------------------------------------------- + * Sample single point. + */ + +template<typename T> +static void sample_linear(const Span<T> src_data, + MutableSpan<T> dst_data, + const IndexRange dst_range, + const bke::curves::CurvePoint sample_point) +{ + BLI_assert(dst_range.size() == 1); + if (sample_point.is_controlpoint()) { + /* Resolves cases where the source curve consist of a single control point. */ + const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index; + dst_data[dst_range.first()] = src_data[index]; + } + else { + dst_data[dst_range.first()] = attribute_math::mix2( + sample_point.parameter, src_data[sample_point.index], src_data[sample_point.next_index]); + } +} + +template<typename T> +static void sample_catmull_rom(const Span<T> src_data, + MutableSpan<T> dst_data, + const IndexRange dst_range, + const bke::curves::CurvePoint sample_point, + const bool src_cyclic) +{ + BLI_assert(dst_range.size() == 1); + if (sample_point.is_controlpoint()) { + /* Resolves cases where the source curve consist of a single control point. */ + const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index; + dst_data[dst_range.first()] = src_data[index]; + } + else { + dst_data[dst_range.first()] = interpolate_catmull_rom(src_data, sample_point, src_cyclic); + } +} + +static void sample_bezier(const Span<float3> src_positions, + const Span<float3> src_handles_l, + const Span<float3> src_handles_r, + const Span<int8_t> src_types_l, + const Span<int8_t> src_types_r, + MutableSpan<float3> dst_positions, + MutableSpan<float3> dst_handles_l, + MutableSpan<float3> dst_handles_r, + MutableSpan<int8_t> dst_types_l, + MutableSpan<int8_t> dst_types_r, + const IndexRange dst_range, + const bke::curves::CurvePoint sample_point) +{ + BLI_assert(dst_range.size() == 1); + if (sample_point.is_controlpoint()) { + /* Resolves cases where the source curve consist of a single control point. */ + const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index; + dst_positions[dst_range.first()] = src_positions[index]; + dst_handles_l[dst_range.first()] = src_handles_l[index]; + dst_handles_r[dst_range.first()] = src_handles_r[index]; + dst_types_l[dst_range.first()] = src_types_l[index]; + dst_types_r[dst_range.first()] = src_types_r[index]; + } + else { + bke::curves::bezier::Insertion insertion_point = knot_insert_bezier( + src_positions, src_handles_l, src_handles_r, sample_point); + dst_positions[dst_range.first()] = insertion_point.position; + dst_handles_l[dst_range.first()] = insertion_point.left_handle; + dst_handles_r[dst_range.first()] = insertion_point.right_handle; + dst_types_l[dst_range.first()] = BEZIER_HANDLE_FREE; + dst_types_r[dst_range.first()] = BEZIER_HANDLE_FREE; + } +} + +/* -------------------------------------------------------------------- + * Sample curve interval (trim). + */ + +/** + * Sample source curve data in the interval defined by the points [start_point, end_point]. + * Uses linear interpolation to compute the endpoints. + * + * \tparam include_start_point If False, the 'start_point' point sample will not be copied + * and not accounted for in the destination range. + * \param src_data: Source to sample from. + * \param dst_data: Destination to write samples to. + * \param src_range: Interval within [start_point, end_point] to copy from the source point domain. + * \param dst_range: Interval to copy point data to in the destination buffer. + * \param start_point: Point on the source curve to start sampling from. + * \param end_point: Last point to sample in the source curve. + */ +template<typename T, bool include_start_point = true> +static void sample_interval_linear(const Span<T> src_data, + MutableSpan<T> dst_data, + const bke::curves::IndexRangeCyclic src_range, + const IndexRange dst_range, + const bke::curves::CurvePoint start_point, + const bke::curves::CurvePoint end_point) +{ + int64_t src_index = src_range.first(); + int64_t dst_index = dst_range.first(); + + if (start_point.is_controlpoint()) { + /* 'start_point' is included in the copy iteration. */ + if constexpr (!include_start_point) { + /* Skip first. */ + ++src_index; + } + } + else if constexpr (!include_start_point) { + /* Do nothing (excluded). */ + } + else { + /* General case, sample 'start_point' */ + dst_data[dst_index] = attribute_math::mix2( + start_point.parameter, src_data[start_point.index], src_data[start_point.next_index]); + ++dst_index; + } + + dst_index = copy_point_data_between_endpoints( + src_data, dst_data, src_range, src_index, dst_index); + + /* Handle last case */ + if (end_point.is_controlpoint()) { + /* 'end_point' is included in the copy iteration. */ + } + else { + dst_data[dst_index] = attribute_math::mix2( + end_point.parameter, src_data[end_point.index], src_data[end_point.next_index]); +#ifdef DEBUG + ++dst_index; +#endif + } + BLI_assert(dst_index == dst_range.one_after_last()); +} + +template<typename T, bool include_start_point = true> +static void sample_interval_catmull_rom(const Span<T> src_data, + MutableSpan<T> dst_data, + const bke::curves::IndexRangeCyclic src_range, + const IndexRange dst_range, + const bke::curves::CurvePoint start_point, + const bke::curves::CurvePoint end_point, + const bool src_cyclic) +{ + int64_t src_index = src_range.first(); + int64_t dst_index = dst_range.first(); + + if (start_point.is_controlpoint()) { + /* 'start_point' is included in the copy iteration. */ + if constexpr (!include_start_point) { + /* Skip first. */ + ++src_index; + } + } + else if constexpr (!include_start_point) { + /* Do nothing (excluded). */ + } + else { + /* General case, sample 'start_point' */ + dst_data[dst_index] = interpolate_catmull_rom(src_data, start_point, src_cyclic); + ++dst_index; + } + + dst_index = copy_point_data_between_endpoints( + src_data, dst_data, src_range, src_index, dst_index); + + /* Handle last case */ + if (end_point.is_controlpoint()) { + /* 'end_point' is included in the copy iteration. */ + } + else { + dst_data[dst_index] = interpolate_catmull_rom(src_data, end_point, src_cyclic); +#ifdef DEBUG + ++dst_index; +#endif + } + BLI_assert(dst_index == dst_range.one_after_last()); +} + +template<bool include_start_point = true> +static void sample_interval_bezier(const Span<float3> src_positions, + const Span<float3> src_handles_l, + const Span<float3> src_handles_r, + const Span<int8_t> src_types_l, + const Span<int8_t> src_types_r, + MutableSpan<float3> dst_positions, + MutableSpan<float3> dst_handles_l, + MutableSpan<float3> dst_handles_r, + MutableSpan<int8_t> dst_types_l, + MutableSpan<int8_t> dst_types_r, + const bke::curves::IndexRangeCyclic src_range, + const IndexRange dst_range, + const bke::curves::CurvePoint start_point, + const bke::curves::CurvePoint end_point) +{ + bke::curves::bezier::Insertion start_point_insert; + int64_t src_index = src_range.first(); + int64_t dst_index = dst_range.first(); + + bool start_point_trimmed = false; + if (start_point.is_controlpoint()) { + /* The 'start_point' control point is included in the copy iteration. */ + if constexpr (!include_start_point) { + ++src_index; /* Skip first! */ + } + } + else if constexpr (!include_start_point) { + /* Do nothing, 'start_point' is excluded. */ + } + else { + /* General case, sample 'start_point'. */ + start_point_insert = knot_insert_bezier( + src_positions, src_handles_l, src_handles_r, start_point); + dst_positions[dst_range.first()] = start_point_insert.position; + dst_handles_l[dst_range.first()] = start_point_insert.left_handle; + dst_handles_r[dst_range.first()] = start_point_insert.right_handle; + dst_types_l[dst_range.first()] = src_types_l[start_point.index]; + dst_types_r[dst_range.first()] = src_types_r[start_point.index]; + + start_point_trimmed = true; + ++dst_index; + } + + /* Copy point data between the 'start_point' and 'end_point'. */ + int64_t increment = src_range.cycles() ? src_range.size_before_loop() : + src_range.one_after_last() - src_range.first(); + + const IndexRange dst_range_to_end(dst_index, increment); + const IndexRange src_range_to_end(src_index, increment); + dst_positions.slice(dst_range_to_end).copy_from(src_positions.slice(src_range_to_end)); + dst_handles_l.slice(dst_range_to_end).copy_from(src_handles_l.slice(src_range_to_end)); + dst_handles_r.slice(dst_range_to_end).copy_from(src_handles_r.slice(src_range_to_end)); + dst_types_l.slice(dst_range_to_end).copy_from(src_types_l.slice(src_range_to_end)); + dst_types_r.slice(dst_range_to_end).copy_from(src_types_r.slice(src_range_to_end)); + dst_index += increment; + + increment = src_range.size_after_loop(); + if (src_range.cycles() && increment > 0) { + const IndexRange dst_range_looped(dst_index, increment); + const IndexRange src_range_looped(src_range.curve_range().first(), increment); + dst_positions.slice(dst_range_looped).copy_from(src_positions.slice(src_range_looped)); + dst_handles_l.slice(dst_range_looped).copy_from(src_handles_l.slice(src_range_looped)); + dst_handles_r.slice(dst_range_looped).copy_from(src_handles_r.slice(src_range_looped)); + dst_types_l.slice(dst_range_looped).copy_from(src_types_l.slice(src_range_looped)); + dst_types_r.slice(dst_range_looped).copy_from(src_types_r.slice(src_range_looped)); + dst_index += increment; + } + + if (start_point_trimmed) { + dst_handles_l[dst_range.first() + 1] = start_point_insert.handle_next; + /* No need to set handle type (remains the same)! */ + } + + /* Handle 'end_point' */ + bke::curves::bezier::Insertion end_point_insert; + if (end_point.is_controlpoint()) { + /* Do nothing, the 'end_point' control point is included in the copy iteration. */ + } + else { + /* Trimmed in both ends within the same (and only) segment! Ensure both end points is not a + * loop.*/ + if (start_point_trimmed && start_point.index == end_point.index && + start_point.parameter <= end_point.parameter) { + + /* Copy following segment control point. */ + dst_positions[dst_index] = src_positions[end_point.next_index]; + dst_handles_r[dst_index] = src_handles_r[end_point.next_index]; + + /* Compute interpolation in the result curve. */ + const float parameter = (end_point.parameter - start_point.parameter) / + (1.0f - start_point.parameter); + end_point_insert = knot_insert_bezier( + dst_positions, + dst_handles_l, + dst_handles_r, + {(int)dst_range.first(), (int)(dst_range.first() + 1), parameter}); + } + else { + /* General case, compute the insertion point. */ + end_point_insert = knot_insert_bezier( + src_positions, src_handles_l, src_handles_r, end_point); + } + + dst_handles_r[dst_index - 1] = end_point_insert.handle_prev; + dst_types_r[dst_index - 1] = src_types_l[end_point.index]; + + dst_handles_l[dst_index] = end_point_insert.left_handle; + dst_handles_r[dst_index] = end_point_insert.right_handle; + dst_positions[dst_index] = end_point_insert.position; + dst_types_l[dst_index] = src_types_l[end_point.next_index]; + dst_types_r[dst_index] = src_types_r[end_point.next_index]; +#ifdef DEBUG + ++dst_index; +#endif // DEBUG + } + BLI_assert(dst_index == dst_range.one_after_last()); +} + +/* -------------------------------------------------------------------- + * Convert to point curves. + */ + +static void convert_point_polygonal_curves( + const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> sample_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + sample_linear<float3>( + src_positions.slice(src_points), dst_positions, dst_points, sample_points[curve_i]); + + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + sample_linear<T>(attribute.src.template typed<T>().slice(src_points), + attribute.dst.span.typed<T>(), + dst_curves.points_for_curve(curve_i), + sample_points[curve_i]); + }); + } + } + }); + + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); +} + +static void convert_point_catmull_curves( + const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> sample_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + const VArray<bool> src_cyclic = src_curves.cyclic(); + + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + sample_catmull_rom<float3>(src_positions.slice(src_points), + dst_positions, + dst_points, + sample_points[curve_i], + src_cyclic[curve_i]); + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + sample_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points), + attribute.dst.span.typed<T>(), + dst_points, + sample_points[curve_i], + src_cyclic[curve_i]); + }); + } + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); +} + +static void convert_point_bezier_curves( + const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> sample_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()}; + const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()}; + const Span<float3> src_handles_l = src_curves.handle_positions_left(); + const Span<float3> src_handles_r = src_curves.handle_positions_right(); + + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write(); + MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write(); + MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); + MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write(); + + threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + sample_bezier(src_positions.slice(src_points), + src_handles_l.slice(src_points), + src_handles_r.slice(src_points), + src_types_l.slice(src_points), + src_types_r.slice(src_points), + dst_positions, + dst_handles_l, + dst_handles_r, + dst_types_l, + dst_types_r, + dst_points, + sample_points[curve_i]); + + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + sample_linear<T>(attribute.src.template typed<T>().slice(src_points), + attribute.dst.span.typed<T>(), + dst_points, + sample_points[curve_i]); + }); + } + } + }); + fill_nurbs_data(dst_curves, selection); +} + +static void convert_point_evaluated_curves( + const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> evaluated_sample_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_eval_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); + + sample_linear<float3>(src_eval_positions.slice(src_evaluated_points), + dst_positions, + dst_points, + evaluated_sample_points[curve_i]); + + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size()); + GMutableSpan evaluated_span = evaluated_data.as_mutable_span(); + src_curves.interpolate_to_evaluated( + curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span); + sample_linear<T>(evaluated_span.typed<T>(), + attribute.dst.span.typed<T>(), + dst_points, + evaluated_sample_points[curve_i]); + }); + } + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); +} + +/* -------------------------------------------------------------------- + * Trim curves. + */ + +static void trim_attribute_linear(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + + bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_points.size()}); + sample_interval_linear<T>(attribute.src.template typed<T>().slice(src_points), + attribute.dst.span.typed<T>(), + src_sample_range, + dst_curves.points_for_curve(curve_i), + start_points[curve_i], + end_points[curve_i]); + } + }); + }); + } +} + +static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_points.size()}); + sample_interval_linear<float3>(src_positions.slice(src_points), + dst_positions, + src_sample_range, + dst_points, + start_points[curve_i], + end_points[curve_i]); + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); + trim_attribute_linear( + src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); +} + +static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + const VArray<bool> src_cyclic = src_curves.cyclic(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_points.size()}); + sample_interval_catmull_rom<float3>(src_positions.slice(src_points), + dst_positions, + src_sample_range, + dst_points, + start_points[curve_i], + end_points[curve_i], + src_cyclic[curve_i]); + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); + + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_points.size()}); + sample_interval_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points), + attribute.dst.span.typed<T>(), + src_sample_range, + dst_points, + start_points[curve_i], + end_points[curve_i], + src_cyclic[curve_i]); + } + }); + }); + } +} + +static void trim_bezier_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()}; + const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()}; + const Span<float3> src_handles_l = src_curves.handle_positions_left(); + const Span<float3> src_handles_r = src_curves.handle_positions_right(); + + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write(); + MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write(); + MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); + MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_points.size()}); + sample_interval_bezier(src_positions.slice(src_points), + src_handles_l.slice(src_points), + src_handles_r.slice(src_points), + src_types_l.slice(src_points), + src_types_r.slice(src_points), + dst_positions, + dst_handles_l, + dst_handles_r, + dst_types_l, + dst_types_r, + src_sample_range, + dst_points, + start_points[curve_i], + end_points[curve_i]); + } + }); + fill_nurbs_data(dst_curves, selection); + trim_attribute_linear( + src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); +} + +static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_eval_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); + + bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_evaluated_points.size()}); + sample_interval_linear<float3>(src_eval_positions.slice(src_evaluated_points), + dst_positions, + src_sample_range, + dst_points, + start_points[curve_i], + end_points[curve_i]); + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); + + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + /* Interpolate onto the evaluated point domain and sample the evaluated domain. */ + const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); + GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size()); + GMutableSpan evaluated_span = evaluated_data.as_mutable_span(); + src_curves.interpolate_to_evaluated( + curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span); + bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], {0, src_evaluated_points.size()}); + sample_interval_linear<T>(evaluated_span.typed<T>(), + attribute.dst.span.typed<T>(), + src_sample_range, + dst_curves.points_for_curve(curve_i), + start_points[curve_i], + end_points[curve_i]); + } + }); + }); + } +} + +bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points) +{ + BLI_assert(selection.size() > 0); + BLI_assert(selection.last() <= start_points.size()); + BLI_assert(start_points.size() == end_points.size()); + + src_curves.ensure_evaluated_offsets(); + Vector<int64_t> inverse_selection_indices; + const IndexMask inverse_selection = selection.invert(src_curves.curves_range(), + inverse_selection_indices); + + /* Create trim curves. */ + bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); + determine_copyable_curve_types(src_curves, + dst_curves, + selection, + inverse_selection, + (CurveTypeMask)(CURVE_TYPE_MASK_CATMULL_ROM | + CURVE_TYPE_MASK_POLY | CURVE_TYPE_MASK_BEZIER)); + + Vector<int64_t> curve_indices; + Vector<int64_t> point_curve_indices; + compute_trim_result_offsets(src_curves, + selection, + inverse_selection, + start_points, + end_points, + dst_curves.curve_types(), + dst_curves.offsets_for_write(), + curve_indices, + point_curve_indices); + /* Finalize by updating the geometry container. */ + dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); + dst_curves.update_curve_types(); + + /* Populate curve domain. */ + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + bke::copy_attribute_domain(src_attributes, + dst_attributes, + selection, + ATTR_DOMAIN_CURVE, + {"cyclic", "curve_type", "nurbs_order", "knots_mode"}); + + /* Fetch custom point domain attributes for transfer (copy). */ + Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_left", + "handle_right", + "handle_type_left", + "handle_type_right", + "nurbs_weight"}); + + auto trim_catmull = [&](IndexMask selection) { + trim_catmull_rom_curves( + src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); + }; + auto trim_poly = [&](IndexMask selection) { + trim_polygonal_curves( + src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); + }; + auto trim_bezier = [&](IndexMask selection) { + trim_bezier_curves( + src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); + }; + auto trim_evaluated = [&](IndexMask selection) { + /* Ensure evaluated positions are available. */ + src_curves.ensure_evaluated_offsets(); + src_curves.evaluated_positions(); + trim_evaluated_curves( + src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); + }; + + auto single_point_catmull = [&](IndexMask selection) { + convert_point_catmull_curves( + src_curves, dst_curves, selection, start_points, transfer_attributes); + }; + auto single_point_poly = [&](IndexMask selection) { + convert_point_polygonal_curves( + src_curves, dst_curves, selection, start_points, transfer_attributes); + }; + auto single_point_bezier = [&](IndexMask selection) { + convert_point_bezier_curves( + src_curves, dst_curves, selection, start_points, transfer_attributes); + }; + auto single_point_evaluated = [&](IndexMask selection) { + convert_point_evaluated_curves( + src_curves, dst_curves, selection, start_points, transfer_attributes); + }; + + /* Populate point domain. */ + bke::curves::foreach_curve_by_type(src_curves.curve_types(), + src_curves.curve_type_counts(), + curve_indices.as_span(), + trim_catmull, + trim_poly, + trim_bezier, + trim_evaluated); + + if (point_curve_indices.size()) { + bke::curves::foreach_curve_by_type(src_curves.curve_types(), + src_curves.curve_type_counts(), + point_curve_indices.as_span(), + single_point_catmull, + single_point_poly, + single_point_bezier, + single_point_evaluated); + } + /* Cleanup/close context */ + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute.dst.finish(); + } + + /* Copy unselected */ + if (!inverse_selection.is_empty()) { + bke::copy_attribute_domain( + src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE); + /* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */ + dst_curves.cyclic_for_write().fill_indices(selection, false); + + /* Copy point domain. */ + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { + bke::curves::copy_point_data( + src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span); + attribute.dst.finish(); + } + } + + dst_curves.tag_topology_changed(); + return dst_curves; +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/uv_parametrizer.cc b/source/blender/geometry/intern/uv_parametrizer.cc index 4f763b09bef..f074febe23a 100644 --- a/source/blender/geometry/intern/uv_parametrizer.cc +++ b/source/blender/geometry/intern/uv_parametrizer.cc @@ -307,12 +307,70 @@ static float p_vec2_angle(const float v1[2], const float v2[2], const float v3[2 { return angle_v2v2v2(v1, v2, v3); } + +/* Angles close to 0 or 180 degrees cause rows filled with zeros in the linear_solver. + * The matrix will then be rank deficient and / or have poor conditioning. + * => Reduce the maximum angle to 179 degrees, and spread the remainder to the other angles. + */ +static void fix_large_angle(const float v_fix[3], + const float v1[3], + const float v2[3], + float *r_fix, + float *r_a1, + float *r_a2) +{ + const float max_angle = (float)M_PI * (179.0f / 180.0f); + const float fix_amount = *r_fix - max_angle; + if (fix_amount < 0.0f) { + return; /* angle is reasonable, i.e. less than 179 degrees. */ + } + + /* The triangle is probably degenerate, or close to it. + * Without loss of generality, transform the triangle such that + * v_fix == { 0, s}, *r_fix = 180 degrees + * v1 == {-x1, 0}, *r_a1 = 0 + * v2 == { x2, 0}, *r_a2 = 0 + * + * With `s = 0`, `x1 > 0`, `x2 > 0` + * + * Now make `s` a small number and do some math: + * tan(*r_a1) = s / x1 + * tan(*r_a2) = s / x2 + * + * Remember that `tan = sin / cos`, `sin(s) ~= s` and `cos(s) = 1` + * + * Rearrange to obtain: + * *r_a1 = fix_amount * x2 / (x1 + x2) + * *r_a2 = fix_amount * x1 / (x1 + x2) + */ + + const float dist_v1 = len_v3v3(v_fix, v1); + const float dist_v2 = len_v3v3(v_fix, v2); + const float sum = dist_v1 + dist_v2; + const float weight = (sum > 1e-20f) ? dist_v2 / sum : 0.5f; + + /* Ensure sum of angles in triangle is unchanged. */ + *r_fix -= fix_amount; + *r_a1 += fix_amount * weight; + *r_a2 += fix_amount * (1.0f - weight); +} + static void p_triangle_angles( const float v1[3], const float v2[3], const float v3[3], float *r_a1, float *r_a2, float *r_a3) { *r_a1 = p_vec_angle(v3, v1, v2); *r_a2 = p_vec_angle(v1, v2, v3); - *r_a3 = (float)M_PI - *r_a2 - *r_a1; + *r_a3 = p_vec_angle(v2, v3, v1); + + /* Fix for degenerate geometry e.g. v1 = sum(v2 + v3). See T100874 */ + fix_large_angle(v1, v2, v3, r_a1, r_a2, r_a3); + fix_large_angle(v2, v3, v1, r_a2, r_a3, r_a1); + fix_large_angle(v3, v1, v2, r_a3, r_a1, r_a2); + + /* Workaround for degenerate geometry, e.g. v1 == v2 == v3. */ + *r_a1 = max_ff(*r_a1, 0.001f); + *r_a2 = max_ff(*r_a2, 0.001f); + *r_a3 = max_ff(*r_a3, 0.001f); } static void p_face_angles(PFace *f, float *r_a1, float *r_a2, float *r_a3) @@ -2266,7 +2324,6 @@ using PAbfSystem = struct PAbfSystem { float *bAlpha, *bTriangle, *bInterior; float *lambdaTriangle, *lambdaPlanar, *lambdaLength; float (*J2dt)[3], *bstar, *dstar; - float minangle, maxangle; }; static void p_abf_setup_system(PAbfSystem *sys) @@ -2294,9 +2351,6 @@ static void p_abf_setup_system(PAbfSystem *sys) for (i = 0; i < sys->ninterior; i++) { sys->lambdaLength[i] = 1.0; } - - sys->minangle = 1.0 * M_PI / 180.0; - sys->maxangle = (float)M_PI - sys->minangle; } static void p_abf_free_system(PAbfSystem *sys) @@ -2707,25 +2761,6 @@ static bool p_chart_abf_solve(PChart *chart) e3 = e2->next; p_face_angles(f, &a1, &a2, &a3); - if (a1 < sys.minangle) { - a1 = sys.minangle; - } - else if (a1 > sys.maxangle) { - a1 = sys.maxangle; - } - if (a2 < sys.minangle) { - a2 = sys.minangle; - } - else if (a2 > sys.maxangle) { - a2 = sys.maxangle; - } - if (a3 < sys.minangle) { - a3 = sys.minangle; - } - else if (a3 > sys.maxangle) { - a3 = sys.maxangle; - } - sys.alpha[e1->u.id] = sys.beta[e1->u.id] = a1; sys.alpha[e2->u.id] = sys.beta[e2->u.id] = a2; sys.alpha[e3->u.id] = sys.beta[e3->u.id] = a3; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 47d4feb7ec9..8b38c22ae28 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -326,6 +326,7 @@ set(GLSL_SRC shaders/compositor/compositor_alpha_crop.glsl shaders/compositor/compositor_bilateral_blur.glsl + shaders/compositor/compositor_blur.glsl shaders/compositor/compositor_bokeh_image.glsl shaders/compositor/compositor_box_mask.glsl shaders/compositor/compositor_convert.glsl @@ -345,8 +346,11 @@ set(GLSL_SRC shaders/compositor/compositor_screen_lens_distortion.glsl shaders/compositor/compositor_set_alpha.glsl shaders/compositor/compositor_split_viewer.glsl + shaders/compositor/compositor_symmetric_blur.glsl + shaders/compositor/compositor_symmetric_separable_blur.glsl shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl + shaders/compositor/library/gpu_shader_compositor_blur_common.glsl shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl @@ -601,6 +605,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/compositor/infos/compositor_alpha_crop_info.hh shaders/compositor/infos/compositor_bilateral_blur_info.hh + shaders/compositor/infos/compositor_blur_info.hh shaders/compositor/infos/compositor_bokeh_image_info.hh shaders/compositor/infos/compositor_box_mask_info.hh shaders/compositor/infos/compositor_convert_info.hh @@ -620,6 +625,8 @@ set(SRC_SHADER_CREATE_INFOS shaders/compositor/infos/compositor_screen_lens_distortion_info.hh shaders/compositor/infos/compositor_set_alpha_info.hh shaders/compositor/infos/compositor_split_viewer_info.hh + shaders/compositor/infos/compositor_symmetric_blur_info.hh + shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh ) set(SRC_SHADER_CREATE_INFOS_MTL diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index d1d91cb7508..5cdc5f19540 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -49,7 +49,6 @@ typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers; */ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct Mesh *mesh, const struct MLoopTri *looptri, - const int *sculpt_face_sets, const int *face_indices, int face_indices_len); diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 8e3058b884d..78f595cbff2 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -210,13 +210,9 @@ static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers, GPUPrimType prim) /** \name Mesh PBVH * \{ */ -static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt, - const bool *hide_vert, - const MLoop *mloop, - const int *sculpt_face_sets) +static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt, const bool *hide_poly) { - return (!paint_is_face_hidden(lt, hide_vert, mloop) && sculpt_face_sets && - sculpt_face_sets[lt->poly] > SCULPT_FACE_SET_NONE); + return !paint_is_face_hidden(lt, hide_poly); } void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, @@ -233,8 +229,8 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, GPUAttrRef vcol_refs[MAX_GPU_ATTR]; GPUAttrRef cd_uvs[MAX_GPU_ATTR]; - const bool *hide_vert = (const bool *)CustomData_get_layer_named( - &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly"); const int *material_indices = (const int *)CustomData_get_layer_named( &mesh->pdata, CD_PROP_INT32, "material_index"); @@ -315,7 +311,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, for (uint i = 0; i < buffers->face_indices_len; i++) { const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]]; - if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) { continue; } @@ -355,7 +351,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, buffers->mloop[lt->tri[2]].v, }; - if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) { continue; } @@ -395,7 +391,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, buffers->mloop[lt->tri[2]].v, }; - if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) { continue; } @@ -459,7 +455,6 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, const MLoopTri *looptri, - const int *sculpt_face_sets, const int *face_indices, const int face_indices_len) { @@ -472,8 +467,8 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers"); - const bool *hide_vert = (bool *)CustomData_get_layer_named( - &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + const bool *hide_poly = (bool *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly"); /* smooth or flat for all */ buffers->smooth = polys[looptri[face_indices[0]].poly].flag & ME_SMOOTH; @@ -483,7 +478,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, /* Count the number of visible triangles */ for (i = 0, tottri = 0; i < face_indices_len; i++) { const MLoopTri *lt = &looptri[face_indices[i]]; - if (gpu_pbvh_is_looptri_visible(lt, hide_vert, loops, sculpt_face_sets)) { + if (gpu_pbvh_is_looptri_visible(lt, hide_poly)) { int r_edges[3]; BKE_mesh_looptri_get_real_edges(mesh, lt, r_edges); for (int j = 0; j < 3; j++) { @@ -516,7 +511,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, const MLoopTri *lt = &looptri[face_indices[i]]; /* Skip hidden faces */ - if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, loops, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) { continue; } diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index 0102b8db5b2..75e148e0a8f 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -11,6 +11,7 @@ #include "DNA_customdata_types.h" #include "DNA_image_types.h" +#include "DNA_material_types.h" #include "BLI_ghash.h" #include "BLI_hash_mm2a.h" @@ -20,6 +21,7 @@ #include "PIL_time.h" +#include "BKE_cryptomatte.hh" #include "BKE_material.h" #include "GPU_capabilities.h" @@ -238,6 +240,7 @@ class GPUCodegen { uint32_t hash_ = 0; BLI_HashMurmur2A hm2a_; ListBase ubo_inputs_ = {nullptr, nullptr}; + GPUInput *cryptomatte_input_ = nullptr; public: GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_) @@ -262,11 +265,13 @@ class GPUCodegen { MEM_SAFE_FREE(output.displacement); MEM_SAFE_FREE(output.composite); MEM_SAFE_FREE(output.material_functions); + MEM_SAFE_FREE(cryptomatte_input_); delete create_info; BLI_freelistN(&ubo_inputs_); }; void generate_graphs(); + void generate_cryptomatte(); void generate_uniform_buffer(); void generate_attribs(); void generate_resources(); @@ -399,7 +404,12 @@ void GPUCodegen::generate_resources() ss << "struct NodeTree {\n"; LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) { GPUInput *input = (GPUInput *)(link->data); - ss << input->type << " u" << input->id << ";\n"; + if (input->source == GPU_SOURCE_CRYPTOMATTE) { + ss << input->type << " crypto_hash;\n"; + } + else { + ss << input->type << " u" << input->id << ";\n"; + } } ss << "};\n\n"; @@ -535,6 +545,24 @@ char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag) return eval_c_str; } +void GPUCodegen::generate_cryptomatte() +{ + cryptomatte_input_ = static_cast<GPUInput *>(MEM_callocN(sizeof(GPUInput), __func__)); + cryptomatte_input_->type = GPU_FLOAT; + cryptomatte_input_->source = GPU_SOURCE_CRYPTOMATTE; + + float material_hash = 0.0f; + Material *material = GPU_material_get_material(&mat); + if (material) { + blender::bke::cryptomatte::CryptomatteHash hash(material->id.name, + BLI_strnlen(material->id.name, MAX_NAME - 2)); + material_hash = hash.float_encoded(); + } + cryptomatte_input_->vec[0] = material_hash; + + BLI_addtail(&ubo_inputs_, BLI_genericNodeN(cryptomatte_input_)); +} + void GPUCodegen::generate_uniform_buffer() { /* Extract uniform inputs. */ @@ -615,6 +643,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, GPUCodegen codegen(material, graph); codegen.generate_graphs(); + codegen.generate_cryptomatte(); codegen.generate_uniform_buffer(); /* Cache lookup: Reuse shaders already compiled. */ diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 08ff8bbef58..74afb721a1c 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -35,6 +35,7 @@ typedef enum eGPUDataSource { GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING, GPU_SOURCE_FUNCTION_CALL, + GPU_SOURCE_CRYPTOMATTE, } eGPUDataSource; typedef enum { diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index e15054bd045..db14d7fbeb9 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -136,9 +136,7 @@ eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id), /* -------------------------------------------------------------------- */ /** \name Stubs of BKE_paint.h * \{ */ -bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt), - const bool *UNUSED(hide_vert), - const struct MLoop *UNUSED(mloop)) +bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt), const bool *UNUSED(hide_poly)) { BLI_assert_unreachable(); return false; diff --git a/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh index 6af67ad44d2..469e488c176 100644 --- a/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh +++ b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh @@ -20,4 +20,4 @@ GPU_SHADER_CREATE_INFO(fullscreen_blit) .sampler(0, ImageType::FLOAT_2D, "imageTexture", Frequency::PASS) .vertex_source("gpu_shader_fullscreen_blit_vert.glsl") .fragment_source("gpu_shader_fullscreen_blit_frag.glsl") - .do_static_compilation(true);
\ No newline at end of file + .do_static_compilation(true); diff --git a/source/blender/gpu/metal/mtl_index_buffer.hh b/source/blender/gpu/metal/mtl_index_buffer.hh index fde26b16927..702aa7f27d6 100644 --- a/source/blender/gpu/metal/mtl_index_buffer.hh +++ b/source/blender/gpu/metal/mtl_index_buffer.hh @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup gpu diff --git a/source/blender/gpu/metal/mtl_index_buffer.mm b/source/blender/gpu/metal/mtl_index_buffer.mm index 99795d7bbd9..2195ab7538d 100644 --- a/source/blender/gpu/metal/mtl_index_buffer.mm +++ b/source/blender/gpu/metal/mtl_index_buffer.mm @@ -1,7 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup gpu */ + #include "mtl_index_buffer.hh" #include "mtl_context.hh" #include "mtl_debug.hh" diff --git a/source/blender/gpu/metal/mtl_primitive.hh b/source/blender/gpu/metal/mtl_primitive.hh index 5aa7a533b95..b32854a04bf 100644 --- a/source/blender/gpu/metal/mtl_primitive.hh +++ b/source/blender/gpu/metal/mtl_primitive.hh @@ -97,4 +97,4 @@ static inline bool mtl_vertex_count_fits_primitive_type(uint32_t vertex_count, return false; } -} // namespace blender::gpu
\ No newline at end of file +} // namespace blender::gpu diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl index db8e114ec7a..e68c173c055 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl @@ -26,9 +26,29 @@ vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3 return values + parameters * slopes; } -/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize - * parameters accordingly. */ -#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range) +/* Curve maps are stored in texture samplers that are evaluated in the [0, 1] range, so normalize + * the parameters accordingly. Additionally, ensure that the parameters evaluate the sampler at the + * center of the pixels, because samplers are evaluated using linear interpolation. */ +float normalize_parameter(float parameter, float minimum, float range_divider) +{ + float normalized_parameter = (parameter - minimum) * range_divider; + + /* Curve maps have a fixed width of 257. We offset by the equivalent of half a pixel and scale + * down such that the normalized parameter 1.0 corresponds to the center of the last pixel. */ + float sampler_offset = 0.5 / 257.0; + float sampler_scale = 1.0 - (1.0 / 257.0); + return normalized_parameter * sampler_scale + sampler_offset; +} + +/* Same as normalize_parameter but vectorized. */ +vec3 normalize_parameters(vec3 parameters, vec3 minimums, vec3 range_dividers) +{ + vec3 normalized_parameters = (parameters - minimums) * range_dividers; + + float sampler_offset = 0.5 / 257.0; + float sampler_scale = 1.0 - (1.0 / 257.0); + return normalized_parameters * sampler_scale + sampler_offset; +} void curves_combined_rgb(float factor, vec4 color, @@ -46,7 +66,7 @@ void curves_combined_rgb(float factor, /* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the * UI. */ - vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa); + vec3 parameters = normalize_parameters(balanced.rgb, range_minimums.aaa, range_dividers.aaa); result.r = texture(curve_map, vec2(parameters.x, layer)).a; result.g = texture(curve_map, vec2(parameters.y, layer)).a; result.b = texture(curve_map, vec2(parameters.z, layer)).a; @@ -55,13 +75,14 @@ void curves_combined_rgb(float factor, result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa); /* Then, evaluate each channel on its curve map. */ - parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb); + parameters = normalize_parameters(result.rgb, range_minimums.rgb, range_dividers.rgb); result.r = texture(curve_map, vec2(parameters.r, layer)).r; result.g = texture(curve_map, vec2(parameters.g, layer)).g; result.b = texture(curve_map, vec2(parameters.b, layer)).b; /* Then, extrapolate again if needed. */ result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb); + result.a = color.a; result = mix(color, result, factor); @@ -83,13 +104,14 @@ void curves_combined_only(float factor, /* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the * UI. */ - vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider); + vec3 parameters = normalize_parameters(balanced.rgb, vec3(range_minimum), vec3(range_divider)); result.r = texture(curve_map, vec2(parameters.x, layer)).a; result.g = texture(curve_map, vec2(parameters.y, layer)).a; result.b = texture(curve_map, vec2(parameters.z, layer)).a; /* Then, extrapolate if needed. */ result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope)); + result.a = color.a; result = mix(color, result, factor); @@ -147,8 +169,8 @@ void curves_film_like(float factor, /* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined * curve in the UI. */ - float min_parameter = NORMALIZE_PARAMETER(minimum, range_minimum, range_divider); - float max_parameter = NORMALIZE_PARAMETER(maximum, range_minimum, range_divider); + float min_parameter = normalize_parameter(minimum, range_minimum, range_divider); + float max_parameter = normalize_parameter(maximum, range_minimum, range_divider); float new_min = texture(curve_map, vec2(min_parameter, layer)).a; float new_max = texture(curve_map, vec2(max_parameter, layer)).a; @@ -165,6 +187,7 @@ void curves_film_like(float factor, vec3 median_or_min = mix(vec3(new_median), vec3(new_min), channel_is_min); bvec3 channel_is_max = equal(balanced.rgb, vec3(maximum)); result.rgb = mix(median_or_min, vec3(new_max), channel_is_max); + result.a = color.a; result = mix(color, result, clamp(factor, 0.0, 1.0)); @@ -180,7 +203,7 @@ void curves_vector(vec3 vector, out vec3 result) { /* Evaluate each component on its curve map. */ - vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers); + vec3 parameters = normalize_parameters(vector, range_minimums, range_dividers); result.x = texture(curve_map, vec2(parameters.x, layer)).x; result.y = texture(curve_map, vec2(parameters.y, layer)).y; result.z = texture(curve_map, vec2(parameters.z, layer)).z; @@ -214,7 +237,7 @@ void curves_float(float value, out float result) { /* Evaluate the normalized value on the first curve map. */ - float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider); + float parameter = normalize_parameter(value, range_minimum, range_divider); result = texture(curve_map, vec2(parameter, layer)).x; /* Then, extrapolate if needed. */ diff --git a/source/blender/gpu/shaders/compositor/compositor_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_blur.glsl new file mode 100644 index 00000000000..4f981c84f59 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_blur.glsl @@ -0,0 +1,55 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +vec4 load_input(ivec2 texel) +{ + vec4 color; + if (extend_bounds) { + /* If bounds are extended, then we treat the input as padded by a radius amount of pixels. So + * we load the input with an offset by the radius amount and fallback to a transparent color if + * it is out of bounds. */ + color = texture_load(input_tx, texel - radius, vec4(0.0)); + } + else { + color = texture_load(input_tx, texel); + } + + return color; +} + +/* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from + * the weights texture, where the texel (0, 0) is considered the center of weights texture. */ +vec4 load_weight(ivec2 texel) +{ + /* Add the radius to transform the texel into the range [0, radius * 2], then divide by the upper + * bound plus one to transform the texel into the normalized range [0, 1] needed to sample the + * weights sampler. Finally, also add 0.5 to sample at the center of the pixels. */ + return texture(weights_tx, (texel + vec2(radius + 0.5)) / (radius * 2 + 1)); +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* The mask input is treated as a boolean. If it is zero, then no blurring happens for this + * pixel. Otherwise, the pixel is blurred normally and the mask value is irrelevant. */ + float mask = texture_load(mask_tx, texel).x; + if (mask == 0.0) { + imageStore(output_img, texel, texture_load(input_tx, texel)); + return; + } + + /* Go over the window of the given radius and accumulate the colors multiplied by their + * respective weights as well as the weights themselves. */ + vec4 accumulated_color = vec4(0.0); + vec4 accumulated_weight = vec4(0.0); + for (int y = -radius; y <= radius; y++) { + for (int x = -radius; x <= radius; x++) { + vec4 weight = load_weight(ivec2(x, y)); + accumulated_color += load_input(texel + ivec2(x, y)) * weight; + accumulated_weight += weight; + } + } + + imageStore(output_img, texel, safe_divide(accumulated_color, accumulated_weight)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl new file mode 100644 index 00000000000..df08991a35c --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl @@ -0,0 +1,77 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_blur_common.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +vec4 load_input(ivec2 texel) +{ + vec4 color; + if (extend_bounds) { + /* If bounds are extended, then we treat the input as padded by a radius amount of pixels. So + * we load the input with an offset by the radius amount and fallback to a transparent color if + * it is out of bounds. Notice that we subtract 1 because the weights texture have an extra + * center weight, see the SymmetricBlurWeights for more information. */ + ivec2 blur_size = texture_size(weights_tx) - 1; + color = texture_load(input_tx, texel - blur_size, vec4(0.0)); + } + else { + color = texture_load(input_tx, texel); + } + + if (gamma_correct) { + color = gamma_correct_blur_input(color); + } + + return color; +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec4 accumulated_color = vec4(0.0); + + /* First, compute the contribution of the center pixel. */ + vec4 center_color = load_input(texel); + accumulated_color += center_color * texture_load(weights_tx, ivec2(0)).x; + + ivec2 weights_size = texture_size(weights_tx); + + /* Then, compute the contributions of the pixels along the x axis of the filter, noting that the + * weights texture only stores the weights for the positive half, but since the filter is + * symmetric, the same weight is used for the negative half and we add both of their + * contributions. */ + for (int x = 1; x < weights_size.x; x++) { + float weight = texture_load(weights_tx, ivec2(x, 0)).x; + accumulated_color += load_input(texel + ivec2(x, 0)) * weight; + accumulated_color += load_input(texel + ivec2(-x, 0)) * weight; + } + + /* Then, compute the contributions of the pixels along the y axis of the filter, noting that the + * weights texture only stores the weights for the positive half, but since the filter is + * symmetric, the same weight is used for the negative half and we add both of their + * contributions. */ + for (int y = 1; y < weights_size.y; y++) { + float weight = texture_load(weights_tx, ivec2(0, y)).x; + accumulated_color += load_input(texel + ivec2(0, y)) * weight; + accumulated_color += load_input(texel + ivec2(0, -y)) * weight; + } + + /* Finally, compute the contributions of the pixels in the four quadrants of the filter, noting + * that the weights texture only stores the weights for the upper right quadrant, but since the + * filter is symmetric, the same weight is used for the rest of the quadrants and we add all four + * of their contributions. */ + for (int y = 1; y < weights_size.y; y++) { + for (int x = 1; x < weights_size.x; x++) { + float weight = texture_load(weights_tx, ivec2(x, y)).x; + accumulated_color += load_input(texel + ivec2(x, y)) * weight; + accumulated_color += load_input(texel + ivec2(-x, y)) * weight; + accumulated_color += load_input(texel + ivec2(x, -y)) * weight; + accumulated_color += load_input(texel + ivec2(-x, -y)) * weight; + } + } + + if (gamma_correct) { + accumulated_color = gamma_uncorrect_blur_output(accumulated_color); + } + + imageStore(output_img, texel, accumulated_color); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl new file mode 100644 index 00000000000..ab0c7baa787 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl @@ -0,0 +1,53 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_blur_common.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +vec4 load_input(ivec2 texel) +{ + vec4 color; + if (extend_bounds) { + /* If bounds are extended, then we treat the input as padded by a radius amount of pixels. So + * we load the input with an offset by the radius amount and fallback to a transparent color if + * it is out of bounds. Notice that we subtract 1 because the weights texture have an extra + * center weight, see the SymmetricSeparableBlurWeights for more information. */ + int blur_size = texture_size(weights_tx) - 1; + color = texture_load(input_tx, texel - ivec2(blur_size, 0), vec4(0.0)); + } + else { + color = texture_load(input_tx, texel); + } + + if (gamma_correct_input) { + color = gamma_correct_blur_input(color); + } + + return color; +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec4 accumulated_color = vec4(0.0); + + /* First, compute the contribution of the center pixel. */ + vec4 center_color = load_input(texel); + accumulated_color += center_color * texture_load(weights_tx, 0).x; + + /* Then, compute the contributions of the pixel to the right and left, noting that the + * weights texture only stores the weights for the positive half, but since the filter is + * symmetric, the same weight is used for the negative half and we add both of their + * contributions. */ + for (int i = 1; i < texture_size(weights_tx); i++) { + float weight = texture_load(weights_tx, i).x; + accumulated_color += load_input(texel + ivec2(i, 0)) * weight; + accumulated_color += load_input(texel + ivec2(-i, 0)) * weight; + } + + if (gamma_uncorrect_output) { + accumulated_color = gamma_uncorrect_blur_output(accumulated_color); + } + + /* Write the color using the transposed texel. See the execute_separable_blur_horizontal_pass + * method for more information on the rational behind this. */ + imageStore(output_img, texel.yx, accumulated_color); +} diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh new file mode 100644 index 00000000000..36b772aa486 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_blur) + .local_group_size(16, 16) + .push_constant(Type::INT, "radius") + .push_constant(Type::BOOL, "extend_bounds") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .sampler(1, ImageType::FLOAT_2D, "weights_tx") + .sampler(2, ImageType::FLOAT_2D, "mask_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_blur.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh new file mode 100644 index 00000000000..8ba2b4e04ef --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_symmetric_blur) + .local_group_size(16, 16) + .push_constant(Type::BOOL, "extend_bounds") + .push_constant(Type::BOOL, "gamma_correct") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .sampler(1, ImageType::FLOAT_2D, "weights_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_symmetric_blur.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh new file mode 100644 index 00000000000..57247dba4b8 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_symmetric_separable_blur) + .local_group_size(16, 16) + .push_constant(Type::BOOL, "extend_bounds") + .push_constant(Type::BOOL, "gamma_correct_input") + .push_constant(Type::BOOL, "gamma_uncorrect_output") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .sampler(1, ImageType::FLOAT_1D, "weights_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_symmetric_separable_blur.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl new file mode 100644 index 00000000000..e404c03bbb0 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl @@ -0,0 +1,32 @@ +/* Preprocess the input of the blur filter by squaring it in its alpha straight form, assuming the + * given color is alpha premultiplied. */ +vec4 gamma_correct_blur_input(vec4 color) +{ + /* Unpremultiply alpha. */ + color.rgb /= color.a > 0.0 ? color.a : 1.0; + + /* Square color channel if it is positive, otherwise zero it. */ + color.rgb *= mix(color.rgb, vec3(0.0), lessThan(color.rgb, vec3(0.0))); + + /* Premultiply alpha to undo previous alpha unpremultiplication. */ + color.rgb *= color.a > 0.0 ? color.a : 1.0; + + return color; +} + +/* Postprocess the output of the blur filter by taking its square root it in its alpha straight + * form, assuming the given color is alpha premultiplied. This essential undoes the processing done + * by the gamma_correct_blur_input function. */ +vec4 gamma_uncorrect_blur_output(vec4 color) +{ + /* Unpremultiply alpha. */ + color.rgb /= color.a > 0.0 ? color.a : 1.0; + + /* Take the square root of the color channel if it is positive, otherwise zero it. */ + color.rgb = mix(sqrt(color.rgb), vec3(0.0), lessThan(color.rgb, vec3(0.0))); + + /* Premultiply alpha to undo previous alpha unpremultiplication. */ + color.rgb *= color.a > 0.0 ? color.a : 1.0; + + return color; +} diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 6881916d1d2..7e652e31506 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -809,7 +809,7 @@ bool imb_addrectImBuf(struct ImBuf *ibuf); */ void imb_freerectImBuf(struct ImBuf *ibuf); -bool imb_addrectfloatImBuf(struct ImBuf *ibuf); +bool imb_addrectfloatImBuf(struct ImBuf *ibuf, const unsigned int channels); /** * Any free `ibuf->rect` frees mipmaps to be sure, creation is in render on first request. */ diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 45d05e9b856..03bb11d0cf6 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -166,8 +166,6 @@ typedef enum eImBufFlags { * \{ */ typedef struct ImBuf { - struct ImBuf *next, *prev; /** < allow lists of #ImBufs, for caches or flip-books. */ - /* dimensions */ /** Width and Height of our image buffer. * Should be 'unsigned int' since most formats use this. diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 9a0a6998fab..bd17316d173 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -264,6 +264,12 @@ struct ImBuf *imb_loadwebp(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); +struct ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[], + size_t *r_width, + size_t *r_height); bool imb_savewebp(struct ImBuf *ibuf, const char *name, int flags); /** \} */ diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 8b9ad94de0c..42b587c3c81 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -258,7 +258,7 @@ bool addzbufImBuf(ImBuf *ibuf) IMB_freezbufImBuf(ibuf); - if ((ibuf->zbuf = imb_alloc_pixels(ibuf->x, ibuf->y, 1, sizeof(unsigned int), __func__))) { + if ((ibuf->zbuf = imb_alloc_pixels(ibuf->x, ibuf->y, 1, sizeof(uint), __func__))) { ibuf->mall |= IB_zbuf; ibuf->flags |= IB_zbuf; return true; @@ -309,7 +309,7 @@ bool imb_addencodedbufferImBuf(ImBuf *ibuf) bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf) { - unsigned int newsize, encodedsize; + uint newsize, encodedsize; void *newbuffer; if (ibuf == NULL) { @@ -351,8 +351,7 @@ bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf) return true; } -void *imb_alloc_pixels( - unsigned int x, unsigned int y, unsigned int channels, size_t typesize, const char *name) +void *imb_alloc_pixels(uint x, uint y, uint channels, size_t typesize, const char *name) { /* Protect against buffer overflow vulnerabilities from files specifying * a width and height that overflow and alloc too little memory. */ @@ -364,7 +363,7 @@ void *imb_alloc_pixels( return MEM_callocN(size, name); } -bool imb_addrectfloatImBuf(ImBuf *ibuf) +bool imb_addrectfloatImBuf(ImBuf *ibuf, const uint channels) { if (ibuf == NULL) { return false; @@ -374,8 +373,8 @@ bool imb_addrectfloatImBuf(ImBuf *ibuf) imb_freerectfloatImBuf(ibuf); /* frees mipmap too, hrm */ } - ibuf->channels = 4; - if ((ibuf->rect_float = imb_alloc_pixels(ibuf->x, ibuf->y, 4, sizeof(float), __func__))) { + ibuf->channels = channels; + if ((ibuf->rect_float = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(float), __func__))) { ibuf->mall |= IB_rectfloat; ibuf->flags |= IB_rectfloat; return true; @@ -399,7 +398,7 @@ bool imb_addrectImBuf(ImBuf *ibuf) } ibuf->rect = NULL; - if ((ibuf->rect = imb_alloc_pixels(ibuf->x, ibuf->y, 4, sizeof(unsigned char), __func__))) { + if ((ibuf->rect = imb_alloc_pixels(ibuf->x, ibuf->y, 4, sizeof(uchar), __func__))) { ibuf->mall |= IB_rect; ibuf->flags |= IB_rect; if (ibuf->planes > 32) { @@ -412,8 +411,7 @@ bool imb_addrectImBuf(ImBuf *ibuf) return false; } -struct ImBuf *IMB_allocFromBufferOwn( - unsigned int *rect, float *rectf, unsigned int w, unsigned int h, unsigned int channels) +struct ImBuf *IMB_allocFromBufferOwn(uint *rect, float *rectf, uint w, uint h, uint channels) { ImBuf *ibuf = NULL; @@ -444,11 +442,8 @@ struct ImBuf *IMB_allocFromBufferOwn( return ibuf; } -struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, - const float *rectf, - unsigned int w, - unsigned int h, - unsigned int channels) +struct ImBuf *IMB_allocFromBuffer( + const uint *rect, const float *rectf, uint w, uint h, uint channels) { ImBuf *ibuf = NULL; @@ -488,8 +483,7 @@ bool imb_addtilesImBuf(ImBuf *ibuf) } if (!ibuf->tiles) { - if ((ibuf->tiles = MEM_callocN(sizeof(unsigned int *) * ibuf->xtiles * ibuf->ytiles, - "imb_tiles"))) { + if ((ibuf->tiles = MEM_callocN(sizeof(uint *) * ibuf->xtiles * ibuf->ytiles, "imb_tiles"))) { ibuf->mall |= IB_tiles; } } @@ -497,7 +491,7 @@ bool imb_addtilesImBuf(ImBuf *ibuf) return (ibuf->tiles != NULL); } -ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int flags) +ImBuf *IMB_allocImBuf(uint x, uint y, uchar planes, uint flags) { ImBuf *ibuf; @@ -513,8 +507,7 @@ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int return ibuf; } -bool IMB_initImBuf( - struct ImBuf *ibuf, unsigned int x, unsigned int y, unsigned char planes, unsigned int flags) +bool IMB_initImBuf(struct ImBuf *ibuf, uint x, uint y, uchar planes, uint flags) { memset(ibuf, 0, sizeof(ImBuf)); @@ -536,7 +529,7 @@ bool IMB_initImBuf( } if (flags & IB_rectfloat) { - if (imb_addrectfloatImBuf(ibuf) == false) { + if (imb_addrectfloatImBuf(ibuf, ibuf->channels) == false) { return false; } } @@ -678,7 +671,7 @@ size_t IMB_get_size_in_memory(ImBuf *ibuf) } if (ibuf->tiles) { - size += sizeof(unsigned int) * ibuf->ytiles * ibuf->xtiles; + size += sizeof(uint) * ibuf->ytiles * ibuf->xtiles; } return size; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 52ed68a1ff3..4e6a52f8464 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -97,9 +97,9 @@ static void free_anim_movie(struct anim *UNUSED(anim)) # define PATHSEPARATOR '/' #endif -static int an_stringdec(const char *string, char *head, char *tail, unsigned short *numlen) +static int an_stringdec(const char *string, char *head, char *tail, ushort *numlen) { - unsigned short len, nume, nums = 0; + ushort len, nume, nums = 0; short i; bool found = false; @@ -139,8 +139,7 @@ static int an_stringdec(const char *string, char *head, char *tail, unsigned sho return true; } -static void an_stringenc( - char *string, const char *head, const char *tail, unsigned short numlen, int pic) +static void an_stringenc(char *string, const char *head, const char *tail, ushort numlen, int pic) { BLI_path_sequence_encode(string, head, tail, numlen, pic); } @@ -454,7 +453,7 @@ static ImBuf *avi_fetchibuf(struct anim *anim, int position) lpbi = AVIStreamGetFrame(anim->pgf, position + AVIStreamStart(anim->pavi[anim->firstvideo])); if (lpbi) { ibuf = IMB_ibImageFromMemory( - (const unsigned char *)lpbi, 100, IB_rect, anim->colorspace, "<avi_fetchibuf>"); + (const uchar *)lpbi, 100, IB_rect, anim->colorspace, "<avi_fetchibuf>"); /* Oh brother... */ } } @@ -1568,7 +1567,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, { struct ImBuf *ibuf = NULL; char head[256], tail[256]; - unsigned short digits; + ushort digits; int pic; int filter_y; if (anim == NULL) { diff --git a/source/blender/imbuf/intern/cache.c b/source/blender/imbuf/intern/cache.c index 51f7dbdf41a..4e1563c62ab 100644 --- a/source/blender/imbuf/intern/cache.c +++ b/source/blender/imbuf/intern/cache.c @@ -81,11 +81,11 @@ static ImGlobalTileCache GLOBAL_CACHE; /** \name Hash Functions * \{ */ -static unsigned int imb_global_tile_hash(const void *gtile_p) +static uint imb_global_tile_hash(const void *gtile_p) { const ImGlobalTile *gtile = gtile_p; - return ((unsigned int)(intptr_t)gtile->ibuf) * 769 + gtile->tx * 53 + gtile->ty * 97; + return ((uint)(intptr_t)gtile->ibuf) * 769 + gtile->tx * 53 + gtile->ty * 97; } static bool imb_global_tile_cmp(const void *a_p, const void *b_p) @@ -96,11 +96,11 @@ static bool imb_global_tile_cmp(const void *a_p, const void *b_p) return ((a->ibuf != b->ibuf) || (a->tx != b->tx) || (a->ty != b->ty)); } -static unsigned int imb_thread_tile_hash(const void *ttile_p) +static uint imb_thread_tile_hash(const void *ttile_p) { const ImThreadTile *ttile = ttile_p; - return ((unsigned int)(intptr_t)ttile->ibuf) * 769 + ttile->tx * 53 + ttile->ty * 97; + return ((uint)(intptr_t)ttile->ibuf) * 769 + ttile->tx * 53 + ttile->ty * 97; } static bool imb_thread_tile_cmp(const void *a_p, const void *b_p) @@ -121,9 +121,9 @@ static void imb_global_cache_tile_load(ImGlobalTile *gtile) { ImBuf *ibuf = gtile->ibuf; int toffs = ibuf->xtiles * gtile->ty + gtile->tx; - unsigned int *rect; + uint *rect; - rect = MEM_callocN(sizeof(unsigned int) * ibuf->tilex * ibuf->tiley, "imb_tile"); + rect = MEM_callocN(sizeof(uint) * ibuf->tilex * ibuf->tiley, "imb_tile"); imb_loadtile(ibuf, gtile->tx, gtile->ty, rect); ibuf->tiles[toffs] = rect; } @@ -136,7 +136,7 @@ static void imb_global_cache_tile_unload(ImGlobalTile *gtile) MEM_freeN(ibuf->tiles[toffs]); ibuf->tiles[toffs] = NULL; - GLOBAL_CACHE.totmem -= sizeof(unsigned int) * ibuf->tilex * ibuf->tiley; + GLOBAL_CACHE.totmem -= sizeof(uint) * ibuf->tilex * ibuf->tiley; } void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty) @@ -343,7 +343,7 @@ static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf, BLI_addhead(&GLOBAL_CACHE.tiles, gtile); /* mark as being loaded and unlock to allow other threads to load too */ - GLOBAL_CACHE.totmem += sizeof(unsigned int) * ibuf->tilex * ibuf->tiley; + GLOBAL_CACHE.totmem += sizeof(uint) * ibuf->tilex * ibuf->tiley; BLI_mutex_unlock(&GLOBAL_CACHE.mutex); @@ -363,10 +363,7 @@ static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf, /** \name Per-Thread Cache * \{ */ -static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache, - ImBuf *ibuf, - int tx, - int ty) +static uint *imb_thread_cache_get_tile(ImThreadTileCache *cache, ImBuf *ibuf, int tx, int ty) { ImThreadTile *ttile, lookuptile; ImGlobalTile *gtile, *replacetile; @@ -418,7 +415,7 @@ static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache, return ibuf->tiles[toffs]; } -unsigned int *IMB_gettile(ImBuf *ibuf, int tx, int ty, int thread) +uint *IMB_gettile(ImBuf *ibuf, int tx, int ty, int thread) { return imb_thread_cache_get_tile(&GLOBAL_CACHE.thread_cache[thread + 1], ibuf, tx, ty); } @@ -427,7 +424,7 @@ void IMB_tiles_to_rect(ImBuf *ibuf) { ImBuf *mipbuf; ImGlobalTile *gtile; - unsigned int *to, *from; + uint *to, *from; int a, tx, ty, y, w, h; for (a = 0; a < ibuf->miptot; a++) { @@ -435,8 +432,7 @@ void IMB_tiles_to_rect(ImBuf *ibuf) /* don't call imb_addrectImBuf, it frees all mipmaps */ if (!mipbuf->rect) { - if ((mipbuf->rect = MEM_callocN(ibuf->x * ibuf->y * sizeof(unsigned int), - "imb_addrectImBuf"))) { + if ((mipbuf->rect = MEM_callocN(ibuf->x * ibuf->y * sizeof(uint), "imb_addrectImBuf"))) { mipbuf->mall |= IB_rect; mipbuf->flags |= IB_rect; } @@ -460,7 +456,7 @@ void IMB_tiles_to_rect(ImBuf *ibuf) h = (ty == mipbuf->ytiles - 1) ? mipbuf->y - ty * mipbuf->tiley : mipbuf->tiley; for (y = 0; y < h; y++) { - memcpy(to, from, sizeof(unsigned int) * w); + memcpy(to, from, sizeof(uint) * w); from += mipbuf->tilex; to += mipbuf->x; } diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c index 1a99d2a34d9..3bff8184b19 100644 --- a/source/blender/imbuf/intern/cineon/cineon_dpx.c +++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c @@ -21,11 +21,8 @@ #include "MEM_guardedalloc.h" -static struct ImBuf *imb_load_dpx_cineon(const unsigned char *mem, - size_t size, - int use_cineon, - int flags, - char colorspace[IM_MAX_SPACE]) +static struct ImBuf *imb_load_dpx_cineon( + const uchar *mem, size_t size, int use_cineon, int flags, char colorspace[IM_MAX_SPACE]) { ImBuf *ibuf; LogImageFile *image; @@ -74,7 +71,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filepath, int use_cineon LogImageFile *logImage; float *fbuf; float *fbuf_ptr; - unsigned char *rect_ptr; + uchar *rect_ptr; int x, y, depth, bitspersample, rvalue; if (flags & IB_mem) { @@ -153,7 +150,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filepath, int use_cineon for (y = 0; y < ibuf->y; y++) { for (x = 0; x < ibuf->x; x++) { fbuf_ptr = fbuf + 4 * ((ibuf->y - y - 1) * ibuf->x + x); - rect_ptr = (unsigned char *)ibuf->rect + 4 * (y * ibuf->x + x); + rect_ptr = (uchar *)ibuf->rect + 4 * (y * ibuf->x + x); fbuf_ptr[0] = (float)rect_ptr[0] / 255.0f; fbuf_ptr[1] = (float)rect_ptr[1] / 255.0f; fbuf_ptr[2] = (float)rect_ptr[2] / 255.0f; @@ -173,15 +170,12 @@ bool imb_save_cineon(struct ImBuf *buf, const char *filepath, int flags) return imb_save_dpx_cineon(buf, filepath, 1, flags); } -bool imb_is_a_cineon(const unsigned char *buf, size_t size) +bool imb_is_a_cineon(const uchar *buf, size_t size) { return logImageIsCineon(buf, size); } -ImBuf *imb_load_cineon(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_cineon(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { if (!imb_is_a_cineon(mem, size)) { return NULL; @@ -194,15 +188,12 @@ bool imb_save_dpx(struct ImBuf *buf, const char *filepath, int flags) return imb_save_dpx_cineon(buf, filepath, 0, flags); } -bool imb_is_a_dpx(const unsigned char *buf, size_t size) +bool imb_is_a_dpx(const uchar *buf, size_t size) { return logImageIsDpx(buf, size); } -ImBuf *imb_load_dpx(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_dpx(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { if (!imb_is_a_dpx(mem, size)) { return NULL; diff --git a/source/blender/imbuf/intern/cineon/cineonlib.c b/source/blender/imbuf/intern/cineon/cineonlib.c index 8312476bda0..6417d92644f 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.c +++ b/source/blender/imbuf/intern/cineon/cineonlib.c @@ -18,6 +18,7 @@ #include <time.h> #include "BLI_fileops.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" @@ -56,9 +57,8 @@ static void fillCineonMainHeader(LogImageFile *cineon, cineon->height * getRowLength(cineon->width, cineon->element[0]), cineon->isMSB); - strcpy(header->fileHeader.version, "v4.5"); - strncpy(header->fileHeader.file_name, filepath, 99); - header->fileHeader.file_name[99] = 0; + STRNCPY(header->fileHeader.version, "v4.5"); + STRNCPY(header->fileHeader.file_name, filepath); fileClock = time(NULL); fileTime = localtime(&fileClock); strftime(header->fileHeader.creation_date, 12, "%Y:%m:%d", fileTime); @@ -93,8 +93,7 @@ static void fillCineonMainHeader(LogImageFile *cineon, header->imageHeader.green_primary_y = swap_float(0.0f, cineon->isMSB); header->imageHeader.blue_primary_x = swap_float(0.0f, cineon->isMSB); header->imageHeader.blue_primary_y = swap_float(0.0f, cineon->isMSB); - strncpy(header->imageHeader.label, creator, 199); - header->imageHeader.label[199] = 0; + STRNCPY(header->imageHeader.label, creator); header->imageHeader.interleave = 0; header->imageHeader.data_sign = 0; header->imageHeader.sense = 0; @@ -122,13 +121,13 @@ static void fillCineonMainHeader(LogImageFile *cineon, /* we leave it blank */ } -LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize) +LogImageFile *cineonOpen(const uchar *byteStuff, int fromMemory, size_t bufferSize) { CineonMainHeader header; LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); const char *filepath = (const char *)byteStuff; int i; - unsigned int dataOffset; + uint dataOffset; if (cineon == NULL) { if (verbose) { @@ -159,8 +158,8 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t cineon->memBufferSize = 0; } else { - cineon->memBuffer = (unsigned char *)byteStuff; - cineon->memCursor = (unsigned char *)byteStuff; + cineon->memBuffer = (uchar *)byteStuff; + cineon->memCursor = (uchar *)byteStuff; cineon->memBufferSize = bufferSize; } @@ -188,7 +187,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t else { if (verbose) { printf("Cineon: Bad magic number %lu in \"%s\".\n", - (unsigned long)header.fileHeader.magic_num, + (ulong)header.fileHeader.magic_num, byteStuff); } logImageClose(cineon); @@ -297,7 +296,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t } if (cineon->element[i].refHighData == CINEON_UNDEFINED_U32) { - cineon->element[i].refHighData = (unsigned int)cineon->element[i].maxValue; + cineon->element[i].refHighData = (uint)cineon->element[i].maxValue; } if (cineon->element[i].refLowQuantity == CINEON_UNDEFINED_R32 || @@ -354,7 +353,7 @@ LogImageFile *cineonCreate( { CineonMainHeader header; const char *shortFilename = NULL; - /* unsigned char pad[6044]; */ + /* uchar pad[6044]; */ LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); if (cineon == NULL) { diff --git a/source/blender/imbuf/intern/cineon/cineonlib.h b/source/blender/imbuf/intern/cineon/cineonlib.h index 13d40461728..ac0cc15590d 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.h +++ b/source/blender/imbuf/intern/cineon/cineonlib.h @@ -38,10 +38,10 @@ typedef struct { } CineonFileHeader; typedef struct { - unsigned char descriptor1; - unsigned char descriptor2; - unsigned char bits_per_sample; - unsigned char filler; + uchar descriptor1; + uchar descriptor2; + uchar bits_per_sample; + uchar filler; unsigned int pixels_per_line; unsigned int lines_per_image; unsigned int ref_low_data; @@ -51,8 +51,8 @@ typedef struct { } CineonElementHeader; typedef struct { - unsigned char orientation; - unsigned char elements_per_image; + uchar orientation; + uchar elements_per_image; unsigned short filler; CineonElementHeader element[8]; float white_point_x; @@ -65,10 +65,10 @@ typedef struct { float blue_primary_y; char label[200]; char reserved[28]; - unsigned char interleave; - unsigned char packing; - unsigned char data_sign; - unsigned char sense; + uchar interleave; + uchar packing; + uchar data_sign; + uchar sense; unsigned int line_padding; unsigned int element_padding; char reserved2[20]; @@ -90,10 +90,10 @@ typedef struct { } CineonOriginationHeader; typedef struct { - unsigned char film_code; - unsigned char film_type; - unsigned char edge_code_perforation_offset; - unsigned char filler; + uchar film_code; + uchar film_type; + uchar edge_code_perforation_offset; + uchar filler; unsigned int prefix; unsigned int count; char format[32]; @@ -112,7 +112,7 @@ typedef struct { } CineonMainHeader; void cineonSetVerbose(int); -LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize); +LogImageFile *cineonOpen(const uchar *byteStuff, int fromMemory, size_t bufferSize); LogImageFile *cineonCreate( const char *filepath, int width, int height, int bitsPerSample, const char *creator); diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c index 28c19116361..494bf37cfe7 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.c +++ b/source/blender/imbuf/intern/cineon/dpxlib.c @@ -18,6 +18,7 @@ #include <time.h> #include "BLI_fileops.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" @@ -60,14 +61,12 @@ static void fillDpxMainHeader(LogImageFile *dpx, header->fileHeader.ind_hdr_size = swap_uint(sizeof(DpxFilmHeader) + sizeof(DpxTelevisionHeader), dpx->isMSB); header->fileHeader.user_data_size = DPX_UNDEFINED_U32; - strncpy(header->fileHeader.file_name, filename, 99); - header->fileHeader.file_name[99] = 0; + STRNCPY(header->fileHeader.file_name, filename); fileClock = time(NULL); fileTime = localtime(&fileClock); strftime(header->fileHeader.creation_date, 24, "%Y:%m:%d:%H:%M:%S%Z", fileTime); header->fileHeader.creation_date[23] = 0; - strncpy(header->fileHeader.creator, creator, 99); - header->fileHeader.creator[99] = 0; + STRNCPY(header->fileHeader.creator, creator); header->fileHeader.project[0] = 0; header->fileHeader.copyright[0] = 0; header->fileHeader.key = 0xFFFFFFFF; @@ -120,7 +119,7 @@ static void fillDpxMainHeader(LogImageFile *dpx, header->televisionHeader.integration_times = swap_float(DPX_UNDEFINED_R32, dpx->isMSB); } -LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize) +LogImageFile *dpxOpen(const uchar *byteStuff, int fromMemory, size_t bufferSize) { DpxMainHeader header; LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); @@ -156,8 +155,8 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf dpx->memBufferSize = 0; } else { - dpx->memBuffer = (unsigned char *)byteStuff; - dpx->memCursor = (unsigned char *)byteStuff; + dpx->memBuffer = (uchar *)byteStuff; + dpx->memCursor = (uchar *)byteStuff; dpx->memBufferSize = bufferSize; } @@ -321,7 +320,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf } if (dpx->element[i].refHighData == DPX_UNDEFINED_U32) { - dpx->element[i].refHighData = (unsigned int)dpx->element[i].maxValue; + dpx->element[i].refHighData = (uint)dpx->element[i].maxValue; } if (IS_DPX_UNDEFINED_R32(dpx->element[i].refLowQuantity)) { @@ -419,7 +418,7 @@ LogImageFile *dpxCreate(const char *filepath, { DpxMainHeader header; const char *shortFilename = NULL; - unsigned char pad[6044]; + uchar pad[6044]; LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); if (dpx == NULL) { diff --git a/source/blender/imbuf/intern/cineon/logImageCore.c b/source/blender/imbuf/intern/cineon/logImageCore.c index e693aa6f891..8188d0d04b9 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.c +++ b/source/blender/imbuf/intern/cineon/logImageCore.c @@ -81,29 +81,29 @@ void logImageSetVerbose(int verbosity) * IO stuff */ -int logImageIsDpx(const void *buffer, const unsigned int size) +int logImageIsDpx(const void *buffer, const uint size) { - unsigned int magicNum; + uint magicNum; if (size < sizeof(magicNum)) { return 0; } - magicNum = *(unsigned int *)buffer; + magicNum = *(uint *)buffer; return (magicNum == DPX_FILE_MAGIC || magicNum == swap_uint(DPX_FILE_MAGIC, 1)); } -int logImageIsCineon(const void *buffer, const unsigned int size) +int logImageIsCineon(const void *buffer, const uint size) { - unsigned int magicNum; + uint magicNum; if (size < sizeof(magicNum)) { return 0; } - magicNum = *(unsigned int *)buffer; + magicNum = *(uint *)buffer; return (magicNum == CINEON_FILE_MAGIC || magicNum == swap_uint(CINEON_FILE_MAGIC, 1)); } LogImageFile *logImageOpenFromFile(const char *filepath, int cineon) { - unsigned int magicNum; + uint magicNum; FILE *f = BLI_fopen(filepath, "rb"); (void)cineon; @@ -120,16 +120,16 @@ LogImageFile *logImageOpenFromFile(const char *filepath, int cineon) fclose(f); if (logImageIsDpx(&magicNum, sizeof(magicNum))) { - return dpxOpen((const unsigned char *)filepath, 0, 0); + return dpxOpen((const uchar *)filepath, 0, 0); } if (logImageIsCineon(&magicNum, sizeof(magicNum))) { - return cineonOpen((const unsigned char *)filepath, 0, 0); + return cineonOpen((const uchar *)filepath, 0, 0); } return NULL; } -LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int size) +LogImageFile *logImageOpenFromMemory(const uchar *buffer, uint size) { if (logImageIsDpx(buffer, size)) { return dpxOpen(buffer, 1, size); @@ -276,9 +276,9 @@ int logImageSetDataRGBA(LogImageFile *logImage, float *data, int dataIsLinearRGB static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement, float *data) { size_t rowLength = getRowLength(logImage->width, logElement); - unsigned char *row; + uchar *row; - row = (unsigned char *)MEM_mallocN(rowLength, __func__); + row = (uchar *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); @@ -289,7 +289,7 @@ static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement, for (size_t y = 0; y < logImage->height; y++) { for (size_t x = 0; x < logImage->width * logImage->depth; x++) { - row[x] = (unsigned char)float_uint(data[y * logImage->width * logImage->depth + x], 255); + row[x] = (uchar)float_uint(data[y * logImage->width * logImage->depth + x], 255); } if (logimage_fwrite(row, rowLength, 1, logImage) == 0) { @@ -307,10 +307,10 @@ static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement, static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement, float *data) { size_t rowLength = getRowLength(logImage->width, logElement); - unsigned int pixel, index; - unsigned int *row; + uint pixel, index; + uint *row; - row = (unsigned int *)MEM_mallocN(rowLength, __func__); + row = (uint *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); @@ -324,8 +324,7 @@ static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement, pixel = 0; for (size_t x = 0; x < logImage->width * logImage->depth; x++) { - pixel |= (unsigned int)float_uint(data[y * logImage->width * logImage->depth + x], 1023) - << offset; + pixel |= (uint)float_uint(data[y * logImage->width * logImage->depth + x], 1023) << offset; offset -= 10; if (offset < 0) { row[index] = swap_uint(pixel, logImage->isMSB); @@ -353,9 +352,9 @@ static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement, static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement, float *data) { size_t rowLength = getRowLength(logImage->width, logElement); - unsigned short *row; + ushort *row; - row = (unsigned short *)MEM_mallocN(rowLength, __func__); + row = (ushort *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); @@ -366,7 +365,7 @@ static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement, for (size_t y = 0; y < logImage->height; y++) { for (size_t x = 0; x < logImage->width * logImage->depth; x++) { row[x] = swap_ushort( - ((unsigned short)float_uint(data[y * logImage->width * logImage->depth + x], 4095)) << 4, + ((ushort)float_uint(data[y * logImage->width * logImage->depth + x], 4095)) << 4, logImage->isMSB); } @@ -385,9 +384,9 @@ static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement, static int logImageSetData16(LogImageFile *logImage, LogImageElement logElement, float *data) { size_t rowLength = getRowLength(logImage->width, logElement); - unsigned short *row; + ushort *row; - row = (unsigned short *)MEM_mallocN(rowLength, __func__); + row = (ushort *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); @@ -398,7 +397,7 @@ static int logImageSetData16(LogImageFile *logImage, LogImageElement logElement, for (size_t y = 0; y < logImage->height; y++) { for (size_t x = 0; x < logImage->width * logImage->depth; x++) { row[x] = swap_ushort( - (unsigned short)float_uint(data[y * logImage->width * logImage->depth + x], 65535), + (ushort)float_uint(data[y * logImage->width * logImage->depth + x], 65535), logImage->isMSB); } @@ -425,11 +424,11 @@ int logImageGetDataRGBA(LogImageFile *logImage, float *data, int dataIsLinearRGB float *elementData[8]; float *elementData_ptr[8]; float *mergedData; - unsigned int sampleIndex; + uint sampleIndex; LogImageElement mergedElement; /* Determine the depth of the picture and if there's a separate alpha element. - * If the element is supported, load it into an unsigned ints array. */ + * If the element is supported, load it into an `uint` array. */ memset(&elementData, 0, 8 * sizeof(float *)); hasAlpha = 0; @@ -695,7 +694,7 @@ static int logImageElementGetData(LogImageFile *logImage, LogImageElement logEle static int logImageElementGetData1(LogImageFile *logImage, LogImageElement logElement, float *data) { - unsigned int pixel; + uint pixel; /* seek at the right place */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { @@ -727,7 +726,7 @@ static int logImageElementGetData1(LogImageFile *logImage, LogImageElement logEl static int logImageElementGetData8(LogImageFile *logImage, LogImageElement logElement, float *data) { size_t rowLength = getRowLength(logImage->width, logElement); - unsigned char pixel; + uchar pixel; /* extract required pixels */ for (size_t y = 0; y < logImage->height; y++) { @@ -756,7 +755,7 @@ static int logImageElementGetData10(LogImageFile *logImage, LogImageElement logElement, float *data) { - unsigned int pixel; + uint pixel; /* seek to data */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { @@ -829,15 +828,14 @@ static int logImageElementGetData10Packed(LogImageFile *logImage, float *data) { size_t rowLength = getRowLength(logImage->width, logElement); - unsigned int pixel, oldPixel; + uint pixel, oldPixel; /* converting bytes to pixels */ for (size_t y = 0; y < logImage->height; y++) { /* seek to data */ if (logimage_fseek(logImage, y * rowLength + logElement.dataOffset, SEEK_SET) != 0) { if (verbose) { - printf("DPX/Cineon: Couldn't seek at %u\n", - (unsigned int)(y * rowLength + logElement.dataOffset)); + printf("DPX/Cineon: Couldn't seek at %u\n", (uint)(y * rowLength + logElement.dataOffset)); } return 1; } @@ -884,9 +882,9 @@ static int logImageElementGetData12(LogImageFile *logImage, LogImageElement logElement, float *data) { - unsigned int sampleIndex; - unsigned int numSamples = logImage->width * logImage->height * logElement.depth; - unsigned short pixel; + uint sampleIndex; + uint numSamples = logImage->width * logImage->height * logElement.depth; + ushort pixel; /* seek to data */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { @@ -923,15 +921,14 @@ static int logImageElementGetData12Packed(LogImageFile *logImage, float *data) { size_t rowLength = getRowLength(logImage->width, logElement); - unsigned int pixel, oldPixel; + uint pixel, oldPixel; /* converting bytes to pixels */ for (size_t y = 0; y < logImage->height; y++) { /* seek to data */ if (logimage_fseek(logImage, y * rowLength + logElement.dataOffset, SEEK_SET) != 0) { if (verbose) { - printf("DPX/Cineon: Couldn't seek at %u\n", - (unsigned int)(y * rowLength + logElement.dataOffset)); + printf("DPX/Cineon: Couldn't seek at %u\n", (uint)(y * rowLength + logElement.dataOffset)); } return 1; } @@ -978,9 +975,9 @@ static int logImageElementGetData16(LogImageFile *logImage, LogImageElement logElement, float *data) { - unsigned int numSamples = logImage->width * logImage->height * logElement.depth; - unsigned int sampleIndex; - unsigned short pixel; + uint numSamples = logImage->width * logImage->height * logElement.depth; + uint sampleIndex; + ushort pixel; /* seek to data */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { @@ -1076,8 +1073,8 @@ static float *getLinToLogLut(LogImageFile *logImage, LogImageElement logElement) { float *lut; float gain, negativeFilmGamma, offset, step; - unsigned int lutsize = (unsigned int)(logElement.maxValue + 1); - unsigned int i; + uint lutsize = (uint)(logElement.maxValue + 1); + uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLinToLogLut"); @@ -1104,8 +1101,8 @@ static float *getLogToLinLut(LogImageFile *logImage, LogImageElement logElement) float *lut; float breakPoint, gain, kneeGain, kneeOffset, negativeFilmGamma, offset, step, softClip; /* float filmGamma; unused */ - unsigned int lutsize = (unsigned int)(logElement.maxValue + 1); - unsigned int i; + uint lutsize = (uint)(logElement.maxValue + 1); + uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut"); @@ -1154,8 +1151,8 @@ static float *getLogToLinLut(LogImageFile *logImage, LogImageElement logElement) static float *getLinToSrgbLut(LogImageElement logElement) { float col, *lut; - unsigned int lutsize = (unsigned int)(logElement.maxValue + 1); - unsigned int i; + uint lutsize = (uint)(logElement.maxValue + 1); + uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut"); @@ -1175,8 +1172,8 @@ static float *getLinToSrgbLut(LogImageElement logElement) static float *getSrgbToLinLut(LogImageElement logElement) { float col, *lut; - unsigned int lutsize = (unsigned int)(logElement.maxValue + 1); - unsigned int i; + uint lutsize = (uint)(logElement.maxValue + 1); + uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut"); @@ -1199,7 +1196,7 @@ static int convertRGBA_RGB(float *src, LogImageElement logElement, int elementIsSource) { - unsigned int i; + uint i; float *src_ptr = src; float *dst_ptr = dst; @@ -1254,7 +1251,7 @@ static int convertRGB_RGBA(float *src, LogImageElement logElement, int elementIsSource) { - unsigned int i; + uint i; float *src_ptr = src; float *dst_ptr = dst; @@ -1309,7 +1306,7 @@ static int convertRGBA_RGBA(float *src, LogImageElement logElement, int elementIsSource) { - unsigned int i; + uint i; float *src_ptr = src; float *dst_ptr = dst; @@ -1354,7 +1351,7 @@ static int convertABGR_RGBA(float *src, LogImageElement logElement, int elementIsSource) { - unsigned int i; + uint i; float *src_ptr = src; float *dst_ptr = dst; @@ -1407,7 +1404,7 @@ static int convertCbYCr_RGBA(float *src, LogImageFile *logImage, LogImageElement logElement) { - unsigned int i; + uint i; float conversionMatrix[9], refLowData, y, cb, cr; float *src_ptr = src; float *dst_ptr = dst; @@ -1439,7 +1436,7 @@ static int convertCbYCrA_RGBA(float *src, LogImageFile *logImage, LogImageElement logElement) { - unsigned int i; + uint i; float conversionMatrix[9], refLowData, y, cb, cr, a; float *src_ptr = src; float *dst_ptr = dst; @@ -1472,7 +1469,7 @@ static int convertCbYCrY_RGBA(float *src, LogImageFile *logImage, LogImageElement logElement) { - unsigned int i; + uint i; float conversionMatrix[9], refLowData, y1, y2, cb, cr; float *src_ptr = src; float *dst_ptr = dst; @@ -1524,7 +1521,7 @@ static int convertCbYACrYA_RGBA(float *src, LogImageFile *logImage, LogImageElement logElement) { - unsigned int i; + uint i; float conversionMatrix[9], refLowData, y1, y2, cb, cr, a1, a2; float *src_ptr = src; float *dst_ptr = dst; @@ -1578,7 +1575,7 @@ static int convertLuminance_RGBA(float *src, LogImageFile *logImage, LogImageElement logElement) { - unsigned int i; + uint i; float conversionMatrix[9], value, refLowData; float *src_ptr = src; float *dst_ptr = dst; @@ -1604,7 +1601,7 @@ static int convertYA_RGBA(float *src, LogImageFile *logImage, LogImageElement logElement) { - unsigned int i; + uint i; float conversionMatrix[9], value, refLowData; float *src_ptr = src; float *dst_ptr = dst; @@ -1629,7 +1626,7 @@ static int convertLogElementToRGBA( float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int dstIsLinearRGB) { int rvalue; - unsigned int i; + uint i; float *src_ptr; float *dst_ptr; @@ -1698,7 +1695,7 @@ static int convertLogElementToRGBA( static int convertRGBAToLogElement( float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int srcIsLinearRGB) { - unsigned int i; + uint i; int rvalue; float *srgbSrc; float *srgbSrc_ptr; diff --git a/source/blender/imbuf/intern/cineon/logmemfile.c b/source/blender/imbuf/intern/cineon/logmemfile.c index f5bd87f96d1..6c24d67b33f 100644 --- a/source/blender/imbuf/intern/cineon/logmemfile.c +++ b/source/blender/imbuf/intern/cineon/logmemfile.c @@ -44,7 +44,7 @@ int logimage_fseek(LogImageFile *logFile, intptr_t offset, int origin) return 0; } -int logimage_fwrite(void *buffer, size_t size, unsigned int count, LogImageFile *logFile) +int logimage_fwrite(void *buffer, size_t size, uint count, LogImageFile *logFile) { if (logFile->file) { return fwrite(buffer, size, count, logFile->file); @@ -54,13 +54,13 @@ int logimage_fwrite(void *buffer, size_t size, unsigned int count, LogImageFile return count; } -int logimage_fread(void *buffer, size_t size, unsigned int count, LogImageFile *logFile) +int logimage_fread(void *buffer, size_t size, uint count, LogImageFile *logFile) { if (logFile->file) { return fread(buffer, size, count, logFile->file); } /* we're reading from memory */ - unsigned char *buf = (unsigned char *)buffer; + uchar *buf = (uchar *)buffer; uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer; size_t total_size = size * count; if (pos + total_size > logFile->memBufferSize) { @@ -77,38 +77,38 @@ int logimage_fread(void *buffer, size_t size, unsigned int count, LogImageFile * return count; } -int logimage_read_uchar(unsigned char *x, LogImageFile *logFile) +int logimage_read_uchar(uchar *x, LogImageFile *logFile) { uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer; - if (pos + sizeof(unsigned char) > logFile->memBufferSize) { + if (pos + sizeof(uchar) > logFile->memBufferSize) { return 1; } - *x = *(unsigned char *)logFile->memCursor; - logFile->memCursor += sizeof(unsigned char); + *x = *(uchar *)logFile->memCursor; + logFile->memCursor += sizeof(uchar); return 0; } -int logimage_read_ushort(unsigned short *x, LogImageFile *logFile) +int logimage_read_ushort(ushort *x, LogImageFile *logFile) { uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer; - if (pos + sizeof(unsigned short) > logFile->memBufferSize) { + if (pos + sizeof(ushort) > logFile->memBufferSize) { return 1; } - *x = *(unsigned short *)logFile->memCursor; - logFile->memCursor += sizeof(unsigned short); + *x = *(ushort *)logFile->memCursor; + logFile->memCursor += sizeof(ushort); return 0; } -int logimage_read_uint(unsigned int *x, LogImageFile *logFile) +int logimage_read_uint(uint *x, LogImageFile *logFile) { uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer; - if (pos + sizeof(unsigned int) > logFile->memBufferSize) { + if (pos + sizeof(uint) > logFile->memBufferSize) { return 1; } - *x = *(unsigned int *)logFile->memCursor; - logFile->memCursor += sizeof(unsigned int); + *x = *(uint *)logFile->memCursor; + logFile->memCursor += sizeof(uint); return 0; } diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index b62bdd5521d..ea5f4ec275d 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -235,11 +235,11 @@ static ColormanageCacheData *colormanage_cachedata_get(const ImBuf *ibuf) return ibuf->colormanage_cache->data; } -static unsigned int colormanage_hashhash(const void *key_v) +static uint colormanage_hashhash(const void *key_v) { const ColormanageCacheKey *key = key_v; - unsigned int rval = (key->display << 16) | (key->view % 0xffff); + uint rval = (key->display << 16) | (key->view % 0xffff); return rval; } @@ -336,11 +336,10 @@ static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, return cache_ibuf; } -static unsigned char *colormanage_cache_get( - ImBuf *ibuf, - const ColormanageCacheViewSettings *view_settings, - const ColormanageCacheDisplaySettings *display_settings, - void **cache_handle) +static uchar *colormanage_cache_get(ImBuf *ibuf, + const ColormanageCacheViewSettings *view_settings, + const ColormanageCacheDisplaySettings *display_settings, + void **cache_handle) { ColormanageCacheKey key; ImBuf *cache_ibuf; @@ -383,7 +382,7 @@ static unsigned char *colormanage_cache_get( return NULL; } - return (unsigned char *)cache_ibuf->rect; + return (uchar *)cache_ibuf->rect; } return NULL; @@ -392,7 +391,7 @@ static unsigned char *colormanage_cache_get( static void colormanage_cache_put(ImBuf *ibuf, const ColormanageCacheViewSettings *view_settings, const ColormanageCacheDisplaySettings *display_settings, - unsigned char *display_buffer, + uchar *display_buffer, void **cache_handle) { ColormanageCacheKey key; @@ -410,7 +409,7 @@ static void colormanage_cache_put(ImBuf *ibuf, /* buffer itself */ cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0); - cache_ibuf->rect = (unsigned int *)display_buffer; + cache_ibuf->rect = (uint *)display_buffer; cache_ibuf->mall |= IB_rect; cache_ibuf->flags |= IB_rect; @@ -1441,10 +1440,10 @@ typedef struct DisplayBufferThread { ColormanageProcessor *cm_processor; const float *buffer; - unsigned char *byte_buffer; + uchar *byte_buffer; float *display_buffer; - unsigned char *display_buffer_byte; + uchar *display_buffer_byte; int width; int start_line; @@ -1463,10 +1462,10 @@ typedef struct DisplayBufferInitData { ImBuf *ibuf; ColormanageProcessor *cm_processor; const float *buffer; - unsigned char *byte_buffer; + uchar *byte_buffer; float *display_buffer; - unsigned char *display_buffer_byte; + uchar *display_buffer_byte; int width; @@ -1539,13 +1538,13 @@ static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle, bool predivide = handle->predivide; if (!handle->buffer) { - unsigned char *byte_buffer = handle->byte_buffer; + uchar *byte_buffer = handle->byte_buffer; const char *from_colorspace = handle->byte_colorspace; const char *to_colorspace = global_role_scene_linear; float *fp; - unsigned char *cp; + uchar *cp; const size_t i_last = ((size_t)width) * height; size_t i; @@ -1608,7 +1607,7 @@ static void *do_display_buffer_apply_thread(void *handle_v) DisplayBufferThread *handle = (DisplayBufferThread *)handle_v; ColormanageProcessor *cm_processor = handle->cm_processor; float *display_buffer = handle->display_buffer; - unsigned char *display_buffer_byte = handle->display_buffer_byte; + uchar *display_buffer_byte = handle->display_buffer_byte; int channels = handle->channels; int width = handle->width; int height = handle->tot_line; @@ -1698,9 +1697,9 @@ static void *do_display_buffer_apply_thread(void *handle_v) static void display_buffer_apply_threaded(ImBuf *ibuf, const float *buffer, - unsigned char *byte_buffer, + uchar *byte_buffer, float *display_buffer, - unsigned char *display_buffer_byte, + uchar *display_buffer_byte, ColormanageProcessor *cm_processor) { DisplayBufferInitData init_data; @@ -1761,7 +1760,7 @@ static bool is_ibuf_rect_in_display_space(ImBuf *ibuf, static void colormanage_display_buffer_process_ex( ImBuf *ibuf, float *display_buffer, - unsigned char *display_buffer_byte, + uchar *display_buffer_byte, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { @@ -1783,7 +1782,7 @@ static void colormanage_display_buffer_process_ex( display_buffer_apply_threaded(ibuf, ibuf->rect_float, - (unsigned char *)ibuf->rect, + (uchar *)ibuf->rect, display_buffer, display_buffer_byte, cm_processor); @@ -1794,7 +1793,7 @@ static void colormanage_display_buffer_process_ex( } static void colormanage_display_buffer_process(ImBuf *ibuf, - unsigned char *display_buffer, + uchar *display_buffer, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { @@ -1810,7 +1809,7 @@ static void colormanage_display_buffer_process(ImBuf *ibuf, typedef struct ProcessorTransformThread { ColormanageProcessor *cm_processor; - unsigned char *byte_buffer; + uchar *byte_buffer; float *float_buffer; int width; int start_line; @@ -1822,7 +1821,7 @@ typedef struct ProcessorTransformThread { typedef struct ProcessorTransformInit { ColormanageProcessor *cm_processor; - unsigned char *byte_buffer; + uchar *byte_buffer; float *float_buffer; int width; int height; @@ -1871,7 +1870,7 @@ static void processor_transform_init_handle(void *handle_v, static void *do_processor_transform_thread(void *handle_v) { ProcessorTransformThread *handle = (ProcessorTransformThread *)handle_v; - unsigned char *byte_buffer = handle->byte_buffer; + uchar *byte_buffer = handle->byte_buffer; float *float_buffer = handle->float_buffer; const int channels = handle->channels; const int width = handle->width; @@ -1907,7 +1906,7 @@ static void *do_processor_transform_thread(void *handle_v) return NULL; } -static void processor_transform_apply_threaded(unsigned char *byte_buffer, +static void processor_transform_apply_threaded(uchar *byte_buffer, float *float_buffer, const int width, const int height, @@ -1942,7 +1941,7 @@ static void processor_transform_apply_threaded(unsigned char *byte_buffer, /* Convert the whole buffer from specified by name color space to another - * internal implementation. */ -static void colormanagement_transform_ex(unsigned char *byte_buffer, +static void colormanagement_transform_ex(uchar *byte_buffer, float *float_buffer, int width, int height, @@ -2008,7 +2007,7 @@ void IMB_colormanagement_transform_threaded(float *buffer, NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, true); } -void IMB_colormanagement_transform_byte(unsigned char *buffer, +void IMB_colormanagement_transform_byte(uchar *buffer, int width, int height, int channels, @@ -2018,7 +2017,7 @@ void IMB_colormanagement_transform_byte(unsigned char *buffer, colormanagement_transform_ex( buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, false); } -void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer, +void IMB_colormanagement_transform_byte_threaded(uchar *buffer, int width, int height, int channels, @@ -2030,7 +2029,7 @@ void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer, } void IMB_colormanagement_transform_from_byte(float *float_buffer, - unsigned char *byte_buffer, + uchar *byte_buffer, int width, int height, int channels, @@ -2050,7 +2049,7 @@ void IMB_colormanagement_transform_from_byte(float *float_buffer, float_buffer, width, height, channels, from_colorspace, to_colorspace, true); } void IMB_colormanagement_transform_from_byte_threaded(float *float_buffer, - unsigned char *byte_buffer, + uchar *byte_buffer, int width, int height, int channels, @@ -2205,7 +2204,7 @@ void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, } } -void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, +void IMB_colormanagement_imbuf_to_byte_texture(uchar *out_buffer, const int offset_x, const int offset_y, const int width, @@ -2220,14 +2219,14 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace) || IMB_colormanagement_space_is_data(ibuf->rect_colorspace)); - const unsigned char *in_buffer = (unsigned char *)ibuf->rect; + const uchar *in_buffer = (uchar *)ibuf->rect; const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied; for (int y = 0; y < height; y++) { const size_t in_offset = (offset_y + y) * ibuf->x + offset_x; const size_t out_offset = y * width; - const unsigned char *in = in_buffer + in_offset * 4; - unsigned char *out = out_buffer + out_offset * 4; + const uchar *in = in_buffer + in_offset * 4; + uchar *out = out_buffer + out_offset * 4; if (use_premultiply) { /* Premultiply only. */ @@ -2305,7 +2304,7 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, } else { /* Byte source buffer. */ - const unsigned char *in_buffer = (unsigned char *)ibuf->rect; + const uchar *in_buffer = (uchar *)ibuf->rect; const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied; /* TODO(brecht): make this multi-threaded, or at least process in batches. */ @@ -2317,7 +2316,7 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, for (int y = 0; y < height; y++) { const size_t in_offset = (offset_y + y) * ibuf->x + offset_x; const size_t out_offset = y * width; - const unsigned char *in = in_buffer + in_offset * 4; + const uchar *in = in_buffer + in_offset * 4; float *out = out_buffer + out_offset * 4; /* Convert to scene linear, to sRGB and premultiply. */ @@ -2458,7 +2457,7 @@ static void colormanagement_imbuf_make_display_space( } colormanage_display_buffer_process_ex( - ibuf, ibuf->rect_float, (unsigned char *)ibuf->rect, view_settings, display_settings); + ibuf, ibuf->rect_float, (uchar *)ibuf->rect, view_settings, display_settings); } void IMB_colormanagement_imbuf_make_display_space( @@ -2545,10 +2544,8 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, } if (colormanaged_ibuf->rect) { - IMB_alpha_under_color_byte((unsigned char *)colormanaged_ibuf->rect, - colormanaged_ibuf->x, - colormanaged_ibuf->y, - color); + IMB_alpha_under_color_byte( + (uchar *)colormanaged_ibuf->rect, colormanaged_ibuf->x, colormanaged_ibuf->y, color); } } @@ -2603,7 +2600,7 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, if (colormanaged_ibuf->rect) { /* Byte to byte. */ - IMB_colormanagement_transform_byte_threaded((unsigned char *)colormanaged_ibuf->rect, + IMB_colormanagement_transform_byte_threaded((uchar *)colormanaged_ibuf->rect, colormanaged_ibuf->x, colormanaged_ibuf->y, colormanaged_ibuf->channels, @@ -2650,12 +2647,12 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, /** \name Public Display Buffers Interfaces * \{ */ -unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, - void **cache_handle) +uchar *IMB_display_buffer_acquire(ImBuf *ibuf, + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + void **cache_handle) { - unsigned char *display_buffer; + uchar *display_buffer; size_t buffer_size; ColormanageCacheViewSettings cache_view_settings; ColormanageCacheDisplaySettings cache_display_settings; @@ -2683,7 +2680,7 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, */ if (ibuf->rect_float == NULL && ibuf->rect_colorspace && ibuf->channels == 4) { if (is_ibuf_rect_in_display_space(ibuf, applied_view_settings, display_settings)) { - return (unsigned char *)ibuf->rect; + return (uchar *)ibuf->rect; } } @@ -2694,7 +2691,7 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) { IMB_partial_display_buffer_update_threaded(ibuf, ibuf->rect_float, - (unsigned char *)ibuf->rect, + (uchar *)ibuf->rect, ibuf->x, 0, 0, @@ -2713,14 +2710,14 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, /* ensure color management bit fields exists */ if (!ibuf->display_buffer_flags) { - ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, + ibuf->display_buffer_flags = MEM_callocN(sizeof(uint) * global_tot_display, "imbuf display_buffer_flags"); } else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) { /* all display buffers were marked as invalid from other areas, * now propagate this flag to internal color management routines */ - memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int)); + memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(uint)); ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID; } @@ -2747,7 +2744,7 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, return display_buffer; } -unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle) +uchar *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle) { ColorManagedViewSettings *view_settings; ColorManagedDisplaySettings *display_settings; @@ -2757,7 +2754,7 @@ unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, vo return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle); } -void IMB_display_buffer_transform_apply(unsigned char *display_buffer, +void IMB_display_buffer_transform_apply(uchar *display_buffer, float *linear_buffer, int width, int height, @@ -3396,9 +3393,9 @@ void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *tot */ static void partial_buffer_update_rect(ImBuf *ibuf, - unsigned char *display_buffer, + uchar *display_buffer, const float *linear_buffer, - const unsigned char *byte_buffer, + const uchar *byte_buffer, int display_stride, int linear_stride, int linear_offset_x, @@ -3547,9 +3544,9 @@ static void partial_buffer_update_rect(ImBuf *ibuf, typedef struct PartialThreadData { ImBuf *ibuf; - unsigned char *display_buffer; + uchar *display_buffer; const float *linear_buffer; - const unsigned char *byte_buffer; + const uchar *byte_buffer; int display_stride; int linear_stride; int linear_offset_x, linear_offset_y; @@ -3580,7 +3577,7 @@ static void partial_buffer_update_rect_thread_do(void *data_v, int scanline) static void imb_partial_display_buffer_update_ex( ImBuf *ibuf, const float *linear_buffer, - const unsigned char *byte_buffer, + const uchar *byte_buffer, int stride, int offset_x, int offset_y, @@ -3595,7 +3592,7 @@ static void imb_partial_display_buffer_update_ex( ColormanageCacheViewSettings cache_view_settings; ColormanageCacheDisplaySettings cache_display_settings; void *cache_handle = NULL; - unsigned char *display_buffer = NULL; + uchar *display_buffer = NULL; int buffer_width = ibuf->x; if (ibuf->display_buffer_flags) { @@ -3621,7 +3618,7 @@ static void imb_partial_display_buffer_update_ex( buffer_width = ibuf->x; /* Mark all other buffers as invalid. */ - memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int)); + memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(uint)); ibuf->display_buffer_flags[display_index] |= view_flag; BLI_thread_unlock(LOCK_COLORMANAGE); @@ -3689,7 +3686,7 @@ static void imb_partial_display_buffer_update_ex( void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, - const unsigned char *byte_buffer, + const uchar *byte_buffer, int stride, int offset_x, int offset_y, @@ -3718,7 +3715,7 @@ void IMB_partial_display_buffer_update(ImBuf *ibuf, void IMB_partial_display_buffer_update_threaded( struct ImBuf *ibuf, const float *linear_buffer, - const unsigned char *byte_buffer, + const uchar *byte_buffer, int stride, int offset_x, int offset_y, @@ -3925,7 +3922,7 @@ void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, } void IMB_colormanagement_processor_apply_byte( - ColormanageProcessor *cm_processor, unsigned char *buffer, int width, int height, int channels) + ColormanageProcessor *cm_processor, uchar *buffer, int width, int height, int channels) { /* TODO(sergey): Would be nice to support arbitrary channels configurations, * but for now it's not so important. diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c index 3c6c0f5fd0a..df513a7330c 100644 --- a/source/blender/imbuf/intern/colormanagement_inline.c +++ b/source/blender/imbuf/intern/colormanagement_inline.c @@ -21,7 +21,7 @@ float IMB_colormanagement_get_luminance(const float rgb[3]) return dot_v3v3(imbuf_luma_coefficients, rgb); } -unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3]) +uchar IMB_colormanagement_get_luminance_byte(const uchar rgb[3]) { float rgbf[3]; float val; diff --git a/source/blender/imbuf/intern/dds/BlockDXT.cpp b/source/blender/imbuf/intern/dds/BlockDXT.cpp index 4048a78e5cf..2d198135a66 100644 --- a/source/blender/imbuf/intern/dds/BlockDXT.cpp +++ b/source/blender/imbuf/intern/dds/BlockDXT.cpp @@ -34,6 +34,8 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ +#include <BLI_sys_types.h> /* For `uint`. */ + #include <BlockDXT.h> #include <ColorBlock.h> #include <Common.h> @@ -576,7 +578,7 @@ void mem_read(Stream &mem, BlockDXT1 &block) void mem_read(Stream &mem, AlphaBlockDXT3 &block) { - for (unsigned short &alpha : block.row) { + for (ushort &alpha : block.row) { mem_read(mem, alpha); } } diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp index ce5dd4927be..4e5dc9ce560 100644 --- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp +++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp @@ -867,7 +867,7 @@ uint DDSHeader::d3d9Format() const return findD3D9Format(pf.bitcount, pf.rmask, pf.gmask, pf.bmask, pf.amask); } -DirectDrawSurface::DirectDrawSurface(unsigned char *mem, uint size) : stream(mem, size), header() +DirectDrawSurface::DirectDrawSurface(uchar *mem, uint size) : stream(mem, size), header() { mem_read(stream, header); @@ -1112,7 +1112,7 @@ void *DirectDrawSurface::readData(uint &rsize) uint size = stream.size - header_size; rsize = size; - unsigned char *data = (unsigned char *)malloc(sizeof(*data) * size); + uchar *data = (uchar *)malloc(sizeof(*data) * size); stream.seek(header_size); mem_read(stream, data, size); @@ -1158,7 +1158,7 @@ void DirectDrawSurface::readLinearImage(Image *img) for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { uint c = 0; - mem_read(stream, (unsigned char *)(&c), byteCount); + mem_read(stream, (uchar *)(&c), byteCount); Color32 pixel(0, 0, 0, 0xFF); pixel.r = PixelFormat::convert((c & header.pf.rmask) >> rshift, rsize, 8); diff --git a/source/blender/imbuf/intern/dds/FlipDXT.cpp b/source/blender/imbuf/intern/dds/FlipDXT.cpp index fc978bff788..3d2b7e51a46 100644 --- a/source/blender/imbuf/intern/dds/FlipDXT.cpp +++ b/source/blender/imbuf/intern/dds/FlipDXT.cpp @@ -104,19 +104,19 @@ static void FlipDXT5BlockFull(uint8_t *block) * bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 + * 256 * (bits_4 + 256 * bits_5)))) * - * bits is a 48-bit unsigned integer, from which a three-bit control code + * bits is a 48-bit unsigned-integer, from which a three-bit control code * is extracted for a texel at location (x,y) in the block using: * * code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0] * * where bit 47 is the most significant and bit 0 is the least * significant bit. */ - unsigned int line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]); - unsigned int line_2_3 = block[5] + 256 * (block[6] + 256 * block[7]); + uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]); + uint line_2_3 = block[5] + 256 * (block[6] + 256 * block[7]); /* swap lines 0 and 1 in line_0_1. */ - unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12); + uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12); /* swap lines 2 and 3 in line_2_3. */ - unsigned int line_3_2 = ((line_2_3 & 0x000fff) << 12) | ((line_2_3 & 0xfff000) >> 12); + uint line_3_2 = ((line_2_3 & 0x000fff) << 12) | ((line_2_3 & 0xfff000) >> 12); block[2] = line_3_2 & 0xff; block[3] = (line_3_2 & 0xff00) >> 8; @@ -133,21 +133,21 @@ static void FlipDXT5BlockFull(uint8_t *block) static void FlipDXT5BlockHalf(uint8_t *block) { /* See layout above. */ - unsigned int line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]); - unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12); + uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]); + uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12); block[2] = line_1_0 & 0xff; block[3] = (line_1_0 & 0xff00) >> 8; block[4] = (line_1_0 & 0xff0000) >> 16; FlipDXT1BlockHalf(block + 8); } -int FlipDXTCImage(unsigned int width, - unsigned int height, - unsigned int levels, +int FlipDXTCImage(uint width, + uint height, + uint levels, int fourcc, uint8_t *data, int data_size, - unsigned int *r_num_valid_levels) + uint *r_num_valid_levels) { *r_num_valid_levels = 0; @@ -162,7 +162,7 @@ int FlipDXTCImage(unsigned int width, FlipBlockFunction full_block_function; FlipBlockFunction half_block_function; - unsigned int block_bytes = 0; + uint block_bytes = 0; switch (fourcc) { case FOURCC_DXT1: @@ -186,15 +186,15 @@ int FlipDXTCImage(unsigned int width, *r_num_valid_levels = levels; - unsigned int mip_width = width; - unsigned int mip_height = height; + uint mip_width = width; + uint mip_height = height; const uint8_t *data_end = data + data_size; - for (unsigned int i = 0; i < levels; i++) { - unsigned int blocks_per_row = (mip_width + 3) / 4; - unsigned int blocks_per_col = (mip_height + 3) / 4; - unsigned int blocks = blocks_per_row * blocks_per_col; + for (uint i = 0; i < levels; i++) { + uint blocks_per_row = (mip_width + 3) / 4; + uint blocks_per_col = (mip_height + 3) / 4; + uint blocks = blocks_per_row * blocks_per_col; if (data + block_bytes * blocks > data_end) { /* Stop flipping when running out of data to be modified, avoiding possible buffer overrun @@ -209,23 +209,23 @@ int FlipDXTCImage(unsigned int width, } if (mip_height == 2) { /* flip the first 2 lines in each block. */ - for (unsigned int i = 0; i < blocks_per_row; i++) { + for (uint i = 0; i < blocks_per_row; i++) { half_block_function(data + i * block_bytes); } } else { /* flip each block. */ - for (unsigned int i = 0; i < blocks; i++) { + for (uint i = 0; i < blocks; i++) { full_block_function(data + i * block_bytes); } /* Swap each block line in the first half of the image with the * corresponding one in the second half. * note that this is a no-op if mip_height is 4. */ - unsigned int row_bytes = block_bytes * blocks_per_row; + uint row_bytes = block_bytes * blocks_per_row; uint8_t *temp_line = new uint8_t[row_bytes]; - for (unsigned int y = 0; y < blocks_per_col / 2; y++) { + for (uint y = 0; y < blocks_per_col / 2; y++) { uint8_t *line1 = data + y * row_bytes; uint8_t *line2 = data + (blocks_per_col - y - 1) * row_bytes; diff --git a/source/blender/imbuf/intern/dds/Stream.cpp b/source/blender/imbuf/intern/dds/Stream.cpp index 566891dac8b..44b7e6d8f42 100644 --- a/source/blender/imbuf/intern/dds/Stream.cpp +++ b/source/blender/imbuf/intern/dds/Stream.cpp @@ -4,6 +4,8 @@ * \ingroup imbdds */ +#include "BLI_sys_types.h" /* For `uint`. */ + #include <Stream.h> #include <cstdio> /* printf */ @@ -12,7 +14,7 @@ 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 count) +inline bool is_read_within_bounds(const Stream &mem, uint count) { if (mem.pos >= mem.size) { /* No more data remained in the memory buffer. */ @@ -27,7 +29,7 @@ inline bool is_read_within_bounds(const Stream &mem, unsigned int count) return true; } -unsigned int Stream::seek(unsigned int p) +uint Stream::seek(uint p) { if (p > size) { set_failed(msg_error_seek); @@ -39,7 +41,7 @@ unsigned int Stream::seek(unsigned int p) return pos; } -unsigned int mem_read(Stream &mem, unsigned long long &i) +uint mem_read(Stream &mem, unsigned long long &i) { if (!is_read_within_bounds(mem, 8)) { mem.set_failed(msg_error_seek); @@ -50,7 +52,7 @@ unsigned int mem_read(Stream &mem, unsigned long long &i) return 8; } -unsigned int mem_read(Stream &mem, unsigned int &i) +uint mem_read(Stream &mem, uint &i) { if (!is_read_within_bounds(mem, 4)) { mem.set_failed(msg_error_read); @@ -61,7 +63,7 @@ unsigned int mem_read(Stream &mem, unsigned int &i) return 4; } -unsigned int mem_read(Stream &mem, unsigned short &i) +uint mem_read(Stream &mem, ushort &i) { if (!is_read_within_bounds(mem, 2)) { mem.set_failed(msg_error_read); @@ -72,7 +74,7 @@ unsigned int mem_read(Stream &mem, unsigned short &i) return 2; } -unsigned int mem_read(Stream &mem, unsigned char &i) +uint mem_read(Stream &mem, uchar &i) { if (!is_read_within_bounds(mem, 1)) { mem.set_failed(msg_error_read); @@ -83,7 +85,7 @@ unsigned int mem_read(Stream &mem, unsigned char &i) return 1; } -unsigned int mem_read(Stream &mem, unsigned char *i, unsigned int count) +uint mem_read(Stream &mem, uchar *i, uint count) { if (!is_read_within_bounds(mem, count)) { mem.set_failed(msg_error_read); diff --git a/source/blender/imbuf/intern/dds/dds_api.cpp b/source/blender/imbuf/intern/dds/dds_api.cpp index e9a13573116..213e10cf744 100644 --- a/source/blender/imbuf/intern/dds/dds_api.cpp +++ b/source/blender/imbuf/intern/dds/dds_api.cpp @@ -58,7 +58,7 @@ bool imb_save_dds(struct ImBuf *ibuf, const char *name, int /*flags*/) return true; } -bool imb_is_a_dds(const unsigned char *mem, const size_t size) +bool imb_is_a_dds(const uchar *mem, const size_t size) { if (size < 8) { return false; @@ -75,19 +75,16 @@ bool imb_is_a_dds(const unsigned char *mem, const size_t size) return true; } -struct ImBuf *imb_load_dds(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +struct ImBuf *imb_load_dds(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = nullptr; - DirectDrawSurface dds((unsigned char *)mem, size); /* reads header */ - unsigned char bits_per_pixel; - unsigned int *rect; + DirectDrawSurface dds((uchar *)mem, size); /* reads header */ + uchar bits_per_pixel; + uint *rect; Image img; - unsigned int numpixels = 0; + uint numpixels = 0; int col; - unsigned char *cp = (unsigned char *)&col; + uchar *cp = (uchar *)&col; Color32 pixel; Color32 *pixels = nullptr; @@ -128,7 +125,7 @@ struct ImBuf *imb_load_dds(const unsigned char *mem, bits_per_pixel = 24; if (img.format() == Image::Format_ARGB) { /* check that there is effectively an alpha channel */ - for (unsigned int i = 0; i < numpixels; i++) { + for (uint i = 0; i < numpixels; i++) { pixel = pixels[i]; if (pixel.a != 255) { bits_per_pixel = 32; @@ -156,7 +153,7 @@ struct ImBuf *imb_load_dds(const unsigned char *mem, rect = ibuf->rect; cp[3] = 0xff; /* default alpha if alpha channel is not present */ - for (unsigned int i = 0; i < numpixels; i++) { + for (uint i = 0; i < numpixels; i++) { pixel = pixels[i]; cp[0] = pixel.r; /* set R component of col */ cp[1] = pixel.g; /* set G component of col */ @@ -168,7 +165,7 @@ struct ImBuf *imb_load_dds(const unsigned char *mem, } if (ibuf->dds_data.fourcc != FOURCC_DDS) { - ibuf->dds_data.data = (unsigned char *)dds.readData(ibuf->dds_data.size); + ibuf->dds_data.data = (uchar *)dds.readData(ibuf->dds_data.size); /* flip compressed texture */ if (ibuf->dds_data.data) { diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 13c8f0887b3..61ef9c111d7 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -48,7 +48,7 @@ static void clear_dither_context(DitherContext *di) /** \name Generic Buffer Conversion * \{ */ -MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4]) +MINLINE void ushort_to_byte_v4(uchar b[4], const ushort us[4]) { b[0] = unit_ushort_to_uchar(us[0]); b[1] = unit_ushort_to_uchar(us[1]); @@ -56,13 +56,13 @@ MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4]) b[3] = unit_ushort_to_uchar(us[3]); } -MINLINE unsigned char ftochar(float value) +MINLINE uchar ftochar(float value) { return unit_float_to_uchar_clamp(value); } MINLINE void ushort_to_byte_dither_v4( - uchar b[4], const unsigned short us[4], DitherContext *di, float s, float t) + uchar b[4], const ushort us[4], DitherContext *di, float s, float t) { #define USHORTTOFLOAT(val) ((float)val / 65535.0f) float dither_value = dither_random_value(s, t) * 0.0033f * di->dither; @@ -192,7 +192,7 @@ void IMB_buffer_byte_from_float(uchar *rect_to, } else if (profile_to == IB_PROFILE_SRGB) { /* convert from linear to sRGB */ - unsigned short us[4]; + ushort us[4]; float straight[4]; if (dither && predivide) { @@ -729,7 +729,7 @@ void IMB_rect_from_float(ImBuf *ibuf) } /* convert float to byte */ - IMB_buffer_byte_from_float((unsigned char *)ibuf->rect, + IMB_buffer_byte_from_float((uchar *)ibuf->rect, buffer, ibuf->channels, ibuf->dither, @@ -768,7 +768,7 @@ void IMB_float_from_rect_ex(struct ImBuf *dst, float *rect_float = dst->rect_float; rect_float += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4; - unsigned char *rect = (unsigned char *)src->rect; + uchar *rect = (uchar *)src->rect; rect += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4; const int region_width = BLI_rcti_size_x(region_to_update); const int region_height = BLI_rcti_size_y(region_to_update); @@ -889,7 +889,7 @@ void IMB_buffer_float_premultiply(float *buf, int width, int height) void IMB_saturation(ImBuf *ibuf, float sat) { size_t i; - unsigned char *rct = (unsigned char *)ibuf->rect; + uchar *rct = (uchar *)ibuf->rect; float *rct_fl = ibuf->rect_float; float hsv[3]; diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 92fa980cd7f..e1d2bea4ae9 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -217,7 +217,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_webp, .load = imb_loadwebp, .load_filepath = NULL, - .load_filepath_thumbnail = NULL, + .load_filepath_thumbnail = imb_load_filepath_thumbnail_webp, .save = imb_savewebp, .load_tile = NULL, .flag = 0, diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c index 91c69d3abc8..67de467bd93 100644 --- a/source/blender/imbuf/intern/filter.c +++ b/source/blender/imbuf/intern/filter.c @@ -18,9 +18,9 @@ #include "imbuf.h" -static void filtrow(unsigned char *point, int x) +static void filtrow(uchar *point, int x) { - unsigned int c1, c2, c3, error; + uint c1, c2, c3, error; if (x > 1) { c1 = c2 = *point; @@ -56,10 +56,10 @@ static void filtrowf(float *point, int x) } } -static void filtcolum(unsigned char *point, int y, int skip) +static void filtcolum(uchar *point, int y, int skip) { - unsigned int c1, c2, c3, error; - unsigned char *point2; + uint c1, c2, c3, error; + uchar *point2; if (y > 1) { c1 = c2 = *point; @@ -101,11 +101,11 @@ static void filtcolumf(float *point, int y, int skip) void IMB_filtery(struct ImBuf *ibuf) { - unsigned char *point; + uchar *point; float *pointf; int x, y, skip; - point = (unsigned char *)ibuf->rect; + point = (uchar *)ibuf->rect; pointf = ibuf->rect_float; x = ibuf->x; @@ -142,11 +142,11 @@ void IMB_filtery(struct ImBuf *ibuf) void imb_filterx(struct ImBuf *ibuf) { - unsigned char *point; + uchar *point; float *pointf; int x, y, skip; - point = (unsigned char *)ibuf->rect; + point = (uchar *)ibuf->rect; pointf = ibuf->rect_float; x = ibuf->x; @@ -395,7 +395,7 @@ static int check_pixel_assigned( res = mask[index] != 0 ? 1 : 0; } else if ((is_float && ((const float *)buffer)[alpha_index] != 0.0f) || - (!is_float && ((const unsigned char *)buffer)[alpha_index] != 0)) { + (!is_float && ((const uchar *)buffer)[alpha_index] != 0)) { res = 1; } } @@ -408,7 +408,7 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter) const int width = ibuf->x; const int height = ibuf->y; const int depth = 4; /* always 4 channels */ - const int chsize = ibuf->rect_float ? sizeof(float) : sizeof(unsigned char); + const int chsize = ibuf->rect_float ? sizeof(float) : sizeof(uchar); const size_t bsize = ((size_t)width) * height * depth * chsize; const bool is_float = (ibuf->rect_float != NULL); void *dstbuf = (void *)MEM_dupallocN(ibuf->rect_float ? (void *)ibuf->rect_float : @@ -478,7 +478,7 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter) } else { for (c = 0; c < depth; c++) { - tmp[c] = (float)((const unsigned char *)srcbuf)[depth * tmpindex + c]; + tmp[c] = (float)((const uchar *)srcbuf)[depth * tmpindex + c]; } } @@ -505,8 +505,10 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter) } else { for (c = 0; c < depth; c++) { - ((unsigned char *)dstbuf)[depth * index + c] = - acc[c] > 255 ? 255 : (acc[c] < 0 ? 0 : (unsigned char)roundf(acc[c])); + ((uchar *)dstbuf)[depth * index + c] = acc[c] > 255 ? + 255 : + (acc[c] < 0 ? 0 : + (uchar)roundf(acc[c])); } } @@ -613,7 +615,7 @@ ImBuf *IMB_getmipmap(ImBuf *ibuf, int level) return (level == 0) ? ibuf : ibuf->mipmap[level - 1]; } -void IMB_premultiply_rect(unsigned int *rect, char planes, int w, int h) +void IMB_premultiply_rect(uint *rect, char planes, int w, int h) { char *cp; int x, y, val; @@ -674,7 +676,7 @@ void IMB_premultiply_alpha(ImBuf *ibuf) } } -void IMB_unpremultiply_rect(unsigned int *rect, char planes, int w, int h) +void IMB_unpremultiply_rect(uint *rect, char planes, int w, int h) { char *cp; int x, y; diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 13bf3697946..4530959e5ac 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -26,7 +26,7 @@ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf) { size_t size; - unsigned char rt, *cp = (unsigned char *)ibuf->rect; + uchar rt, *cp = (uchar *)ibuf->rect; float rtf, *cpf = ibuf->rect_float; if (ibuf->rect) { @@ -58,14 +58,13 @@ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf) } } -static void pixel_from_buffer( - const struct ImBuf *ibuf, unsigned char **outI, float **outF, int x, int y) +static void pixel_from_buffer(const struct ImBuf *ibuf, uchar **outI, float **outF, int x, int y) { size_t offset = ((size_t)ibuf->x) * y * 4 + 4 * x; if (ibuf->rect) { - *outI = (unsigned char *)ibuf->rect + offset; + *outI = (uchar *)ibuf->rect + offset; } if (ibuf->rect_float) { @@ -78,19 +77,19 @@ static void pixel_from_buffer( * \{ */ void bicubic_interpolation_color( - const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v) { if (outF) { BLI_bicubic_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v); } else { - BLI_bicubic_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v); + BLI_bicubic_interpolation_char((uchar *)in->rect, outI, in->x, in->y, 4, u, v); } } void bicubic_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) { - unsigned char *outI = NULL; + uchar *outI = NULL; float *outF = NULL; if (in == NULL || (in->rect == NULL && in->rect_float == NULL)) { @@ -110,7 +109,7 @@ void bicubic_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xo * \{ */ void bilinear_interpolation_color_fl( - const struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) + const struct ImBuf *in, uchar UNUSED(outI[4]), float outF[4], float u, float v) { BLI_assert(outF); BLI_assert(in->rect_float); @@ -118,21 +117,21 @@ void bilinear_interpolation_color_fl( } void bilinear_interpolation_color_char( - const struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) + const struct ImBuf *in, uchar outI[4], float UNUSED(outF[4]), float u, float v) { BLI_assert(outI); BLI_assert(in->rect); - BLI_bilinear_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v); + BLI_bilinear_interpolation_char((uchar *)in->rect, outI, in->x, in->y, 4, u, v); } void bilinear_interpolation_color( - const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v) { if (outF) { BLI_bilinear_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v); } else { - BLI_bilinear_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v); + BLI_bilinear_interpolation_char((uchar *)in->rect, outI, in->x, in->y, 4, u, v); } } @@ -140,10 +139,10 @@ void bilinear_interpolation_color( /* BILINEAR INTERPOLATION */ void bilinear_interpolation_color_wrap( - const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v) { float *row1, *row2, *row3, *row4, a, b; - unsigned char *row1I, *row2I, *row3I, *row4I; + uchar *row1I, *row2I, *row3I, *row4I; float a_b, ma_b, a_mb, ma_mb; int y1, y2, x1, x2; @@ -198,10 +197,10 @@ void bilinear_interpolation_color_wrap( } if (outI) { /* sample including outside of edges of image */ - row1I = (unsigned char *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x1; - row2I = (unsigned char *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x1; - row3I = (unsigned char *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x2; - row4I = (unsigned char *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x2; + row1I = (uchar *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x1; + row2I = (uchar *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x1; + row3I = (uchar *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x2; + row4I = (uchar *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x2; /* Tested with white images and this should not wrap back to zero. */ outI[0] = roundf(ma_mb * row1I[0] + a_mb * row3I[0] + ma_b * row2I[0] + a_b * row4I[0]); @@ -213,7 +212,7 @@ void bilinear_interpolation_color_wrap( void bilinear_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) { - unsigned char *outI = NULL; + uchar *outI = NULL; float *outF = NULL; if (in == NULL || (in->rect == NULL && in->rect_float == NULL)) { @@ -233,7 +232,7 @@ void bilinear_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int x * \{ */ void nearest_interpolation_color_char( - const struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) + const struct ImBuf *in, uchar outI[4], float UNUSED(outF[4]), float u, float v) { BLI_assert(outI); BLI_assert(in->rect); @@ -248,7 +247,7 @@ void nearest_interpolation_color_char( } const size_t offset = ((size_t)in->x * y1 + x1) * 4; - const unsigned char *dataI = (unsigned char *)in->rect + offset; + const uchar *dataI = (uchar *)in->rect + offset; outI[0] = dataI[0]; outI[1] = dataI[1]; outI[2] = dataI[2]; @@ -256,7 +255,7 @@ void nearest_interpolation_color_char( } void nearest_interpolation_color_fl( - const struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) + const struct ImBuf *in, uchar UNUSED(outI[4]), float outF[4], float u, float v) { BLI_assert(outF); BLI_assert(in->rect_float); @@ -276,7 +275,7 @@ void nearest_interpolation_color_fl( } void nearest_interpolation_color( - const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v) { if (outF) { nearest_interpolation_color_fl(in, outI, outF, u, v); @@ -287,10 +286,10 @@ void nearest_interpolation_color( } void nearest_interpolation_color_wrap( - const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v) { const float *dataF; - unsigned char *dataI; + uchar *dataI; int y, x; /* ImBuf in must have a valid rect or rect_float, assume this is already checked */ @@ -309,7 +308,7 @@ void nearest_interpolation_color_wrap( y += in->y; } - dataI = (unsigned char *)in->rect + ((size_t)in->x) * y * 4 + 4 * x; + dataI = (uchar *)in->rect + ((size_t)in->x) * y * 4 + 4 * x; if (outI) { outI[0] = dataI[0]; outI[1] = dataI[1]; @@ -327,7 +326,7 @@ void nearest_interpolation_color_wrap( void nearest_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) { - unsigned char *outI = NULL; + uchar *outI = NULL; float *outF = NULL; if (in == NULL || (in->rect == NULL && in->rect_float == NULL)) { @@ -446,10 +445,10 @@ void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[ } } -void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3]) +void IMB_alpha_under_color_byte(uchar *rect, int x, int y, const float backcol[3]) { size_t a = ((size_t)x) * y; - unsigned char *cp = rect; + uchar *cp = rect; while (a--) { if (cp[3] == 255) { @@ -487,7 +486,7 @@ void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_r nearest_interpolation_color(ibuf, NULL, color, x, y); } else { - unsigned char byte_color[4]; + uchar byte_color[4]; nearest_interpolation_color(ibuf, byte_color, NULL, x, y); rgba_uchar_to_float(color, byte_color); if (make_linear_rgb) { diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index a14c94d5d62..f57d4382672 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -39,7 +39,7 @@ typedef struct img_folder { float *rates; } img_fol_t; -static bool check_jp2(const unsigned char *mem, const size_t size) /* J2K_CFMT */ +static bool check_jp2(const uchar *mem, const size_t size) /* J2K_CFMT */ { if (size < sizeof(JP2_HEAD)) { return false; @@ -47,7 +47,7 @@ static bool check_jp2(const unsigned char *mem, const size_t size) /* J2K_CFMT * return memcmp(JP2_HEAD, mem, sizeof(JP2_HEAD)) ? 0 : 1; } -static bool check_j2k(const unsigned char *mem, const size_t size) /* J2K_CFMT */ +static bool check_j2k(const uchar *mem, const size_t size) /* J2K_CFMT */ { if (size < sizeof(J2K_HEAD)) { return false; @@ -55,8 +55,7 @@ static bool check_j2k(const unsigned char *mem, const size_t size) /* J2K_CFMT * return memcmp(J2K_HEAD, mem, sizeof(J2K_HEAD)) ? 0 : 1; } -static OPJ_CODEC_FORMAT format_from_header(const unsigned char mem[JP2_FILEHEADER_SIZE], - const size_t size) +static OPJ_CODEC_FORMAT format_from_header(const uchar mem[JP2_FILEHEADER_SIZE], const size_t size) { if (check_jp2(mem, size)) { return OPJ_CODEC_JP2; @@ -68,7 +67,7 @@ static OPJ_CODEC_FORMAT format_from_header(const unsigned char mem[JP2_FILEHEADE return OPJ_CODEC_UNKNOWN; } -bool imb_is_a_jp2(const unsigned char *buf, size_t size) +bool imb_is_a_jp2(const uchar *buf, size_t size) { return (check_jp2(buf, size) || check_j2k(buf, size)); } @@ -102,11 +101,11 @@ static void info_callback(const char *msg, void *client_data) #endif #define PIXEL_LOOPER_BEGIN(_rect) \ - for (y = h - 1; y != (unsigned int)(-1); y--) { \ + for (y = h - 1; y != (uint)(-1); y--) { \ for (i = y * w, i_next = (y + 1) * w; i < i_next; i++, _rect += 4) { #define PIXEL_LOOPER_BEGIN_CHANNELS(_rect, _channels) \ - for (y = h - 1; y != (unsigned int)(-1); y--) { \ + for (y = h - 1; y != (uint)(-1); y--) { \ for (i = y * w, i_next = (y + 1) * w; i < i_next; i++, _rect += _channels) { #define PIXEL_LOOPER_END \ @@ -119,8 +118,8 @@ static void info_callback(const char *msg, void *client_data) * \{ */ struct BufInfo { - const unsigned char *buf; - const unsigned char *cur; + const uchar *buf; + const uchar *cur; OPJ_OFF_T len; }; @@ -300,10 +299,7 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, int flags, char colorspace[IM_MAX_SPACE]); -ImBuf *imb_load_jp2(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_jp2(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { const OPJ_CODEC_FORMAT format = (size > JP2_FILEHEADER_SIZE) ? format_from_header(mem, size) : OPJ_CODEC_UNKNOWN; @@ -322,7 +318,7 @@ ImBuf *imb_load_jp2(const unsigned char *mem, ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]) { FILE *p_file = NULL; - unsigned char mem[JP2_FILEHEADER_SIZE]; + uchar mem[JP2_FILEHEADER_SIZE]; opj_stream_t *stream = opj_stream_create_from_file( filepath, OPJ_J2K_STREAM_CHUNK_SIZE, true, &p_file); if (stream) { @@ -358,8 +354,8 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, long signed_offsets[4] = {0, 0, 0, 0}; int float_divs[4] = {1, 1, 1, 1}; - unsigned int i, i_next, w, h, planes; - unsigned int y; + uint i, i_next, w, h, planes; + uint y; int *r, *g, *b, *a; /* matching 'opj_image_comp.data' type */ opj_dparameters_t parameters; /* decompression parameters */ @@ -509,7 +505,7 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, } } else { - unsigned char *rect_uchar = (unsigned char *)ibuf->rect; + uchar *rect_uchar = (uchar *)ibuf->rect; if (image->numcomps < 3) { r = image->comps[0].data; @@ -599,11 +595,11 @@ static opj_image_t *rawtoimage(const char *filename, (_val) <= 0.0f ? 0 : ((_val) >= 1.0f ? 65535 : (int)(65535.0f * (_val))) #else -BLI_INLINE int UPSAMPLE_8_TO_12(const unsigned char _val) +BLI_INLINE int UPSAMPLE_8_TO_12(const uchar _val) { return (_val << 4) | (_val & ((1 << 4) - 1)); } -BLI_INLINE int UPSAMPLE_8_TO_16(const unsigned char _val) +BLI_INLINE int UPSAMPLE_8_TO_16(const uchar _val) { return (_val << 8) + _val; } @@ -811,14 +807,14 @@ static float channel_colormanage_noop(float value) static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) { - unsigned char *rect_uchar; + uchar *rect_uchar; float *rect_float, from_straight[4]; - unsigned int subsampling_dx = parameters->subsampling_dx; - unsigned int subsampling_dy = parameters->subsampling_dy; + uint subsampling_dx = parameters->subsampling_dx; + uint subsampling_dy = parameters->subsampling_dy; - unsigned int i, i_next, numcomps, w, h, prec; - unsigned int y; + uint i, i_next, numcomps, w, h, prec; + uint y; int *r, *g, *b, *a; /* matching 'opj_image_comp.data' type */ OPJ_COLOR_SPACE color_space; opj_image_cmptparm_t cmptparm[4]; /* maximum of 4 components */ @@ -910,7 +906,7 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) image->y1 = image->y0 + (h - 1) * subsampling_dy + 1 + image->y0; /* set image data */ - rect_uchar = (unsigned char *)ibuf->rect; + rect_uchar = (uchar *)ibuf->rect; rect_float = ibuf->rect_float; /* set the destination channels */ diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 06f9202a1c6..e03765fea92 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -37,7 +37,7 @@ static void init_source(j_decompress_ptr cinfo); static boolean fill_input_buffer(j_decompress_ptr cinfo); static void skip_input_data(j_decompress_ptr cinfo, long num_bytes); static void term_source(j_decompress_ptr cinfo); -static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size); +static void memory_source(j_decompress_ptr cinfo, const uchar *buffer, size_t size); static boolean handle_app1(j_decompress_ptr cinfo); static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags, @@ -48,7 +48,7 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, static const uchar jpeg_default_quality = 75; static uchar ibuf_quality; -bool imb_is_a_jpeg(const unsigned char *mem, const size_t size) +bool imb_is_a_jpeg(const uchar *mem, const size_t size) { const char magic[2] = {0xFF, 0xD8}; if (size < sizeof(magic)) { @@ -89,7 +89,7 @@ static void jpeg_error(j_common_ptr cinfo) #if 0 typedef struct { - unsigned char *buffer; + uchar *buffer; int filled; } buffer_struct; #endif @@ -97,7 +97,7 @@ typedef struct { typedef struct { struct jpeg_source_mgr pub; /* public fields */ - const unsigned char *buffer; + const uchar *buffer; int size; JOCTET terminal[2]; } my_source_mgr; @@ -144,7 +144,7 @@ static void term_source(j_decompress_ptr cinfo) (void)cinfo; /* unused */ } -static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size) +static void memory_source(j_decompress_ptr cinfo, const uchar *buffer, size_t size) { my_src_ptr src; @@ -205,11 +205,11 @@ static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, s MAKESTMT(MAKE_BYTE_AVAIL(cinfo, action); bytes_in_buffer--; V = GETJOCTET(*next_input_byte++);) /* As above, but read two bytes interpreted as an unsigned 16-bit integer. - * V should be declared unsigned int or perhaps INT32. + * V should be declared `uint` or perhaps INT32. */ #define INPUT_2BYTES(cinfo, V, action) \ MAKESTMT(MAKE_BYTE_AVAIL(cinfo, action); bytes_in_buffer--; \ - V = ((unsigned int)GETJOCTET(*next_input_byte++)) << 8; \ + V = ((uint)GETJOCTET(*next_input_byte++)) << 8; \ MAKE_BYTE_AVAIL(cinfo, action); \ bytes_in_buffer--; \ V += GETJOCTET(*next_input_byte++);) @@ -445,10 +445,7 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, return ibuf; } -ImBuf *imb_load_jpeg(const unsigned char *buffer, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_jpeg(const uchar *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo; struct my_error_mgr jerr; @@ -521,7 +518,7 @@ struct ImBuf *imb_thumbnail_jpeg(const char *filepath, if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) && (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) { /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */ - unsigned int i = JPEG_APP1_MAX; + uint i = JPEG_APP1_MAX; /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) && !feof(infile) && i--) { diff --git a/source/blender/imbuf/intern/moviecache.cc b/source/blender/imbuf/intern/moviecache.cc index 91a7dfdfae2..54d95578120 100644 --- a/source/blender/imbuf/intern/moviecache.cc +++ b/source/blender/imbuf/intern/moviecache.cc @@ -81,7 +81,7 @@ struct MovieCacheItem { bool added_empty; }; -static unsigned int moviecache_hashhash(const void *keyv) +static uint moviecache_hashhash(const void *keyv) { const MovieCacheKey *key = (const MovieCacheKey *)keyv; diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp index e887424d7b2..5c7b7d9fae4 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp +++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp @@ -32,7 +32,7 @@ OIIO_NAMESPACE_USING using std::string; using std::unique_ptr; -using uchar = unsigned char; +using uchar = uchar; template<class T, class Q> static void fill_all_channels(T *pixels, int width, int height, int components, Q alpha) @@ -147,9 +147,9 @@ static ImBuf *imb_oiio_load_image_float( extern "C" { -bool imb_is_a_photoshop(const unsigned char *mem, size_t size) +bool imb_is_a_photoshop(const uchar *mem, size_t size) { - const unsigned char magic[4] = {'8', 'B', 'P', 'S'}; + const uchar magic[4] = {'8', 'B', 'P', 'S'}; if (size < sizeof(magic)) { return false; } diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index eb6ce5df794..b4ccdfab9a5 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -122,8 +122,7 @@ static void imb_exr_type_by_channels(ChannelList &channels, class IMemStream : public Imf::IStream { public: - IMemStream(unsigned char *exrbuf, size_t exrsize) - : IStream("<memory>"), _exrpos(0), _exrsize(exrsize) + IMemStream(uchar *exrbuf, size_t exrsize) : IStream("<memory>"), _exrpos(0), _exrsize(exrsize) { _exrbuf = exrbuf; } @@ -156,7 +155,7 @@ class IMemStream : public Imf::IStream { private: exr_file_offset_t _exrpos; exr_file_offset_t _exrsize; - unsigned char *_exrbuf; + uchar *_exrbuf; }; /* Memory-Mapped Input Stream */ @@ -178,7 +177,7 @@ class IMMapStream : public Imf::IStream { throw IEX_NAMESPACE::InputExc("BLI_mmap_open failed"); } close(file); - _exrbuf = (unsigned char *)BLI_mmap_get_pointer(_mmap_file); + _exrbuf = (uchar *)BLI_mmap_get_pointer(_mmap_file); } ~IMMapStream() override @@ -216,7 +215,7 @@ class IMMapStream : public Imf::IStream { BLI_mmap_file *_mmap_file; exr_file_offset_t _exrpos; exr_file_offset_t _exrsize; - unsigned char *_exrbuf; + uchar *_exrbuf; }; /* File Input Stream */ @@ -395,7 +394,7 @@ static half float_to_half_safe(const float value) extern "C" { -bool imb_is_a_openexr(const unsigned char *mem, const size_t size) +bool imb_is_a_openexr(const uchar *mem, const size_t size) { /* No define is exposed for this size. */ if (size < 4) { @@ -547,10 +546,10 @@ static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags } } else { - unsigned char *from; + uchar *from; for (int i = ibuf->y - 1; i >= 0; i--) { - from = (unsigned char *)ibuf->rect + 4 * i * width; + from = (uchar *)ibuf->rect + 4 * i * width; for (int j = ibuf->x; j > 0; j--) { to->r = srgb_to_linearrgb((float)from[0] / 255.0f); @@ -1670,29 +1669,29 @@ static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data) if (ELEM(pass->totchan, 3, 4)) { if (pass->chan[0]->chan_id == 'B' || pass->chan[1]->chan_id == 'B' || pass->chan[2]->chan_id == 'B') { - lookup[(unsigned int)'R'] = 0; - lookup[(unsigned int)'G'] = 1; - lookup[(unsigned int)'B'] = 2; - lookup[(unsigned int)'A'] = 3; + lookup[(uint)'R'] = 0; + lookup[(uint)'G'] = 1; + lookup[(uint)'B'] = 2; + lookup[(uint)'A'] = 3; } else if (pass->chan[0]->chan_id == 'Y' || pass->chan[1]->chan_id == 'Y' || pass->chan[2]->chan_id == 'Y') { - lookup[(unsigned int)'X'] = 0; - lookup[(unsigned int)'Y'] = 1; - lookup[(unsigned int)'Z'] = 2; - lookup[(unsigned int)'W'] = 3; + lookup[(uint)'X'] = 0; + lookup[(uint)'Y'] = 1; + lookup[(uint)'Z'] = 2; + lookup[(uint)'W'] = 3; } else { - lookup[(unsigned int)'U'] = 0; - lookup[(unsigned int)'V'] = 1; - lookup[(unsigned int)'A'] = 2; + lookup[(uint)'U'] = 0; + lookup[(uint)'V'] = 1; + lookup[(uint)'A'] = 2; } for (int a = 0; a < pass->totchan; a++) { echan = pass->chan[a]; - echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id]; + echan->rect = pass->rect + lookup[(uint)echan->chan_id]; echan->xstride = pass->totchan; echan->ystride = data->width * pass->totchan; - pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id; + pass->chan_id[(uint)lookup[(uint)echan->chan_id]] = echan->chan_id; } } else { /* unknown */ @@ -1969,7 +1968,7 @@ bool IMB_exr_has_multilayer(void *handle) return imb_exr_is_multi(*data->ifile); } -struct ImBuf *imb_load_openexr(const unsigned char *mem, +struct ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) @@ -1987,7 +1986,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, try { bool is_multi; - membuf = new IMemStream((unsigned char *)mem, size); + membuf = new IMemStream((uchar *)mem, size); file = new MultiPartInputFile(*membuf); Box2i dw = file->header(0).dataWindow(); @@ -2058,7 +2057,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, size_t xstride = sizeof(float[4]); size_t ystride = -xstride * width; - imb_addrectfloatImBuf(ibuf); + imb_addrectfloatImBuf(ibuf, 4); /* Inverse correct first pixel for data-window * coordinates (- dw.min.y because of y flip). */ @@ -2209,7 +2208,7 @@ struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, if (file->header().hasPreviewImage()) { const Imf::PreviewImage &preview = file->header().previewImage(); ImBuf *ibuf = IMB_allocFromBuffer( - (unsigned int *)preview.pixels(), nullptr, preview.width(), preview.height(), 4); + (uint *)preview.pixels(), nullptr, preview.width(), preview.height(), 4); delete file; delete stream; IMB_flipy(ibuf); diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 4d6dfac0ba0..df6959ca90b 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -31,21 +31,21 @@ #include "IMB_colormanagement_intern.h" typedef struct PNGReadStruct { - const unsigned char *data; - unsigned int size; - unsigned int seek; + const uchar *data; + uint size; + uint seek; } PNGReadStruct; static void ReadData(png_structp png_ptr, png_bytep data, png_size_t length); static void WriteData(png_structp png_ptr, png_bytep data, png_size_t length); static void Flush(png_structp png_ptr); -BLI_INLINE unsigned short UPSAMPLE_8_TO_16(const unsigned char _val) +BLI_INLINE ushort UPSAMPLE_8_TO_16(const uchar _val) { return (_val << 8) + _val; } -bool imb_is_a_png(const unsigned char *mem, size_t size) +bool imb_is_a_png(const uchar *mem, size_t size) { const int num_to_check = 8; if (size < num_to_check) { @@ -102,7 +102,7 @@ static float channel_colormanage_noop(float value) } /* wrap to avoid macro calling functions multiple times */ -BLI_INLINE unsigned short ftoshort(float val) +BLI_INLINE ushort ftoshort(float val) { return unit_float_to_ushort_clamp(val); } @@ -112,9 +112,9 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) png_structp png_ptr; png_infop info_ptr; - unsigned char *pixels = NULL; - unsigned char *from, *to; - unsigned short *pixels16 = NULL, *to16; + uchar *pixels = NULL; + uchar *from, *to; + ushort *pixels16 = NULL, *to16; float *from_float, from_straight[4]; png_bytepp row_pointers = NULL; int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY; @@ -169,10 +169,10 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) /* copy image data */ num_bytes = ((size_t)ibuf->x) * ibuf->y * bytesperpixel; if (is_16bit) { - pixels16 = MEM_mallocN(num_bytes * sizeof(unsigned short), "png 16bit pixels"); + pixels16 = MEM_mallocN(num_bytes * sizeof(ushort), "png 16bit pixels"); } else { - pixels = MEM_mallocN(num_bytes * sizeof(unsigned char), "png 8bit pixels"); + pixels = MEM_mallocN(num_bytes * sizeof(uchar), "png 8bit pixels"); } if (pixels == NULL && pixels16 == NULL) { printf( @@ -210,7 +210,7 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) return 0; } - from = (unsigned char *)ibuf->rect; + from = (uchar *)ibuf->rect; to = pixels; from_float = ibuf->rect_float; to16 = pixels16; @@ -453,8 +453,8 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) { png_set_pHYs(png_ptr, info_ptr, - (unsigned int)(ibuf->ppm[0] + 0.5), - (unsigned int)(ibuf->ppm[1] + 0.5), + (uint)(ibuf->ppm[0] + 0.5), + (uint)(ibuf->ppm[1] + 0.5), PNG_RESOLUTION_METER); } @@ -468,15 +468,15 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) /* set the individual row-pointers to point at the correct offsets */ if (is_16bit) { for (i = 0; i < ibuf->y; i++) { - row_pointers[ibuf->y - 1 - i] = (png_bytep)((unsigned short *)pixels16 + + row_pointers[ibuf->y - 1 - i] = (png_bytep)((ushort *)pixels16 + (((size_t)i) * ibuf->x) * bytesperpixel); } } else { for (i = 0; i < ibuf->y; i++) { - row_pointers[ibuf->y - 1 - i] = (png_bytep)((unsigned char *)pixels + - (((size_t)i) * ibuf->x) * bytesperpixel * - sizeof(unsigned char)); + row_pointers[ibuf->y - 1 - i] = (png_bytep)((uchar *)pixels + (((size_t)i) * ibuf->x) * + bytesperpixel * + sizeof(uchar)); } } @@ -521,22 +521,22 @@ static void imb_png_error(png_structp UNUSED(png_ptr), png_const_charp message) fprintf(stderr, "libpng error: %s\n", message); } -ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_loadpng(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; png_structp png_ptr; png_infop info_ptr; - unsigned char *pixels = NULL; - unsigned short *pixels16 = NULL; + uchar *pixels = NULL; + ushort *pixels16 = NULL; png_bytepp row_pointers = NULL; png_uint_32 width, height; int bit_depth, color_type; PNGReadStruct ps; - unsigned char *from, *to; - unsigned short *from16; + uchar *from, *to; + ushort *from16; float *to_float; - unsigned int channels; + uint channels; if (imb_is_a_png(mem, size) == 0) { return NULL; @@ -646,7 +646,7 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors if (ibuf && ((flags & IB_test) == 0)) { if (bit_depth == 16) { - imb_addrectfloatImBuf(ibuf); + imb_addrectfloatImBuf(ibuf, 4); png_set_swap(png_ptr); pixels16 = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(png_uint_16), "pixels"); @@ -718,7 +718,7 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors else { imb_addrectImBuf(ibuf); - pixels = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(unsigned char), "pixels"); + pixels = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(uchar), "pixels"); if (pixels == NULL || ibuf->rect == NULL) { printf("Cannot allocate pixels array\n"); longjmp(png_jmpbuf(png_ptr), 1); @@ -733,16 +733,16 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors /* set the individual row-pointers to point at the correct offsets */ for (int i = 0; i < ibuf->y; i++) { - row_pointers[ibuf->y - 1 - i] = (png_bytep)((unsigned char *)pixels + - (((size_t)i) * ibuf->x) * channels * - sizeof(unsigned char)); + row_pointers[ibuf->y - 1 - i] = (png_bytep)((uchar *)pixels + (((size_t)i) * ibuf->x) * + channels * + sizeof(uchar)); } png_read_image(png_ptr, row_pointers); /* copy image data */ - to = (unsigned char *)ibuf->rect; + to = (uchar *)ibuf->rect; from = pixels; switch (channels) { diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index aa07edf5c3a..00ef12a54f8 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -33,7 +33,7 @@ #define BLU 2 #define EXP 3 #define COLXS 128 -typedef unsigned char RGBE[4]; +typedef uchar RGBE[4]; typedef float fCOLOR[3]; /* copy source -> dest */ @@ -41,10 +41,7 @@ typedef float fCOLOR[3]; (c2[RED] = c1[RED], c2[GRN] = c1[GRN], c2[BLU] = c1[BLU], c2[EXP] = c1[EXP]) /* read routines */ -static const unsigned char *oldreadcolrs(RGBE *scan, - const unsigned char *mem, - int xmax, - const unsigned char *mem_eof) +static const uchar *oldreadcolrs(RGBE *scan, const uchar *mem, int xmax, const uchar *mem_eof) { size_t i, rshift = 0, len = xmax; while (len > 0) { @@ -72,10 +69,7 @@ static const unsigned char *oldreadcolrs(RGBE *scan, return mem; } -static const unsigned char *freadcolrs(RGBE *scan, - const unsigned char *mem, - int xmax, - const unsigned char *mem_eof) +static const uchar *freadcolrs(RGBE *scan, const uchar *mem, int xmax, const uchar *mem_eof) { if (UNLIKELY(mem_eof - mem < 4)) { return NULL; @@ -118,7 +112,7 @@ static const unsigned char *freadcolrs(RGBE *scan, } val = *mem++; while (code--) { - scan[j++][i] = (unsigned char)val; + scan[j++][i] = (uchar)val; } } else { @@ -167,16 +161,16 @@ static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe) } else { d = (float)frexp(d, &e) * 256.0f / d; - rgbe[RED] = (unsigned char)(fcol[RED] * d); - rgbe[GRN] = (unsigned char)(fcol[GRN] * d); - rgbe[BLU] = (unsigned char)(fcol[BLU] * d); - rgbe[EXP] = (unsigned char)(e + COLXS); + rgbe[RED] = (uchar)(fcol[RED] * d); + rgbe[GRN] = (uchar)(fcol[GRN] * d); + rgbe[BLU] = (uchar)(fcol[BLU] * d); + rgbe[EXP] = (uchar)(e + COLXS); } } /* ImBuf read */ -bool imb_is_a_hdr(const unsigned char *buf, const size_t size) +bool imb_is_a_hdr(const uchar *buf, const size_t size) { /* NOTE: `#?RADIANCE` is used by other programs such as `ImageMagik`, * Although there are some files in the wild that only use `#?` (from looking online). @@ -187,17 +181,14 @@ bool imb_is_a_hdr(const unsigned char *buf, const size_t size) * * See: http://paulbourke.net/dataformats/pic/ */ - const unsigned char magic[2] = {'#', '?'}; + const uchar magic[2] = {'#', '?'}; if (size < sizeof(magic)) { return false; } return memcmp(buf, magic, sizeof(magic)) == 0; } -struct ImBuf *imb_loadhdr(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +struct ImBuf *imb_loadhdr(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf; RGBE *sline; @@ -205,7 +196,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, float *rect_float; int found = 0; int width = 0, height = 0; - const unsigned char *ptr, *mem_eof = mem + size; + const uchar *ptr, *mem_eof = mem + size; char oriY[3], oriX[3]; if (!imb_is_a_hdr(mem, size)) { @@ -246,7 +237,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, * since the format uses RLE compression. Can cause excessive memory allocation to occur. */ /* find end of this line, data right behind it */ - ptr = (const unsigned char *)strchr((const char *)&mem[x], '\n'); + ptr = (const uchar *)strchr((const char *)&mem[x], '\n'); if (ptr == NULL || ptr >= mem_eof) { return NULL; } @@ -306,7 +297,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, /* ImBuf write */ static int fwritecolrs( - FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan) + FILE *file, int width, int channels, const uchar *ibufscan, const float *fpscan) { int beg, c2, count = 0; fCOLOR fcol; @@ -343,8 +334,8 @@ static int fwritecolrs( /* put magic header */ putc(2, file); putc(2, file); - putc((unsigned char)(width >> 8), file); - putc((unsigned char)(width & 255), file); + putc((uchar)(width >> 8), file); + putc((uchar)(width & 255), file); /* put components separately */ for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < width; j += count) { /* find next run */ @@ -362,8 +353,8 @@ static int fwritecolrs( c2 = j + 1; while (rgbe_scan[c2++][i] == rgbe_scan[j][i]) { if (c2 == beg) { /* short run */ - putc((unsigned char)(128 + beg - j), file); - putc((unsigned char)(rgbe_scan[j][i]), file); + putc((uchar)(128 + beg - j), file); + putc((uchar)(rgbe_scan[j][i]), file); j = beg; break; } @@ -373,13 +364,13 @@ static int fwritecolrs( if ((c2 = beg - j) > 128) { c2 = 128; } - putc((unsigned char)(c2), file); + putc((uchar)(c2), file); while (c2--) { putc(rgbe_scan[j++][i], file); } } if (count >= MINRUN) { /* write out run */ - putc((unsigned char)(128 + count), file); + putc((uchar)(128 + count), file); putc(rgbe_scan[beg][i], file); } else { @@ -411,7 +402,7 @@ bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags) FILE *file = BLI_fopen(filepath, "wb"); float *fp = NULL; size_t width = ibuf->x, height = ibuf->y; - unsigned char *cp = NULL; + uchar *cp = NULL; (void)flags; /* unused */ @@ -422,7 +413,7 @@ bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags) writeHeader(file, width, height); if (ibuf->rect) { - cp = (unsigned char *)ibuf->rect + ibuf->channels * (height - 1) * width; + cp = (uchar *)ibuf->rect + ibuf->channels * (height - 1) * width; } if (ibuf->rect_float) { fp = ibuf->rect_float + ibuf->channels * (height - 1) * width; diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index b33e9dc4e0e..a9b79ad6d19 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -81,11 +81,8 @@ static void imb_handle_alpha(ImBuf *ibuf, colormanage_imbuf_make_linear(ibuf, effective_colorspace); } -ImBuf *IMB_ibImageFromMemory(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE], - const char *descr) +ImBuf *IMB_ibImageFromMemory( + const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE], const char *descr) { ImBuf *ibuf; const ImFileType *type; @@ -157,7 +154,7 @@ ImBuf *IMB_loadifffile( int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr) { ImBuf *ibuf; - unsigned char *mem; + uchar *mem; size_t size; if (file == -1) { @@ -319,9 +316,9 @@ ImBuf *IMB_testiffname(const char *filepath, int flags) return ibuf; } -static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int *rect) +static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, uint *rect) { - unsigned char *mem; + uchar *mem; size_t size; if (file == -1) { @@ -352,7 +349,7 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int imb_mmap_unlock(); } -void imb_loadtile(ImBuf *ibuf, int tx, int ty, unsigned int *rect) +void imb_loadtile(ImBuf *ibuf, int tx, int ty, uint *rect) { int file; diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index 2f864534d61..4159aa851c4 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -21,9 +21,9 @@ #include "MEM_guardedalloc.h" -void IMB_blend_color_byte(unsigned char dst[4], - const unsigned char src1[4], - const unsigned char src2[4], +void IMB_blend_color_byte(uchar dst[4], + const uchar src1[4], + const uchar src2[4], IMB_BlendMode mode) { switch (mode) { @@ -487,17 +487,15 @@ void IMB_rectcpy(ImBuf *dbuf, false); } -typedef void (*IMB_blend_func)(unsigned char *dst, - const unsigned char *src1, - const unsigned char *src2); +typedef void (*IMB_blend_func)(uchar *dst, const uchar *src1, const uchar *src2); typedef void (*IMB_blend_func_float)(float *dst, const float *src1, const float *src2); void IMB_rectblend(ImBuf *dbuf, const ImBuf *obuf, const ImBuf *sbuf, - unsigned short *dmask, - const unsigned short *curvemask, - const unsigned short *texmask, + ushort *dmask, + const ushort *curvemask, + const ushort *texmask, float mask_max, int destx, int desty, @@ -510,11 +508,11 @@ void IMB_rectblend(ImBuf *dbuf, IMB_BlendMode mode, bool accumulate) { - unsigned int *drect = NULL, *orect = NULL, *srect = NULL, *dr, * or, *sr; + uint *drect = NULL, *orect = NULL, *srect = NULL, *dr, * or, *sr; float *drectf = NULL, *orectf = NULL, *srectf = NULL, *drf, *orf, *srf; - const unsigned short *cmaskrect = curvemask, *cmr; - unsigned short *dmaskrect = dmask, *dmr; - const unsigned short *texmaskrect = texmask, *tmr; + const ushort *cmaskrect = curvemask, *cmr; + ushort *dmaskrect = dmask, *dmr; + const ushort *texmaskrect = texmask, *tmr; int srcskip, destskip, origskip, x; IMB_blend_func func = NULL; IMB_blend_func_float func_float = NULL; @@ -766,7 +764,7 @@ void IMB_rectblend(ImBuf *dbuf, if (dmaskrect) { dmr = dmaskrect; for (x = width; x > 0; x--, dr++, or ++, sr++, dmr++, cmr++) { - unsigned char *src = (unsigned char *)sr; + uchar *src = (uchar *)sr; float mask_lim = mask_max * (*cmr); if (texmaskrect) { @@ -786,7 +784,7 @@ void IMB_rectblend(ImBuf *dbuf, mask = min_ff(mask, 65535.0); if (mask > *dmr) { - unsigned char mask_src[4]; + uchar mask_src[4]; *dmr = mask; @@ -797,11 +795,11 @@ void IMB_rectblend(ImBuf *dbuf, if (mode == IMB_BLEND_INTERPOLATE) { mask_src[3] = src[3]; blend_color_interpolate_byte( - (unsigned char *)dr, (unsigned char *) or, mask_src, mask / 65535.0f); + (uchar *)dr, (uchar *) or, mask_src, mask / 65535.0f); } else { mask_src[3] = divide_round_i(src[3] * mask, 65535); - func((unsigned char *)dr, (unsigned char *) or, mask_src); + func((uchar *)dr, (uchar *) or, mask_src); } } } @@ -811,7 +809,7 @@ void IMB_rectblend(ImBuf *dbuf, /* no destination mask buffer, do regular blend with masktexture if present */ else { for (x = width; x > 0; x--, dr++, or ++, sr++, cmr++) { - unsigned char *src = (unsigned char *)sr; + uchar *src = (uchar *)sr; float mask = (float)mask_max * ((float)(*cmr)); if (texmaskrect) { @@ -821,7 +819,7 @@ void IMB_rectblend(ImBuf *dbuf, mask = min_ff(mask, 65535.0); if (src[3] && (mask > 0.0f)) { - unsigned char mask_src[4]; + uchar mask_src[4]; mask_src[0] = src[0]; mask_src[1] = src[1]; @@ -830,11 +828,11 @@ void IMB_rectblend(ImBuf *dbuf, if (mode == IMB_BLEND_INTERPOLATE) { mask_src[3] = src[3]; blend_color_interpolate_byte( - (unsigned char *)dr, (unsigned char *) or, mask_src, mask / 65535.0f); + (uchar *)dr, (uchar *) or, mask_src, mask / 65535.0f); } else { mask_src[3] = divide_round_i(src[3] * mask, 65535); - func((unsigned char *)dr, (unsigned char *) or, mask_src); + func((uchar *)dr, (uchar *) or, mask_src); } } } @@ -848,8 +846,8 @@ void IMB_rectblend(ImBuf *dbuf, else { /* regular blending */ for (x = width; x > 0; x--, dr++, or ++, sr++) { - if (((unsigned char *)sr)[3]) { - func((unsigned char *)dr, (unsigned char *) or, (unsigned char *)sr); + if (((uchar *)sr)[3]) { + func((uchar *)dr, (uchar *) or, (uchar *)sr); } } } @@ -956,8 +954,8 @@ void IMB_rectblend(ImBuf *dbuf, typedef struct RectBlendThreadData { ImBuf *dbuf; const ImBuf *obuf, *sbuf; - unsigned short *dmask; - const unsigned short *curvemask, *texmask; + ushort *dmask; + const ushort *curvemask, *texmask; float mask_max; int destx, desty, origx, origy; int srcx, srcy, width; @@ -991,9 +989,9 @@ static void rectblend_thread_do(void *data_v, int scanline) void IMB_rectblend_threaded(ImBuf *dbuf, const ImBuf *obuf, const ImBuf *sbuf, - unsigned short *dmask, - const unsigned short *curvemask, - const unsigned short *texmask, + ushort *dmask, + const ushort *curvemask, + const ushort *texmask, float mask_max, int destx, int desty, @@ -1052,7 +1050,7 @@ void IMB_rectfill(ImBuf *drect, const float col[4]) int num; if (drect->rect) { - unsigned int *rrect = drect->rect; + uint *rrect = drect->rect; char ccol[4]; ccol[0] = (int)(col[0] * 255); @@ -1062,7 +1060,7 @@ void IMB_rectfill(ImBuf *drect, const float col[4]) num = drect->x * drect->y; for (; num > 0; num--) { - *rrect++ = *((unsigned int *)ccol); + *rrect++ = *((uint *)ccol); } } @@ -1106,15 +1104,15 @@ void IMB_rectfill_area_replace( return; } - unsigned char col_char[4] = {col[0] * 255, col[1] * 255, col[2] * 255, col[3] * 255}; + uchar col_char[4] = {col[0] * 255, col[1] * 255, col[2] * 255, col[3] * 255}; for (int y = y1; y < y2; y++) { for (int x = x1; x < x2; x++) { size_t offset = ((size_t)ibuf->x) * y * 4 + 4 * x; if (ibuf->rect) { - unsigned char *rrect = (unsigned char *)ibuf->rect + offset; - memcpy(rrect, &col_char, sizeof(unsigned char) * 4); + uchar *rrect = (uchar *)ibuf->rect + offset; + memcpy(rrect, &col_char, sizeof(uchar) * 4); } if (ibuf->rect_float) { @@ -1125,7 +1123,7 @@ void IMB_rectfill_area_replace( } } -void buf_rectfill_area(unsigned char *rect, +void buf_rectfill_area(uchar *rect, float *rectf, int width, int height, @@ -1165,8 +1163,8 @@ void buf_rectfill_area(unsigned char *rect, aich = ai / 255.0f; if (rect) { - unsigned char *pixel; - unsigned char chr = 0, chg = 0, chb = 0; + uchar *pixel; + uchar chr = 0, chg = 0, chb = 0; float fr = 0, fg = 0, fb = 0; const int alphaint = unit_float_to_uchar_clamp(a); @@ -1247,16 +1245,8 @@ void IMB_rectfill_area(ImBuf *ibuf, if (!ibuf) { return; } - buf_rectfill_area((unsigned char *)ibuf->rect, - ibuf->rect_float, - ibuf->x, - ibuf->y, - col, - display, - x1, - y1, - x2, - y2); + buf_rectfill_area( + (uchar *)ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, col, display, x1, y1, x2, y2); } void IMB_rectfill_alpha(ImBuf *ibuf, const float value) @@ -1271,8 +1261,8 @@ void IMB_rectfill_alpha(ImBuf *ibuf, const float value) } if (ibuf->rect) { - const unsigned char cvalue = value * 255; - unsigned char *cbuf = ((unsigned char *)ibuf->rect) + 3; + const uchar cvalue = value * 255; + uchar *cbuf = ((uchar *)ibuf->rect) + 3; for (i = ibuf->x * ibuf->y; i > 0; i--, cbuf += 4) { *cbuf = cvalue; } diff --git a/source/blender/imbuf/intern/rotate.c b/source/blender/imbuf/intern/rotate.c index ac07ce85526..7081bf2ad26 100644 --- a/source/blender/imbuf/intern/rotate.c +++ b/source/blender/imbuf/intern/rotate.c @@ -22,7 +22,7 @@ void IMB_flipy(struct ImBuf *ibuf) } if (ibuf->rect) { - unsigned int *top, *bottom, *line; + uint *top, *bottom, *line; x_size = ibuf->x; y_size = ibuf->y; @@ -88,7 +88,7 @@ void IMB_flipx(struct ImBuf *ibuf) for (yi = y - 1; yi >= 0; yi--) { const size_t x_offset = (size_t)x * yi; for (xr = x - 1, xl = 0; xr >= xl; xr--, xl++) { - SWAP(unsigned int, ibuf->rect[x_offset + xr], ibuf->rect[x_offset + xl]); + SWAP(uint, ibuf->rect[x_offset + xr], ibuf->rect[x_offset + xl]); } } } diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c index f4abc668402..05bee77a6cb 100644 --- a/source/blender/imbuf/intern/scaling.c +++ b/source/blender/imbuf/intern/scaling.c @@ -324,10 +324,9 @@ struct ImBuf *IMB_double_y(struct ImBuf *ibuf1) /* pretty much specific functions which converts uchar <-> ushort but assumes * ushort range of 255*255 which is more convenient here */ -MINLINE void straight_uchar_to_premul_ushort(unsigned short result[4], - const unsigned char color[4]) +MINLINE void straight_uchar_to_premul_ushort(ushort result[4], const uchar color[4]) { - unsigned short alpha = color[3]; + ushort alpha = color[3]; result[0] = color[0] * alpha; result[1] = color[1] * alpha; @@ -335,7 +334,7 @@ MINLINE void straight_uchar_to_premul_ushort(unsigned short result[4], result[3] = alpha * 256; } -MINLINE void premul_ushort_to_straight_uchar(unsigned char *result, const unsigned short color[4]) +MINLINE void premul_ushort_to_straight_uchar(uchar *result, const ushort color[4]) { if (color[3] <= 255) { result[0] = unit_ushort_to_uchar(color[0]); @@ -344,7 +343,7 @@ MINLINE void premul_ushort_to_straight_uchar(unsigned char *result, const unsign result[3] = unit_ushort_to_uchar(color[3]); } else { - unsigned short alpha = color[3] / 256; + ushort alpha = color[3] / 256; result[0] = unit_ushort_to_uchar((ushort)(color[0] / alpha * 256)); result[1] = unit_ushort_to_uchar((ushort)(color[1] / alpha * 256)); @@ -373,25 +372,25 @@ void imb_onehalf_no_alloc(struct ImBuf *ibuf2, struct ImBuf *ibuf1) } if (do_rect) { - unsigned char *cp1, *cp2, *dest; + uchar *cp1, *cp2, *dest; - cp1 = (unsigned char *)ibuf1->rect; - dest = (unsigned char *)ibuf2->rect; + cp1 = (uchar *)ibuf1->rect; + dest = (uchar *)ibuf2->rect; for (y = ibuf2->y; y > 0; y--) { cp2 = cp1 + (ibuf1->x << 2); for (x = ibuf2->x; x > 0; x--) { - unsigned short p1i[8], p2i[8], desti[4]; + ushort p1i[8], p2i[8], desti[4]; straight_uchar_to_premul_ushort(p1i, cp1); straight_uchar_to_premul_ushort(p2i, cp2); straight_uchar_to_premul_ushort(p1i + 4, cp1 + 4); straight_uchar_to_premul_ushort(p2i + 4, cp2 + 4); - desti[0] = ((unsigned int)p1i[0] + p2i[0] + p1i[4] + p2i[4]) >> 2; - desti[1] = ((unsigned int)p1i[1] + p2i[1] + p1i[5] + p2i[5]) >> 2; - desti[2] = ((unsigned int)p1i[2] + p2i[2] + p1i[6] + p2i[6]) >> 2; - desti[3] = ((unsigned int)p1i[3] + p2i[3] + p1i[7] + p2i[7]) >> 2; + desti[0] = ((uint)p1i[0] + p2i[0] + p1i[4] + p2i[4]) >> 2; + desti[1] = ((uint)p1i[1] + p2i[1] + p1i[5] + p2i[5]) >> 2; + desti[2] = ((uint)p1i[2] + p2i[2] + p1i[6] + p2i[6]) >> 2; + desti[3] = ((uint)p1i[3] + p2i[3] + p1i[7] + p2i[7]) >> 2; premul_ushort_to_straight_uchar(dest, desti); @@ -460,12 +459,8 @@ ImBuf *IMB_onehalf(struct ImBuf *ibuf1) /* q_scale_linear_interpolation helper functions */ -static void enlarge_picture_byte(unsigned char *src, - unsigned char *dst, - int src_width, - int src_height, - int dst_width, - int dst_height) +static void enlarge_picture_byte( + uchar *src, uchar *dst, int src_width, int src_height, int dst_width, int dst_height) { double ratiox = (double)(dst_width - 1.0) / (double)(src_width - 1.001); double ratioy = (double)(dst_height - 1.0) / (double)(src_height - 1.001); @@ -477,8 +472,8 @@ static void enlarge_picture_byte(unsigned char *src, y_src = 0; for (y_dst = 0; y_dst < dst_height; y_dst++) { - unsigned char *line1 = src + (y_src >> 16) * 4 * src_width; - unsigned char *line2 = line1 + 4 * src_width; + uchar *line1 = src + (y_src >> 16) * 4 * src_width; + uchar *line2 = line1 + 4 * src_width; uintptr_t weight1y = 65536 - (y_src & 0xffff); uintptr_t weight2y = 65536 - weight1y; @@ -491,7 +486,7 @@ static void enlarge_picture_byte(unsigned char *src, uintptr_t weight1x = 65536 - (x_src & 0xffff); uintptr_t weight2x = 65536 - weight1x; - unsigned long x = (x_src >> 16) * 4; + ulong x = (x_src >> 16) * 4; *dst++ = ((((line1[x] * weight1y) >> 16) * weight1x) >> 16) + ((((line2[x] * weight2y) >> 16) * weight1x) >> 16) + @@ -528,19 +523,15 @@ struct scale_outpix_byte { uintptr_t weight; }; -static void shrink_picture_byte(unsigned char *src, - unsigned char *dst, - int src_width, - int src_height, - int dst_width, - int dst_height) +static void shrink_picture_byte( + uchar *src, uchar *dst, int src_width, int src_height, int dst_width, int dst_height) { double ratiox = (double)(dst_width) / (double)(src_width); double ratioy = (double)(dst_height) / (double)(src_height); uintptr_t x_src, dx_dst, x_dst; uintptr_t y_src, dy_dst, y_dst; intptr_t y_counter; - unsigned char *dst_begin = dst; + uchar *dst_begin = dst; struct scale_outpix_byte *dst_line1 = NULL; struct scale_outpix_byte *dst_line2 = NULL; @@ -556,7 +547,7 @@ static void shrink_picture_byte(unsigned char *src, y_dst = 0; y_counter = 65536; for (y_src = 0; y_src < src_height; y_src++) { - unsigned char *line = src + y_src * 4 * src_width; + uchar *line = src + y_src * 4 * src_width; uintptr_t weight1y = 65535 - (y_dst & 0xffff); uintptr_t weight2y = 65535 - weight1y; x_dst = 0; @@ -643,12 +634,8 @@ static void shrink_picture_byte(unsigned char *src, MEM_freeN(dst_line2); } -static void q_scale_byte(unsigned char *in, - unsigned char *out, - int in_width, - int in_height, - int dst_width, - int dst_height) +static void q_scale_byte( + uchar *in, uchar *out, int in_width, int in_height, int dst_width, int dst_height) { if (dst_width > in_width && dst_height > in_height) { enlarge_picture_byte(in, out, in_width, in_height, dst_width, dst_height); @@ -868,12 +855,12 @@ static bool q_scale_linear_interpolation(struct ImBuf *ibuf, int newx, int newy) } if (ibuf->rect) { - unsigned char *newrect = MEM_mallocN(sizeof(int) * newx * newy, "q_scale rect"); - q_scale_byte((unsigned char *)ibuf->rect, newrect, ibuf->x, ibuf->y, newx, newy); + uchar *newrect = MEM_mallocN(sizeof(int) * newx * newy, "q_scale rect"); + q_scale_byte((uchar *)ibuf->rect, newrect, ibuf->x, ibuf->y, newx, newy); imb_freerectImBuf(ibuf); ibuf->mall |= IB_rect; - ibuf->rect = (unsigned int *)newrect; + ibuf->rect = (uint *)newrect; } if (ibuf->rect_float) { float *newrect = MEM_mallocN(sizeof(float[4]) * newx * newy, "q_scale rectfloat"); @@ -1014,7 +1001,7 @@ static ImBuf *scaledownx(struct ImBuf *ibuf, int newx) BLI_assert((uchar *)rect - ((uchar *)ibuf->rect) == rect_size); /* see bug T26502. */ imb_freerectImBuf(ibuf); ibuf->mall |= IB_rect; - ibuf->rect = (unsigned int *)_newrect; + ibuf->rect = (uint *)_newrect; } if (do_float) { // printf("%ld %ld\n", rectf - ibuf->rect_float, rect_size); @@ -1156,7 +1143,7 @@ static ImBuf *scaledowny(struct ImBuf *ibuf, int newy) BLI_assert((uchar *)rect - ((uchar *)ibuf->rect) == rect_size); /* see bug T26502. */ imb_freerectImBuf(ibuf); ibuf->mall |= IB_rect; - ibuf->rect = (unsigned int *)_newrect; + ibuf->rect = (uint *)_newrect; } if (do_float) { // printf("%ld %ld\n", rectf - ibuf->rect_float, rect_size); @@ -1361,7 +1348,7 @@ static ImBuf *scaleupx(struct ImBuf *ibuf, int newx) if (do_rect) { imb_freerectImBuf(ibuf); ibuf->mall |= IB_rect; - ibuf->rect = (unsigned int *)_newrect; + ibuf->rect = (uint *)_newrect; } if (do_float) { imb_freerectfloatImBuf(ibuf); @@ -1564,7 +1551,7 @@ static ImBuf *scaleupy(struct ImBuf *ibuf, int newy) if (do_rect) { imb_freerectImBuf(ibuf); ibuf->mall |= IB_rect; - ibuf->rect = (unsigned int *)_newrect; + ibuf->rect = (uint *)_newrect; } if (do_float) { imb_freerectfloatImBuf(ibuf); @@ -1641,7 +1628,7 @@ static void scalefast_Z_ImBuf(ImBuf *ibuf, int newx, int newy) } } -bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy) +bool IMB_scaleImBuf(struct ImBuf *ibuf, uint newx, uint newy) { BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); @@ -1686,11 +1673,11 @@ struct imbufRGBA { float r, g, b, a; }; -bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy) +bool IMB_scalefastImBuf(struct ImBuf *ibuf, uint newx, uint newy) { BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); - unsigned int *rect, *_newrect, *newrect; + uint *rect, *_newrect, *newrect; struct imbufRGBA *rectf, *_newrectf, *newrectf; int x, y; bool do_float = false, do_rect = false; @@ -1789,23 +1776,23 @@ bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy typedef struct ScaleTreadInitData { ImBuf *ibuf; - unsigned int newx; - unsigned int newy; + uint newx; + uint newy; - unsigned char *byte_buffer; + uchar *byte_buffer; float *float_buffer; } ScaleTreadInitData; typedef struct ScaleThreadData { ImBuf *ibuf; - unsigned int newx; - unsigned int newy; + uint newx; + uint newy; int start_line; int tot_line; - unsigned char *byte_buffer; + uchar *byte_buffer; float *float_buffer; } ScaleThreadData; @@ -1844,9 +1831,8 @@ static void *do_scale_thread(void *data_v) int offset = y * data->newx + x; if (data->byte_buffer) { - unsigned char *pixel = data->byte_buffer + 4 * offset; - BLI_bilinear_interpolation_char( - (unsigned char *)ibuf->rect, pixel, ibuf->x, ibuf->y, 4, u, v); + uchar *pixel = data->byte_buffer + 4 * offset; + BLI_bilinear_interpolation_char((uchar *)ibuf->rect, pixel, ibuf->x, ibuf->y, 4, u, v); } if (data->float_buffer) { @@ -1860,7 +1846,7 @@ static void *do_scale_thread(void *data_v) return NULL; } -void IMB_scaleImBuf_threaded(ImBuf *ibuf, unsigned int newx, unsigned int newy) +void IMB_scaleImBuf_threaded(ImBuf *ibuf, uint newx, uint newy) { BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); @@ -1893,7 +1879,7 @@ void IMB_scaleImBuf_threaded(ImBuf *ibuf, unsigned int newx, unsigned int newy) if (ibuf->rect) { imb_freerectImBuf(ibuf); ibuf->mall |= IB_rect; - ibuf->rect = (unsigned int *)init_data.byte_buffer; + ibuf->rect = (uint *)init_data.byte_buffer; } if (ibuf->rect_float) { diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index 2a0baaf6172..eb2701b5b9c 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -650,8 +650,8 @@ static void imb_stereo3d_squeeze_rect( IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); ibuf = IMB_allocImBuf(width, height, channels, IB_rect); - IMB_buffer_byte_from_byte((unsigned char *)ibuf->rect, - (unsigned char *)rect, + IMB_buffer_byte_from_byte((uchar *)ibuf->rect, + (uchar *)rect, IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, @@ -661,7 +661,7 @@ static void imb_stereo3d_squeeze_rect( width); IMB_scaleImBuf_threaded(ibuf, x, y); - memcpy(rect, ibuf->rect, x * y * sizeof(unsigned int)); + memcpy(rect, ibuf->rect, x * y * sizeof(uint)); IMB_freeImBuf(ibuf); } @@ -761,11 +761,14 @@ ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, Im IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height); - ibuf_stereo = IMB_allocImBuf( - width, height, ibuf_left->planes, (is_float ? IB_rectfloat : IB_rect)); + ibuf_stereo = IMB_allocImBuf(width, height, ibuf_left->planes, 0); - ibuf_stereo->rect_colorspace = ibuf_left->rect_colorspace; - ibuf_stereo->float_colorspace = ibuf_left->float_colorspace; + if (is_float) { + imb_addrectfloatImBuf(ibuf_stereo, ibuf_left->channels); + } + else { + imb_addrectImBuf(ibuf_stereo); + } ibuf_stereo->flags = ibuf_left->flags; @@ -773,7 +776,7 @@ ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, Im is_float, ibuf_left->x, ibuf_left->y, - 4, + ibuf_left->channels, (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo->rect, @@ -1286,10 +1289,17 @@ void IMB_ImBufFromStereo3d(const Stereo3dFormat *s3d, &width, &height); - ibuf_left = IMB_allocImBuf( - width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); - ibuf_right = IMB_allocImBuf( - width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); + ibuf_left = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, 0); + ibuf_right = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, 0); + + if (is_float) { + imb_addrectfloatImBuf(ibuf_left, ibuf_stereo3d->channels); + imb_addrectfloatImBuf(ibuf_right, ibuf_stereo3d->channels); + } + else { + imb_addrectImBuf(ibuf_left); + imb_addrectImBuf(ibuf_right); + } ibuf_left->flags = ibuf_stereo3d->flags; ibuf_right->flags = ibuf_stereo3d->flags; @@ -1307,7 +1317,7 @@ void IMB_ImBufFromStereo3d(const Stereo3dFormat *s3d, is_float, ibuf_left->x, ibuf_left->y, - 4, + ibuf_left->channels, (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo3d->rect, diff --git a/source/blender/imbuf/intern/targa.c b/source/blender/imbuf/intern/targa.c index 7cf90cd12e2..ed6e6e9866d 100644 --- a/source/blender/imbuf/intern/targa.c +++ b/source/blender/imbuf/intern/targa.c @@ -30,18 +30,18 @@ /***/ typedef struct TARGA { - unsigned char numid; - unsigned char maptyp; - unsigned char imgtyp; + uchar numid; + uchar maptyp; + uchar imgtyp; short maporig; short mapsize; - unsigned char mapbits; + uchar mapbits; short xorig; short yorig; short xsize; short ysize; - unsigned char pixsize; - unsigned char imgdes; + uchar pixsize; + uchar imgdes; } TARGA; /** @@ -54,7 +54,7 @@ typedef struct TARGA { /***/ -static int tga_out1(unsigned int data, FILE *file) +static int tga_out1(uint data, FILE *file) { uchar *p; @@ -65,7 +65,7 @@ static int tga_out1(unsigned int data, FILE *file) return ~EOF; } -static int tga_out2(unsigned int data, FILE *file) +static int tga_out2(uint data, FILE *file) { uchar *p; @@ -79,7 +79,7 @@ static int tga_out2(unsigned int data, FILE *file) return ~EOF; } -static int tga_out3(unsigned int data, FILE *file) +static int tga_out3(uint data, FILE *file) { uchar *p; @@ -96,7 +96,7 @@ static int tga_out3(unsigned int data, FILE *file) return ~EOF; } -static int tga_out4(unsigned int data, FILE *file) +static int tga_out4(uint data, FILE *file) { uchar *p; @@ -117,11 +117,11 @@ static int tga_out4(unsigned int data, FILE *file) return ~EOF; } -static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(unsigned int, FILE *)) +static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(uint, FILE *)) { int last, this; int copy, bytes; - unsigned int *rect, *rectstart, *temp; + uint *rect, *rectstart, *temp; int y; for (y = 0; y < ibuf->y; y++) { @@ -345,7 +345,7 @@ bool imb_savetarga(struct ImBuf *ibuf, const char *filepath, int UNUSED(flags)) return ok; } -static bool checktarga(TARGA *tga, const unsigned char *mem, const size_t size) +static bool checktarga(TARGA *tga, const uchar *mem, const size_t size) { if (size < TARGA_HEADER_SIZE) { return false; @@ -397,14 +397,14 @@ static bool checktarga(TARGA *tga, const unsigned char *mem, const size_t size) return true; } -bool imb_is_a_targa(const unsigned char *buf, size_t size) +bool imb_is_a_targa(const uchar *buf, size_t size) { TARGA tga; return checktarga(&tga, buf, size); } -static void complete_partial_load(struct ImBuf *ibuf, unsigned int *rect) +static void complete_partial_load(struct ImBuf *ibuf, uint *rect) { int size = (ibuf->x * ibuf->y) - (rect - ibuf->rect); if (size) { @@ -420,11 +420,11 @@ static void complete_partial_load(struct ImBuf *ibuf, unsigned int *rect) } } -static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem_size, int psize) +static void decodetarga(struct ImBuf *ibuf, const uchar *mem, size_t mem_size, int psize) { - const unsigned char *mem_end = mem + mem_size; + const uchar *mem_end = mem + mem_size; int count, col, size; - unsigned int *rect; + uint *rect; uchar *cp = (uchar *)&col; if (ibuf == NULL) { @@ -545,11 +545,11 @@ partial_load: complete_partial_load(ibuf, rect); } -static void ldtarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem_size, int psize) +static void ldtarga(struct ImBuf *ibuf, const uchar *mem, size_t mem_size, int psize) { - const unsigned char *mem_end = mem + mem_size; + const uchar *mem_end = mem + mem_size; int col, size; - unsigned int *rect; + uint *rect; uchar *cp = (uchar *)&col; if (ibuf == NULL) { @@ -609,15 +609,12 @@ partial_load: complete_partial_load(ibuf, rect); } -ImBuf *imb_loadtarga(const unsigned char *mem, - size_t mem_size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_loadtarga(const uchar *mem, size_t mem_size, int flags, char colorspace[IM_MAX_SPACE]) { TARGA tga; struct ImBuf *ibuf; int count, size; - unsigned int *rect, *cmap = NULL /*, mincol = 0*/, cmap_max = 0; + uint *rect, *cmap = NULL /*, mincol = 0*/, cmap_max = 0; int32_t cp_data; uchar *cp = (uchar *)&cp_data; @@ -650,7 +647,7 @@ ImBuf *imb_loadtarga(const unsigned char *mem, /* Load color map. */ // mincol = tga.maporig; /* UNUSED */ cmap_max = tga.mapsize; - cmap = MEM_callocN(sizeof(unsigned int) * cmap_max, "targa cmap"); + cmap = MEM_callocN(sizeof(uint) * cmap_max, "targa cmap"); for (count = 0; count < cmap_max; count++) { switch (tga.mapbits >> 3) { @@ -753,7 +750,7 @@ ImBuf *imb_loadtarga(const unsigned char *mem, } if (tga.pixsize == 16) { - unsigned int col; + uint col; rect = ibuf->rect; for (size = ibuf->x * ibuf->y; size > 0; size--, rect++) { col = *rect; @@ -773,10 +770,10 @@ ImBuf *imb_loadtarga(const unsigned char *mem, if (ELEM(tga.imgtyp, 3, 11)) { uchar *crect; - unsigned int *lrect, col; + uint *lrect, col; crect = (uchar *)ibuf->rect; - lrect = (unsigned int *)ibuf->rect; + lrect = (uint *)ibuf->rect; for (size = ibuf->x * ibuf->y; size > 0; size--) { col = *lrect++; diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 6f39009d38d..d535bd00501 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -136,7 +136,7 @@ typedef enum { /* Don't lose comment alignment. */ /* clang-format off */ -static const unsigned char acceptable[96] = { +static const uchar acceptable[96] = { /* A table of the ASCII chars from space (32) to DEL (127) */ /* ! " # $ % & ' ( ) * + , - . / */ 0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C, @@ -176,7 +176,7 @@ static void escape_uri_string(const char *string, escaped_string_size -= 1; for (q = escaped_string, p = string; (*p != '\0') && escaped_string_size; p++) { - c = (unsigned char)*p; + c = (uchar)*p; if (!ACCEPTABLE(c)) { if (escaped_string_size < 3) { @@ -227,7 +227,7 @@ static bool uri_from_filename(const char *path, char *uri) return 0; } /* on windows, using always uppercase drive/volume letter in uri */ - vol[0] = (unsigned char)toupper(path[0]); + vol[0] = (uchar)toupper(path[0]); vol[1] = ':'; vol[2] = '\0'; strcat(orig_uri, vol); @@ -256,7 +256,7 @@ static bool thumbpathname_from_uri( if (r_name) { char hexdigest[33]; - unsigned char digest[16]; + uchar digest[16]; BLI_hash_md5_buffer(uri, strlen(uri), digest); hexdigest[0] = '\0'; BLI_snprintf(r_name, name_len, "%s.png", BLI_hash_md5_to_hexdigest(digest, hexdigest)); diff --git a/source/blender/imbuf/intern/thumbs_font.c b/source/blender/imbuf/intern/thumbs_font.c index c0a33f608a5..65848bfb55e 100644 --- a/source/blender/imbuf/intern/thumbs_font.c +++ b/source/blender/imbuf/intern/thumbs_font.c @@ -41,7 +41,7 @@ void IMB_thumb_ensure_translations(void) } } -struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned int y) +struct ImBuf *IMB_thumb_load_font(const char *filepath, uint x, uint y) { const int font_size = y / 4; @@ -66,7 +66,7 @@ struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned ARRAY_SIZE(thumb_str), font_color, font_size, - (unsigned char *)ibuf->rect, + (uchar *)ibuf->rect, ibuf->x, ibuf->y, ibuf->channels); @@ -83,7 +83,7 @@ bool IMB_thumb_load_font_get_hash(char *r_hash) int draw_str_lines = ARRAY_SIZE(thumb_str); int i; - unsigned char digest[16]; + uchar digest[16]; len += BLI_strncpy_rlen(str + len, THUMB_DEFAULT_HASH, sizeof(buf) - len); diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 1989566fc32..f4829386aac 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -60,7 +60,7 @@ static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size); /** Structure for in-memory TIFF file. */ typedef struct ImbTIFFMemFile { /** Location of first byte of TIFF file. */ - const unsigned char *mem; + const uchar *mem; /** Current offset within the file. */ toff_t offset; /** Size of the TIFF file. */ @@ -262,7 +262,7 @@ static toff_t imb_tiff_SizeProc(thandle_t handle) return (toff_t)(mfile->size); } -static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const unsigned char *mem, size_t size) +static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const uchar *mem, size_t size) { /* open the TIFF client layer interface to the in-memory file */ memFile->mem = mem; @@ -303,7 +303,7 @@ static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const unsigned char * * hence my manual comparison. - Jonathan Merritt (lancelet) 4th Sept 2005. */ #define IMB_TIFF_NCB 4 /* number of comparison bytes used */ -bool imb_is_a_tiff(const unsigned char *buf, size_t size) +bool imb_is_a_tiff(const uchar *buf, size_t size) { const char big_endian[IMB_TIFF_NCB] = {0x4d, 0x4d, 0x00, 0x2a}; const char lil_endian[IMB_TIFF_NCB] = {0x49, 0x49, 0x2a, 0x00}; @@ -315,10 +315,7 @@ bool imb_is_a_tiff(const unsigned char *buf, size_t size) (memcmp(lil_endian, buf, IMB_TIFF_NCB) == 0)); } -static void scanline_contig_16bit(float *rectf, - const unsigned short *sbuf, - int scanline_w, - int spp) +static void scanline_contig_16bit(float *rectf, const ushort *sbuf, int scanline_w, int spp) { int i; for (i = 0; i < scanline_w; i++) { @@ -340,10 +337,7 @@ static void scanline_contig_32bit(float *rectf, const float *fbuf, int scanline_ } } -static void scanline_separate_16bit(float *rectf, - const unsigned short *sbuf, - int scanline_w, - int chan) +static void scanline_separate_16bit(float *rectf, const ushort *sbuf, int scanline_w, int chan) { int i; for (i = 0; i < scanline_w; i++) { @@ -392,7 +386,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) size_t scanline; int ib_flag = 0, row, chan; float *fbuf = NULL; - unsigned short *sbuf = NULL; + ushort *sbuf = NULL; TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample); TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp); /* number of 'channels' */ @@ -410,7 +404,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) * So let's keep this thing here for until proper solution is found (sergey) */ - unsigned short extraSampleTypes[1]; + ushort extraSampleTypes[1]; extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA; TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes); } @@ -428,7 +422,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) } else if (bitspersample == 16) { ib_flag = IB_rectfloat; - sbuf = (unsigned short *)_TIFFmalloc(scanline); + sbuf = (ushort *)_TIFFmalloc(scanline); if (!sbuf) { goto cleanup; } @@ -539,10 +533,7 @@ void imb_inittiff(void) } } -ImBuf *imb_loadtiff(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_loadtiff(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { TIFF *image = NULL; ImBuf *ibuf = NULL, *hbuf; @@ -589,7 +580,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem, /* get alpha mode from file header */ if (flags & IB_alphamode_detect) { if (spp == 4) { - unsigned short extra, *extraSampleTypes; + ushort extra, *extraSampleTypes; const int found = TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extra, &extraSampleTypes); if (found && (extraSampleTypes[0] == EXTRASAMPLE_ASSOCALPHA)) { @@ -661,8 +652,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem, return ibuf; } -void imb_loadtiletiff( - ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect) +void imb_loadtiletiff(ImBuf *ibuf, const uchar *mem, size_t size, int tx, int ty, uint *rect) { TIFF *image = NULL; uint32_t width, height; @@ -723,9 +713,9 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) TIFF *image = NULL; uint16_t samplesperpixel, bitspersample; size_t npixels; - unsigned char *pixels = NULL; - unsigned char *from = NULL, *to = NULL; - unsigned short *pixels16 = NULL, *to16 = NULL; + uchar *pixels = NULL; + uchar *from = NULL, *to = NULL; + ushort *pixels16 = NULL, *to16 = NULL; float *fromf = NULL; float xres, yres; int x, y, from_i, to_i, i; @@ -786,10 +776,10 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) /* allocate array for pixel data */ npixels = ibuf->x * ibuf->y; if (bitspersample == 16) { - pixels16 = (unsigned short *)_TIFFmalloc(npixels * samplesperpixel * sizeof(unsigned short)); + pixels16 = (ushort *)_TIFFmalloc(npixels * samplesperpixel * sizeof(ushort)); } else { - pixels = (unsigned char *)_TIFFmalloc(npixels * samplesperpixel * sizeof(unsigned char)); + pixels = (uchar *)_TIFFmalloc(npixels * samplesperpixel * sizeof(uchar)); } if (pixels == NULL && pixels16 == NULL) { @@ -804,7 +794,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) to16 = pixels16; } else { - from = (unsigned char *)ibuf->rect; + from = (uchar *)ibuf->rect; to = pixels; } @@ -813,7 +803,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel); if (samplesperpixel == 4) { - unsigned short extraSampleTypes[1]; + ushort extraSampleTypes[1]; if (bitspersample == 16) { extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA; @@ -908,7 +898,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); if (TIFFWriteEncodedStrip(image, 0, - (bitspersample == 16) ? (unsigned char *)pixels16 : pixels, + (bitspersample == 16) ? (uchar *)pixels16 : pixels, (size_t)ibuf->x * ibuf->y * samplesperpixel * bitspersample / 8) == -1) { fprintf(stderr, "imb_savetiff: Could not write encoded TIFF.\n"); diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index d64a48569ae..276d31c0557 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -147,7 +147,7 @@ class NoDiscard : public BaseDiscard { template< /** * \brief Kind of buffer. - * Possible options: float, unsigned char. + * Possible options: float, uchar. */ typename StorageType = float, @@ -170,10 +170,9 @@ class PixelPointer { if constexpr (std::is_same_v<StorageType, float>) { pointer = image_buffer->rect_float + offset; } - else if constexpr (std::is_same_v<StorageType, unsigned char>) { - pointer = const_cast<unsigned char *>( - static_cast<const unsigned char *>(static_cast<const void *>(image_buffer->rect)) + - offset); + else if constexpr (std::is_same_v<StorageType, uchar>) { + pointer = const_cast<uchar *>( + static_cast<const uchar *>(static_cast<const void *>(image_buffer->rect)) + offset); } else { pointer = nullptr; @@ -264,7 +263,7 @@ template< /** \brief Interpolation mode to use when sampling. */ eIMBInterpolationFilterMode Filter, - /** \brief storage type of a single pixel channel (unsigned char or float). */ + /** \brief storage type of a single pixel channel (uchar or float). */ typename StorageType, /** * \brief number of channels if the image to read. @@ -294,14 +293,14 @@ class Sampler { const float wrapped_v = uv_wrapper.modify_v(source, v); bilinear_interpolation_color_fl(source, nullptr, r_sample.data(), wrapped_u, wrapped_v); } - else if constexpr (Filter == IMB_FILTER_NEAREST && - std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { + else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, uchar> && + NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); nearest_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v); } - else if constexpr (Filter == IMB_FILTER_BILINEAR && - std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { + else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, uchar> && + NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); bilinear_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v); @@ -374,7 +373,7 @@ class Sampler { * * Template class to convert and store a sample in a PixelPointer. * It supports: - * - 4 channel unsigned char -> 4 channel unsigned char. + * - 4 channel uchar -> 4 channel uchar. * - 4 channel float -> 4 channel float. * - 3 channel float -> 4 channel float. * - 2 channel float -> 4 channel float. @@ -392,7 +391,7 @@ class ChannelConverter { */ void convert_and_store(const SampleType &sample, PixelType &pixel_pointer) { - if constexpr (std::is_same_v<StorageType, unsigned char>) { + if constexpr (std::is_same_v<StorageType, uchar>) { BLI_STATIC_ASSERT(SourceNumChannels == 4, "Unsigned chars always have 4 channels."); BLI_STATIC_ASSERT(DestinationNumChannels == 4, "Unsigned chars always have 4 channels."); @@ -550,8 +549,8 @@ static void transform_threaded(TransformUserData *user_data, const eIMBTransform scanline_func = get_scanline_function<Filter>(user_data, mode); } else if (user_data->dst->rect && user_data->src->rect) { - /* Number of channels is always 4 when using unsigned char buffers (sRGB + straight alpha). */ - scanline_func = get_scanline_function<Filter, unsigned char, 4, 4>(mode); + /* Number of channels is always 4 when using uchar buffers (sRGB + straight alpha). */ + scanline_func = get_scanline_function<Filter, uchar, 4, 4>(mode); } if (scanline_func != nullptr) { diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index ffa989a29b4..2870ff56c0a 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -106,8 +106,7 @@ const char *imb_ext_audio[] = { /* Increased from 32 to 64 because of the bitmaps header size. */ #define HEADER_SIZE 64 -static ssize_t imb_ispic_read_header_from_filepath(const char *filepath, - unsigned char buf[HEADER_SIZE]) +static ssize_t imb_ispic_read_header_from_filepath(const char *filepath, uchar buf[HEADER_SIZE]) { BLI_stat_t st; int fp; @@ -135,7 +134,7 @@ static ssize_t imb_ispic_read_header_from_filepath(const char *filepath, return size; } -int IMB_ispic_type_from_memory(const unsigned char *buf, const size_t buf_size) +int IMB_ispic_type_from_memory(const uchar *buf, const size_t buf_size) { for (const ImFileType *type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) { if (type->is_a != NULL) { @@ -150,7 +149,7 @@ int IMB_ispic_type_from_memory(const unsigned char *buf, const size_t buf_size) int IMB_ispic_type(const char *filepath) { - unsigned char buf[HEADER_SIZE]; + uchar buf[HEADER_SIZE]; const ssize_t buf_size = imb_ispic_read_header_from_filepath(filepath, buf); if (buf_size <= 0) { return IMB_FTYPE_NONE; @@ -160,7 +159,7 @@ int IMB_ispic_type(const char *filepath) bool IMB_ispic_type_matches(const char *filepath, int filetype) { - unsigned char buf[HEADER_SIZE]; + uchar buf[HEADER_SIZE]; const ssize_t buf_size = imb_ispic_read_header_from_filepath(filepath, buf); if (buf_size <= 0) { return false; @@ -251,7 +250,7 @@ const char *IMB_ffmpeg_last_error(void) static int isffmpeg(const char *filepath) { AVFormatContext *pFormatCtx = NULL; - unsigned int i; + uint i; int videoStream; const AVCodec *pCodec; diff --git a/source/blender/imbuf/intern/webp.c b/source/blender/imbuf/intern/webp.c index 19fe2373ea0..27c26fb19c1 100644 --- a/source/blender/imbuf/intern/webp.c +++ b/source/blender/imbuf/intern/webp.c @@ -4,14 +4,23 @@ * \ingroup imbuf */ +#ifdef _WIN32 +# include <io.h> +#else +# include <unistd.h> +#endif + +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <webp/decode.h> #include <webp/encode.h> #include "BLI_fileops.h" +#include "BLI_mmap.h" #include "BLI_utildefines.h" +#include "IMB_allocimbuf.h" #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" #include "IMB_filetype.h" @@ -20,7 +29,7 @@ #include "MEM_guardedalloc.h" -bool imb_is_a_webp(const unsigned char *buf, size_t size) +bool imb_is_a_webp(const uchar *buf, size_t size) { if (WebPGetInfo(buf, size, NULL, NULL)) { return true; @@ -28,10 +37,7 @@ bool imb_is_a_webp(const unsigned char *buf, size_t size) return false; } -ImBuf *imb_loadwebp(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_loadwebp(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { if (!imb_is_a_webp(mem, size)) { return NULL; @@ -57,7 +63,7 @@ ImBuf *imb_loadwebp(const unsigned char *mem, 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); + uchar *last_row = (uchar *)(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"); @@ -67,10 +73,93 @@ ImBuf *imb_loadwebp(const unsigned char *mem, return ibuf; } +struct ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath, + const int UNUSED(flags), + const size_t max_thumb_size, + char colorspace[], + size_t *r_width, + size_t *r_height) +{ + const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0); + if (file == -1) { + return NULL; + } + + const size_t data_size = BLI_file_descriptor_size(file); + + imb_mmap_lock(); + BLI_mmap_file *mmap_file = BLI_mmap_open(file); + imb_mmap_unlock(); + close(file); + if (mmap_file == NULL) { + return NULL; + } + + const uchar *data = BLI_mmap_get_pointer(mmap_file); + + WebPDecoderConfig config; + if (!data || !WebPInitDecoderConfig(&config) || + WebPGetFeatures(data, data_size, &config.input) != VP8_STATUS_OK) { + fprintf(stderr, "WebP: Invalid file\n"); + imb_mmap_lock(); + BLI_mmap_free(mmap_file); + imb_mmap_unlock(); + return NULL; + } + + /* Return full size of the image. */ + *r_width = (size_t)config.input.width; + *r_height = (size_t)config.input.height; + + const float scale = (float)max_thumb_size / MAX2(config.input.width, config.input.height); + const int dest_w = (int)(config.input.width * scale); + const int dest_h = (int)(config.input.height * scale); + + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rect); + if (ibuf == NULL) { + fprintf(stderr, "WebP: Failed to allocate image memory\n"); + imb_mmap_lock(); + BLI_mmap_free(mmap_file); + imb_mmap_unlock(); + return NULL; + } + + config.options.no_fancy_upsampling = 1; + config.options.use_scaling = 1; + config.options.scaled_width = dest_w; + config.options.scaled_height = dest_h; + config.options.bypass_filtering = 1; + config.options.use_threads = 0; + config.options.flip = 1; + config.output.is_external_memory = 1; + config.output.colorspace = MODE_RGBA; + config.output.u.RGBA.rgba = (uint8_t *)ibuf->rect; + config.output.u.RGBA.stride = 4 * ibuf->x; + config.output.u.RGBA.size = (size_t)(config.output.u.RGBA.stride * ibuf->y); + + if (WebPDecode(data, data_size, &config) != VP8_STATUS_OK) { + fprintf(stderr, "WebP: Failed to decode image\n"); + imb_mmap_lock(); + BLI_mmap_free(mmap_file); + imb_mmap_unlock(); + return NULL; + } + + /* Free the output buffer. */ + WebPFreeDecBuffer(&config.output); + + imb_mmap_lock(); + BLI_mmap_free(mmap_file); + imb_mmap_unlock(); + + 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; + uchar *encoded_data, *last_row; size_t encoded_data_size; if (bytesperpixel == 3) { @@ -84,7 +173,7 @@ bool imb_savewebp(struct ImBuf *ibuf, const char *name, int UNUSED(flags)) rgb_rect[i * 3 + 2] = rgba_rect[i * 4 + 2]; } - last_row = (unsigned char *)(rgb_rect + (ibuf->y - 1) * ibuf->x * 3); + last_row = (uchar *)(rgb_rect + (ibuf->y - 1) * ibuf->x * 3); if (ibuf->foptions.quality == 100.0f) { encoded_data_size = WebPEncodeLosslessRGB( @@ -97,7 +186,7 @@ bool imb_savewebp(struct ImBuf *ibuf, const char *name, int UNUSED(flags)) MEM_freeN(rgb_rect); } else if (bytesperpixel == 4) { - last_row = (unsigned char *)(ibuf->rect + (ibuf->y - 1) * ibuf->x); + last_row = (uchar *)(ibuf->rect + (ibuf->y - 1) * ibuf->x); if (ibuf->foptions.quality == 100.0f) { encoded_data_size = WebPEncodeLosslessRGBA( diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 52c11d32933..7d38cd1ec88 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -391,7 +391,7 @@ void ABCGenericMeshWriter::get_geo_groups(Object *object, struct Mesh *mesh, std::map<std::string, std::vector<int32_t>> &geo_groups) { - const bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh); + const bke::AttributeAccessor attributes = mesh->attributes(); const VArraySpan<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index 64f1087a5de..5494bfaa6e8 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -57,7 +57,7 @@ static void get_uvs(const CDStreamConfig &config, } const int num_poly = config.totpoly; - MPoly *polygons = config.mpoly; + MPoly *mpoly = config.mpoly; MLoop *mloop = config.mloop; if (!config.pack_uvs) { @@ -67,7 +67,7 @@ static void get_uvs(const CDStreamConfig &config, /* Iterate in reverse order to match exported polygons. */ for (int i = 0; i < num_poly; i++) { - MPoly ¤t_poly = polygons[i]; + MPoly ¤t_poly = mpoly[i]; const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; for (int j = 0; j < current_poly.totloop; j++, count++) { @@ -85,7 +85,7 @@ static void get_uvs(const CDStreamConfig &config, int idx_count = 0; for (int i = 0; i < num_poly; i++) { - MPoly ¤t_poly = polygons[i]; + MPoly ¤t_poly = mpoly[i]; MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop; const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index c80d580eb32..c07aaa37988 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -135,8 +135,6 @@ static void read_mverts_interp(MVert *mverts, interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), static_cast<float>(weight)); copy_zup_from_yup(mvert.co, tmp); - - mvert.bweight = 0; } } @@ -163,8 +161,6 @@ void read_mverts(Mesh &mesh, const P3fArraySamplePtr positions, const N3fArraySa Imath::V3f pos_in = (*positions)[i]; copy_zup_from_yup(mvert.co, pos_in.getValue()); - - mvert.bweight = 0; } if (normals) { float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(&mesh); @@ -310,7 +306,7 @@ static void process_vertex_normals(CDStreamConfig &config, } config.mesh->flag |= ME_AUTOSMOOTH; - BKE_mesh_set_custom_normals_from_vertices(config.mesh, vnors); + BKE_mesh_set_custom_normals_from_verts(config.mesh, vnors); MEM_freeN(vnors); } @@ -619,11 +615,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr); if (read_mesh != mesh) { - /* XXX FIXME: after 2.80; mesh->flag isn't copied by #BKE_mesh_nomain_to_mesh(). */ - /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ - uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); - mesh->flag |= autosmooth; + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object); } if (m_settings->validate_meshes) { @@ -769,7 +761,7 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, size_t num_polys = new_mesh->totpoly; if (num_polys > 0) { std::map<std::string, int> mat_map; - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*new_mesh); + bke::MutableAttributeAccessor attributes = new_mesh->attributes_for_write(); bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map); @@ -830,7 +822,7 @@ void AbcMeshReader::assign_facesets_to_material_indices(const ISampleSelector &s void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel) { std::map<std::string, int> mat_map; - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map); @@ -1003,7 +995,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr); if (read_mesh != mesh) { - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object); } ISubDSchema::Sample sample; diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index ff189bc92dc..54ae71ad7a6 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -69,7 +69,7 @@ void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSel Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, nullptr); if (read_mesh != mesh) { - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object); } if (m_settings->validate_meshes) { diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 655b1158380..11c26fd2f72 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -601,7 +601,7 @@ static void import_endjob(void *user_data) else { Base *base; LayerCollection *lc; - Scene *scene = data->scene; + const Scene *scene = data->scene; ViewLayer *view_layer = data->view_layer; BKE_view_layer_base_deselect_all(scene, view_layer); diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index 3728bbd34c3..e60900ccdb6 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -288,7 +288,7 @@ static bool collect_vertex_counts_per_poly(Mesh *me, std::vector<unsigned long> &vcount_list) { const Span<MPoly> polys = me->polys(); - const blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*me); + const blender::bke::AttributeAccessor attributes = me->attributes(); const blender::VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); bool is_triangulated = true; @@ -399,7 +399,7 @@ void GeometryExporter::create_mesh_primitive_list(short material_index, /* performs the actual writing */ prepareToAppendValues(is_triangulated, *primitive_list, vcount_list); - const blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*me); + const blender::bke::AttributeAccessor attributes = me->attributes(); const blender::VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp index e7a4f7b6b51..b22346d0281 100644 --- a/source/blender/io/collada/MeshImporter.cpp +++ b/source/blender/io/collada/MeshImporter.cpp @@ -587,7 +587,6 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me) unsigned int *indices = mp->getPositionIndices().getData(); for (int j = 0; j < edge_count; j++, med++) { - med->bweight = 0; med->crease = 0; med->flag |= ME_LOOSEEDGE; med->v1 = indices[2 * j]; diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp index 5af0f360a44..b98ff27c89e 100644 --- a/source/blender/io/collada/SceneExporter.cpp +++ b/source/blender/io/collada/SceneExporter.cpp @@ -82,7 +82,7 @@ void SceneExporter::writeNodeList(std::vector<Object *> &child_objects, Object * void SceneExporter::writeNode(Object *ob) { - Scene *scene = blender_context.get_scene(); + const Scene *scene = blender_context.get_scene(); ViewLayer *view_layer = blender_context.get_view_layer(); std::vector<Object *> child_objects; diff --git a/source/blender/io/collada/collada.cpp b/source/blender/io/collada/collada.cpp index 8695fc81770..342b856f313 100644 --- a/source/blender/io/collada/collada.cpp +++ b/source/blender/io/collada/collada.cpp @@ -57,7 +57,7 @@ int collada_import(bContext *C, ImportSettings *import_settings) int collada_export(bContext *C, ExportSettings *export_settings) { BlenderContext blender_context(C); - Scene *scene = blender_context.get_scene(); + const Scene *scene = blender_context.get_scene(); ViewLayer *view_layer = blender_context.get_view_layer(); int includeFilter = OB_REL_NONE; diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index af07e25d857..54ad7ef5410 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -310,7 +310,7 @@ static void import_endjob(void *customdata) else if (data->archive) { Base *base; LayerCollection *lc; - Scene *scene = data->scene; + const Scene *scene = data->scene; ViewLayer *view_layer = data->view_layer; BKE_view_layer_base_deselect_all(scene, view_layer); diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 259b52ed435..7cb4c65f166 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -248,11 +248,7 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) is_initial_load_ = false; if (read_mesh != mesh) { - /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ - /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ - uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); - BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true); - mesh->flag |= autosmooth; + BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_); } readFaceSetsSample(bmain, mesh, motionSampleTime); @@ -807,7 +803,7 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot std::map<pxr::SdfPath, int> mat_map; - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); this->assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map); @@ -916,7 +912,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, MutableSpan<MPoly> polys = active_mesh->polys_for_write(); if (!polys.is_empty() && import_params_.import_materials) { std::map<pxr::SdfPath, int> mat_map; - bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*active_mesh); + bke::MutableAttributeAccessor attributes = active_mesh->attributes_for_write(); bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map); diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 9f8e38c4dfd..a39f74c6420 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -256,7 +256,7 @@ static void get_loops_polys(const Mesh *mesh, USDMeshData &usd_mesh_data) { /* Only construct face groups (a.k.a. geometry subsets) when we need them for material * assignments. */ - const bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh); + const bke::AttributeAccessor attributes = mesh->attributes(); const VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); if (!material_indices.is_single() && mesh->totcol > 1) { diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index a07315d8b4e..3494d8ffdc3 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -52,7 +52,7 @@ struct USDImportParams { bool import_materials; bool import_meshes; bool import_volumes; - char *prim_path_mask; + char prim_path_mask[1024]; bool import_subdiv; bool import_instance_proxies; bool create_collection; diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 847b02d3fd1..0a92bbca477 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -16,8 +16,6 @@ extern "C" { #endif -static const int TOTAL_AXES = 3; - struct OBJExportParams { /** Full path to the destination .OBJ file. */ char filepath[FILE_MAX]; @@ -50,18 +48,15 @@ struct OBJExportParams { bool export_triangulated_mesh; bool export_curves_as_nurbs; ePathReferenceMode path_mode; + bool export_pbr_extensions; /* Grouping options. */ bool export_object_groups; bool export_material_groups; bool export_vertex_groups; - /** - * Calculate smooth groups from sharp edges. - */ + /* Calculate smooth groups from sharp edges. */ bool export_smooth_groups; - /** - * Create bitflags instead of the default "0"/"1" group IDs. - */ + /* Create bitflags instead of the default "0"/"1" group IDs. */ bool smooth_groups_bitflags; }; 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 902f801ee5b..f2547e6fc14 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 @@ -255,7 +255,7 @@ void OBJWriter::write_vertex_coords(FormatHandler &fh, colors_layer = BKE_id_attributes_active_color_get(&mesh->id); } if (write_colors && (colors_layer != nullptr)) { - const bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh); + const bke::AttributeAccessor attributes = mesh->attributes(); const VArray<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>( colors_layer->name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f, 0.0f}); @@ -374,7 +374,7 @@ void OBJWriter::write_poly_elements(FormatHandler &fh, } } - const bke::AttributeAccessor attributes = bke::mesh_attributes(*obj_mesh_data.get_mesh()); + const bke::AttributeAccessor attributes = obj_mesh_data.get_mesh()->attributes(); const VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); @@ -493,11 +493,14 @@ void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_d static const char *tex_map_type_to_string[] = { "map_Kd", + "map_Pm", "map_Ks", "map_Ns", - "map_d", + "map_Pr", + "map_Ps", "map_refl", "map_Ke", + "map_d", "map_Bump", }; BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_string) == (int)MTLTexMapType::Count, @@ -553,29 +556,64 @@ StringRefNull MTLWriter::mtl_file_path() const return mtl_filepath_; } -void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl) +void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl, bool write_pbr) { /* For various material properties, we only capture information * coming from the texture, or the default value of the socket. * When the texture is present, do not emit the default value. */ - if (!mtl.tex_map_of_type(MTLTexMapType::Ns).is_valid()) { - fmt_handler_.write_mtl_float("Ns", mtl.Ns); + + /* Do not write Ns & Ka when writing in PBR mode. */ + if (!write_pbr) { + if (!mtl.tex_map_of_type(MTLTexMapType::SpecularExponent).is_valid()) { + fmt_handler_.write_mtl_float("Ns", mtl.spec_exponent); + } + fmt_handler_.write_mtl_float3( + "Ka", mtl.ambient_color.x, mtl.ambient_color.y, mtl.ambient_color.z); + } + if (!mtl.tex_map_of_type(MTLTexMapType::Color).is_valid()) { + fmt_handler_.write_mtl_float3("Kd", mtl.color.x, mtl.color.y, mtl.color.z); } - fmt_handler_.write_mtl_float3("Ka", mtl.Ka.x, mtl.Ka.y, mtl.Ka.z); - if (!mtl.tex_map_of_type(MTLTexMapType::Kd).is_valid()) { - fmt_handler_.write_mtl_float3("Kd", mtl.Kd.x, mtl.Kd.y, mtl.Kd.z); + if (!mtl.tex_map_of_type(MTLTexMapType::Specular).is_valid()) { + fmt_handler_.write_mtl_float3("Ks", mtl.spec_color.x, mtl.spec_color.y, mtl.spec_color.z); } - if (!mtl.tex_map_of_type(MTLTexMapType::Ks).is_valid()) { - fmt_handler_.write_mtl_float3("Ks", mtl.Ks.x, mtl.Ks.y, mtl.Ks.z); + if (!mtl.tex_map_of_type(MTLTexMapType::Emission).is_valid()) { + fmt_handler_.write_mtl_float3( + "Ke", mtl.emission_color.x, mtl.emission_color.y, mtl.emission_color.z); } - if (!mtl.tex_map_of_type(MTLTexMapType::Ke).is_valid()) { - fmt_handler_.write_mtl_float3("Ke", mtl.Ke.x, mtl.Ke.y, mtl.Ke.z); + fmt_handler_.write_mtl_float("Ni", mtl.ior); + if (!mtl.tex_map_of_type(MTLTexMapType::Alpha).is_valid()) { + fmt_handler_.write_mtl_float("d", mtl.alpha); } - fmt_handler_.write_mtl_float("Ni", mtl.Ni); - if (!mtl.tex_map_of_type(MTLTexMapType::d).is_valid()) { - fmt_handler_.write_mtl_float("d", mtl.d); + fmt_handler_.write_mtl_illum(mtl.illum_mode); + + if (write_pbr) { + if (!mtl.tex_map_of_type(MTLTexMapType::Roughness).is_valid() && mtl.roughness >= 0.0f) { + fmt_handler_.write_mtl_float("Pr", mtl.roughness); + } + if (!mtl.tex_map_of_type(MTLTexMapType::Metallic).is_valid() && mtl.metallic >= 0.0f) { + fmt_handler_.write_mtl_float("Pm", mtl.metallic); + } + if (!mtl.tex_map_of_type(MTLTexMapType::Sheen).is_valid() && mtl.sheen >= 0.0f) { + fmt_handler_.write_mtl_float("Ps", mtl.sheen); + } + if (mtl.cc_thickness >= 0.0f) { + fmt_handler_.write_mtl_float("Pc", mtl.cc_thickness); + } + if (mtl.cc_roughness >= 0.0f) { + fmt_handler_.write_mtl_float("Pcr", mtl.cc_roughness); + } + if (mtl.aniso >= 0.0f) { + fmt_handler_.write_mtl_float("aniso", mtl.aniso); + } + if (mtl.aniso_rot >= 0.0f) { + fmt_handler_.write_mtl_float("anisor", mtl.aniso_rot); + } + if (mtl.transmit_color.x > 0.0f || mtl.transmit_color.y > 0.0f || + mtl.transmit_color.z > 0.0f) { + fmt_handler_.write_mtl_float3( + "Tf", mtl.transmit_color.x, mtl.transmit_color.y, mtl.transmit_color.z); + } } - fmt_handler_.write_mtl_illum(mtl.illum); } void MTLWriter::write_texture_map(const MTLMaterial &mtl_material, @@ -594,8 +632,8 @@ void MTLWriter::write_texture_map(const MTLMaterial &mtl_material, if (texture_map.scale != float3{1.0f, 1.0f, 1.0f}) { options.append(" -s ").append(float3_to_string(texture_map.scale)); } - if (texture_key == MTLTexMapType::bump && mtl_material.map_Bump_strength > 0.0001f) { - options.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength)); + if (texture_key == MTLTexMapType::Normal && mtl_material.normal_strength > 0.0001f) { + options.append(" -bm ").append(std::to_string(mtl_material.normal_strength)); } std::string path = path_reference( @@ -606,9 +644,21 @@ void MTLWriter::write_texture_map(const MTLMaterial &mtl_material, fmt_handler_.write_mtl_map(tex_map_type_to_string[(int)texture_key], options, path); } +static bool is_pbr_map(MTLTexMapType type) +{ + return type == MTLTexMapType::Metallic || type == MTLTexMapType::Roughness || + type == MTLTexMapType::Sheen; +} + +static bool is_non_pbr_map(MTLTexMapType type) +{ + return type == MTLTexMapType::SpecularExponent || type == MTLTexMapType::Reflection; +} + void MTLWriter::write_materials(const char *blen_filepath, ePathReferenceMode path_mode, - const char *dest_dir) + const char *dest_dir, + bool write_pbr) { if (mtlmaterials_.size() == 0) { return; @@ -626,12 +676,18 @@ void MTLWriter::write_materials(const char *blen_filepath, for (const MTLMaterial &mtlmat : mtlmaterials_) { fmt_handler_.write_string(""); fmt_handler_.write_mtl_newmtl(mtlmat.name); - write_bsdf_properties(mtlmat); + write_bsdf_properties(mtlmat, write_pbr); for (int key = 0; key < (int)MTLTexMapType::Count; key++) { const MTLTexMap &tex = mtlmat.texture_maps[key]; if (!tex.is_valid()) { continue; } + if (!write_pbr && is_pbr_map((MTLTexMapType)key)) { + continue; + } + if (write_pbr && is_non_pbr_map((MTLTexMapType)key)) { + continue; + } write_texture_map( mtlmat, (MTLTexMapType)key, tex, blen_filedir, dest_dir, path_mode, copy_set); } 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 4544037fbc1..eda4576297b 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 @@ -186,7 +186,8 @@ class MTLWriter : NonMovable, NonCopyable { */ void write_materials(const char *blen_filepath, ePathReferenceMode path_mode, - const char *dest_dir); + const char *dest_dir, + bool write_pbr); StringRefNull mtl_file_path() const; /** * Add the materials of the given object to #MTLWriter, de-duplicating @@ -203,7 +204,7 @@ class MTLWriter : NonMovable, NonCopyable { /** * Write properties sourced from p-BSDF node or #Object.Material. */ - void write_bsdf_properties(const MTLMaterial &mtl_material); + void write_bsdf_properties(const MTLMaterial &mtl_material, bool write_pbr); /** * Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image". */ 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 cd724152b05..10880b016fb 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -203,7 +203,7 @@ void OBJMesh::calc_smooth_groups(const bool use_bitflags) void OBJMesh::calc_poly_order() { - const bke::AttributeAccessor attributes = bke::mesh_attributes(*export_mesh_eval_); + const bke::AttributeAccessor attributes = export_mesh_eval_->attributes(); const VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); if (material_indices.is_single() && material_indices.get_internal_single() == 0) { @@ -242,8 +242,8 @@ const Material *OBJMesh::get_object_material(const int16_t mat_nr) const bool OBJMesh::is_ith_poly_smooth(const int poly_index) const { - const Span<MPoly> polygons = export_mesh_eval_->polys(); - return polygons[poly_index].flag & ME_SMOOTH; + const Span<MPoly> polys = export_mesh_eval_->polys(); + return polys[poly_index].flag & ME_SMOOTH; } const char *OBJMesh::get_object_name() const @@ -317,7 +317,7 @@ void OBJMesh::store_uv_coords_and_indices() if (uv_vert->separate) { tot_uv_vertices_ += 1; } - const int vertices_in_poly = polys[uv_vert->poly_index].totloop; + const int verts_in_poly = polys[uv_vert->poly_index].totloop; /* Store UV vertex coordinates. */ uv_coords_.resize(tot_uv_vertices_); @@ -326,7 +326,7 @@ void OBJMesh::store_uv_coords_and_indices() uv_coords_[tot_uv_vertices_ - 1] = float2(vert_uv_coords[0], vert_uv_coords[1]); /* Store UV vertex indices. */ - uv_indices_[uv_vert->poly_index].resize(vertices_in_poly); + uv_indices_[uv_vert->poly_index].resize(verts_in_poly); /* Keep indices zero-based and let the writer handle the "+ 1" as per OBJ spec. */ uv_indices_[uv_vert->poly_index][uv_vert->loop_of_poly_index] = tot_uv_vertices_ - 1; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc index 6a02695c304..f8c7da75a70 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc @@ -23,11 +23,14 @@ namespace blender::io::obj { const char *tex_map_type_to_socket_id[] = { "Base Color", + "Metallic", "Specular", + "Roughness", /* Map specular exponent to roughness. */ "Roughness", - "Alpha", - "Metallic", + "Sheen", + "Metallic", /* Map reflection to metallic. */ "Emission", + "Alpha", "Normal", }; BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_socket_id) == (int)MTLTexMapType::Count, @@ -188,7 +191,6 @@ static void store_bsdf_properties(const bNode *bsdf_node, const Material *material, MTLMaterial &r_mtl_mat) { - /* If p-BSDF is not present, fallback to #Object.Material. */ float roughness = material->roughness; if (bsdf_node) { copy_property_from_node(SOCK_FLOAT, bsdf_node, "Roughness", {&roughness, 1}); @@ -212,11 +214,11 @@ static void store_bsdf_properties(const bNode *bsdf_node, copy_property_from_node(SOCK_FLOAT, bsdf_node, "IOR", {&refraction_index, 1}); } - float dissolved = material->a; + float alpha = material->a; if (bsdf_node) { - copy_property_from_node(SOCK_FLOAT, bsdf_node, "Alpha", {&dissolved, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Alpha", {&alpha, 1}); } - const bool transparent = dissolved != 1.0f; + const bool transparent = alpha != 1.0f; float3 diffuse_col = {material->r, material->g, material->b}; if (bsdf_node) { @@ -231,6 +233,22 @@ static void store_bsdf_properties(const bNode *bsdf_node, } mul_v3_fl(emission_col, emission_strength); + float sheen = -1.0f; + float clearcoat = -1.0f; + float clearcoat_roughness = -1.0f; + float aniso = -1.0f; + float aniso_rot = -1.0f; + float transmission = -1.0f; + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Sheen", {&sheen, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Clearcoat", {&clearcoat, 1}); + copy_property_from_node( + SOCK_FLOAT, bsdf_node, "Clearcoat Roughness", {&clearcoat_roughness, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic", {&aniso, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic Rotation", {&aniso_rot, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Transmission", {&transmission, 1}); + } + /* See https://wikipedia.org/wiki/Wavefront_.obj_file for all possible values of `illum`. */ /* Highlight on. */ int illum = 2; @@ -253,19 +271,27 @@ static void store_bsdf_properties(const bNode *bsdf_node, /* Transparency: Glass on, Reflection: Ray trace off */ illum = 9; } - r_mtl_mat.Ns = spec_exponent; + r_mtl_mat.spec_exponent = spec_exponent; if (metallic != 0.0f) { - r_mtl_mat.Ka = {metallic, metallic, metallic}; + r_mtl_mat.ambient_color = {metallic, metallic, metallic}; } else { - r_mtl_mat.Ka = {1.0f, 1.0f, 1.0f}; + r_mtl_mat.ambient_color = {1.0f, 1.0f, 1.0f}; } - r_mtl_mat.Kd = diffuse_col; - r_mtl_mat.Ks = {specular, specular, specular}; - r_mtl_mat.Ke = emission_col; - r_mtl_mat.Ni = refraction_index; - r_mtl_mat.d = dissolved; - r_mtl_mat.illum = illum; + r_mtl_mat.color = diffuse_col; + r_mtl_mat.spec_color = {specular, specular, specular}; + r_mtl_mat.emission_color = emission_col; + r_mtl_mat.ior = refraction_index; + r_mtl_mat.alpha = alpha; + r_mtl_mat.illum_mode = illum; + r_mtl_mat.roughness = roughness; + r_mtl_mat.metallic = metallic; + r_mtl_mat.sheen = sheen; + r_mtl_mat.cc_thickness = clearcoat; + r_mtl_mat.cc_roughness = clearcoat_roughness; + r_mtl_mat.aniso = aniso; + r_mtl_mat.aniso_rot = aniso_rot; + r_mtl_mat.transmit_color = {transmission, transmission, transmission}; } /** @@ -291,7 +317,7 @@ static void store_image_textures(const bNode *bsdf_node, Vector<const bNodeSocket *> linked_sockets; const bNode *normal_map_node{nullptr}; - if (key == (int)MTLTexMapType::bump) { + if (key == (int)MTLTexMapType::Normal) { /* Find sockets linked to destination "Normal" socket in P-BSDF node. */ linked_sockets_to_dest_id(bsdf_node, *node_tree, "Normal", linked_sockets); /* Among the linked sockets, find Normal Map shader node. */ @@ -302,7 +328,7 @@ static void store_image_textures(const bNode *bsdf_node, } else { /* Skip emission map if emission strength is zero. */ - if (key == (int)MTLTexMapType::Ke) { + if (key == (int)MTLTexMapType::Emission) { float emission_strength = 0.0f; copy_property_from_node( SOCK_FLOAT, bsdf_node, "Emission Strength", {&emission_strength, 1}); @@ -331,7 +357,7 @@ static void store_image_textures(const bNode *bsdf_node, if (normal_map_node) { copy_property_from_node( - SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.map_Bump_strength, 1}); + SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.normal_strength, 1}); } /* Texture transform options. Only translation (origin offset, "-o") and scale * ("-o") are supported. */ diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh index d8eafff107b..9c1bc2f0f8f 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh @@ -13,7 +13,19 @@ struct Material; namespace blender::io::obj { -enum class MTLTexMapType { Kd = 0, Ks, Ns, d, refl, Ke, bump, Count }; +enum class MTLTexMapType { + Color = 0, + Metallic, + Specular, + SpecularExponent, + Roughness, + Sheen, + Reflection, + Emission, + Alpha, + Normal, + Count +}; extern const char *tex_map_type_to_socket_id[]; struct MTLTexMap { @@ -47,17 +59,26 @@ struct MTLMaterial { std::string name; /* Always check for negative values while importing or exporting. Use defaults if * any value is negative. */ - float Ns{-1.0f}; - float3 Ka{-1.0f}; - float3 Kd{-1.0f}; - float3 Ks{-1.0f}; - float3 Ke{-1.0f}; - float Ni{-1.0f}; - float d{-1.0f}; - int illum{-1}; + float spec_exponent{-1.0f}; /* `Ns` */ + float3 ambient_color{-1.0f}; /* `Ka` */ + float3 color{-1.0f}; /* `Kd` */ + float3 spec_color{-1.0f}; /* `Ks` */ + float3 emission_color{-1.0f}; /* `Ke` */ + float ior{-1.0f}; /* `Ni` */ + float alpha{-1.0f}; /* `d` */ + float3 transmit_color{-1.0f}; /* `Kt` / `Tf` */ + float roughness{-1.0f}; /* `Pr` */ + float metallic{-1.0f}; /* `Pm` */ + float sheen{-1.0f}; /* `Ps` */ + float cc_thickness{-1.0f}; /* `Pc` */ + float cc_roughness{-1.0f}; /* `Pcr` */ + float aniso{-1.0f}; /* `aniso` */ + float aniso_rot{-1.0f}; /* `anisor` */ + + int illum_mode{-1}; MTLTexMap texture_maps[(int)MTLTexMapType::Count]; - /** Only used for Normal Map node: "map_Bump". */ - float map_Bump_strength{-1.0f}; + /* Only used for Normal Map node: `map_Bump`. */ + float normal_strength{-1.0f}; }; MTLMaterial mtlmaterial_for_material(const Material *material); diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 294ea81fd58..a51c017f81d 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -293,7 +293,10 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co } BLI_path_slash_native(dest_dir); BLI_path_normalize(nullptr, dest_dir); - mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir); + mtl_writer->write_materials(export_params.blen_filepath, + export_params.path_mode, + dest_dir, + export_params.export_pbr_extensions); } write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer); } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index 2ad8a09bd90..f92f9894f75 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -596,26 +596,35 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, static MTLTexMapType mtl_line_start_to_texture_type(const char *&p, const char *end) { if (parse_keyword(p, end, "map_Kd")) { - return MTLTexMapType::Kd; + return MTLTexMapType::Color; } if (parse_keyword(p, end, "map_Ks")) { - return MTLTexMapType::Ks; + return MTLTexMapType::Specular; } if (parse_keyword(p, end, "map_Ns")) { - return MTLTexMapType::Ns; + return MTLTexMapType::SpecularExponent; } if (parse_keyword(p, end, "map_d")) { - return MTLTexMapType::d; + return MTLTexMapType::Alpha; } if (parse_keyword(p, end, "refl") || parse_keyword(p, end, "map_refl")) { - return MTLTexMapType::refl; + return MTLTexMapType::Reflection; } if (parse_keyword(p, end, "map_Ke")) { - return MTLTexMapType::Ke; + return MTLTexMapType::Emission; } if (parse_keyword(p, end, "bump") || parse_keyword(p, end, "map_Bump") || parse_keyword(p, end, "map_bump")) { - return MTLTexMapType::bump; + return MTLTexMapType::Normal; + } + if (parse_keyword(p, end, "map_Pr")) { + return MTLTexMapType::Roughness; + } + if (parse_keyword(p, end, "map_Pm")) { + return MTLTexMapType::Metallic; + } + if (parse_keyword(p, end, "map_Ps")) { + return MTLTexMapType::Sheen; } return MTLTexMapType::Count; } @@ -647,7 +656,7 @@ static bool parse_texture_option(const char *&p, return true; } if (parse_keyword(p, end, "-bm")) { - p = parse_float(p, end, 1.0f, material->map_Bump_strength, true, true); + p = parse_float(p, end, 1.0f, material->normal_strength, true, true); return true; } if (parse_keyword(p, end, "-type")) { @@ -780,31 +789,55 @@ void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mat } else if (material != nullptr) { if (parse_keyword(p, end, "Ns")) { - parse_float(p, end, 324.0f, material->Ns); + parse_float(p, end, 324.0f, material->spec_exponent); } else if (parse_keyword(p, end, "Ka")) { - parse_floats(p, end, 0.0f, material->Ka, 3); + parse_floats(p, end, 0.0f, material->ambient_color, 3); } else if (parse_keyword(p, end, "Kd")) { - parse_floats(p, end, 0.8f, material->Kd, 3); + parse_floats(p, end, 0.8f, material->color, 3); } else if (parse_keyword(p, end, "Ks")) { - parse_floats(p, end, 0.5f, material->Ks, 3); + parse_floats(p, end, 0.5f, material->spec_color, 3); } else if (parse_keyword(p, end, "Ke")) { - parse_floats(p, end, 0.0f, material->Ke, 3); + parse_floats(p, end, 0.0f, material->emission_color, 3); } else if (parse_keyword(p, end, "Ni")) { - parse_float(p, end, 1.45f, material->Ni); + parse_float(p, end, 1.45f, material->ior); } else if (parse_keyword(p, end, "d")) { - parse_float(p, end, 1.0f, material->d); + parse_float(p, end, 1.0f, material->alpha); } else if (parse_keyword(p, end, "illum")) { /* Some files incorrectly use a float (T60135). */ float val; parse_float(p, end, 1.0f, val); - material->illum = val; + material->illum_mode = val; + } + else if (parse_keyword(p, end, "Pr")) { + parse_float(p, end, 0.5f, material->roughness); + } + else if (parse_keyword(p, end, "Pm")) { + parse_float(p, end, 0.0f, material->metallic); + } + else if (parse_keyword(p, end, "Ps")) { + parse_float(p, end, 0.0f, material->sheen); + } + else if (parse_keyword(p, end, "Pc")) { + parse_float(p, end, 0.0f, material->cc_thickness); + } + else if (parse_keyword(p, end, "Pcr")) { + parse_float(p, end, 0.0f, material->cc_roughness); + } + else if (parse_keyword(p, end, "aniso")) { + parse_float(p, end, 0.0f, material->aniso); + } + else if (parse_keyword(p, end, "anisor")) { + parse_float(p, end, 0.0f, material->aniso_rot); + } + else if (parse_keyword(p, end, "Kt") || parse_keyword(p, end, "Tf")) { + parse_floats(p, end, 0.0f, material->transmit_color, 3); } else { parse_texture_map(p, end, material, mtl_dir_path_); diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 84f1c6dd6b0..ef05534928a 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -69,11 +69,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, } transform_object(obj, import_params); - /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ - const uint16_t autosmooth = (mesh->flag & ME_AUTOSMOOTH); - Mesh *dst = static_cast<Mesh *>(obj->data); - BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true); - dst->flag |= autosmooth; + BKE_mesh_nomain_to_mesh(mesh, static_cast<Mesh *>(obj->data), obj); /* NOTE: vertex groups have to be created after final mesh is assigned to the object. */ create_vertex_groups(obj); @@ -188,8 +184,8 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) MutableSpan<MPoly> polys = mesh->polys_for_write(); MutableSpan<MLoop> loops = mesh->loops_for_write(); bke::SpanAttributeWriter<int> material_indices = - bke::mesh_attributes_for_write(*mesh).lookup_or_add_for_write_only_span<int>( - "material_index", ATTR_DOMAIN_FACE); + mesh->attributes_for_write().lookup_or_add_for_write_only_span<int>("material_index", + ATTR_DOMAIN_FACE); const int64_t tot_face_elems{mesh->totpoly}; int tot_loop_idx = 0; diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index 0922a71979e..c471b2002de 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -178,7 +178,7 @@ static void link_sockets(bNodeTree *ntree, static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial &mtl_mat) { - const int illum = mtl_mat.illum; + const int illum = mtl_mat.illum_mode; bool do_highlight = false; bool do_tranparency = false; bool do_reflection = false; @@ -244,21 +244,23 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial /* Approximations for trying to map obj/mtl material model into * Principled BSDF: */ /* Specular: average of Ks components. */ - float specular = (mtl_mat.Ks[0] + mtl_mat.Ks[1] + mtl_mat.Ks[2]) / 3; + float specular = (mtl_mat.spec_color[0] + mtl_mat.spec_color[1] + mtl_mat.spec_color[2]) / 3; if (specular < 0.0f) { specular = do_highlight ? 1.0f : 0.0f; } /* Roughness: map 0..1000 range to 1..0 and apply non-linearity. */ float roughness; - if (mtl_mat.Ns < 0.0f) { + if (mtl_mat.spec_exponent < 0.0f) { roughness = do_highlight ? 0.0f : 1.0f; } else { - float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat.Ns)); + float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat.spec_exponent)); roughness = 1.0f - sqrt(clamped_ns / 1000.0f); } - /* Metallic: average of Ka components. */ - float metallic = (mtl_mat.Ka[0] + mtl_mat.Ka[1] + mtl_mat.Ka[2]) / 3; + /* Metallic: average of `Ka` components. */ + float metallic = (mtl_mat.ambient_color[0] + mtl_mat.ambient_color[1] + + mtl_mat.ambient_color[2]) / + 3; if (do_reflection) { if (metallic < 0.0f) { metallic = 1.0f; @@ -268,7 +270,7 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial metallic = 0.0f; } - float ior = mtl_mat.Ni; + float ior = mtl_mat.ior; if (ior < 0) { if (do_tranparency) { ior = 1.0f; @@ -277,12 +279,20 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial ior = 1.5f; } } - float alpha = mtl_mat.d; + float alpha = mtl_mat.alpha; if (do_tranparency && alpha < 0) { alpha = 1.0f; } - float3 base_color = {mtl_mat.Kd[0], mtl_mat.Kd[1], mtl_mat.Kd[2]}; + /* PBR values, when present, override the ones calculated above. */ + if (mtl_mat.roughness >= 0) { + roughness = mtl_mat.roughness; + } + if (mtl_mat.metallic >= 0) { + metallic = mtl_mat.metallic; + } + + float3 base_color = mtl_mat.color; if (base_color.x >= 0 && base_color.y >= 0 && base_color.z >= 0) { set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf); /* Viewport shading uses legacy r,g,b base color. */ @@ -291,11 +301,11 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial mat->b = base_color.z; } - float3 emission_color = {mtl_mat.Ke[0], mtl_mat.Ke[1], mtl_mat.Ke[2]}; + float3 emission_color = mtl_mat.emission_color; if (emission_color.x >= 0 && emission_color.y >= 0 && emission_color.z >= 0) { set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf); } - if (mtl_mat.tex_map_of_type(MTLTexMapType::Ke).is_valid()) { + if (mtl_mat.tex_map_of_type(MTLTexMapType::Emission).is_valid()) { set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf); } set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf); @@ -312,6 +322,30 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial if (do_tranparency || (alpha >= 0.0f && alpha < 1.0f)) { mat->blend_method = MA_BM_BLEND; } + + if (mtl_mat.sheen >= 0) { + set_property_of_socket(SOCK_FLOAT, "Sheen", {mtl_mat.sheen}, bsdf); + } + if (mtl_mat.cc_thickness >= 0) { + set_property_of_socket(SOCK_FLOAT, "Clearcoat", {mtl_mat.cc_thickness}, bsdf); + } + if (mtl_mat.cc_roughness >= 0) { + set_property_of_socket(SOCK_FLOAT, "Clearcoat Roughness", {mtl_mat.cc_roughness}, bsdf); + } + if (mtl_mat.aniso >= 0) { + set_property_of_socket(SOCK_FLOAT, "Anisotropic", {mtl_mat.aniso}, bsdf); + } + if (mtl_mat.aniso_rot >= 0) { + set_property_of_socket(SOCK_FLOAT, "Anisotropic Rotation", {mtl_mat.aniso_rot}, bsdf); + } + + /* Transmission: average of transmission color. */ + float transmission = (mtl_mat.transmit_color[0] + mtl_mat.transmit_color[1] + + mtl_mat.transmit_color[2]) / + 3; + if (transmission >= 0) { + set_property_of_socket(SOCK_FLOAT, "Transmission", {transmission}, bsdf); + } } static void add_image_textures(Main *bmain, @@ -341,9 +375,9 @@ static void add_image_textures(Main *bmain, /* Add normal map node if needed. */ bNode *normal_map = nullptr; - if (key == (int)MTLTexMapType::bump) { + if (key == (int)MTLTexMapType::Normal) { normal_map = add_node(ntree, SH_NODE_NORMAL_MAP, node_locx_normalmap, node_locy); - const float bump = std::max(0.0f, mtl_mat.map_Bump_strength); + const float bump = std::max(0.0f, mtl_mat.normal_strength); set_property_of_socket(SOCK_FLOAT, "Strength", {bump}, normal_map); } @@ -362,7 +396,7 @@ static void add_image_textures(Main *bmain, link_sockets(ntree, image_node, "Color", normal_map, "Color"); link_sockets(ntree, normal_map, "Normal", bsdf, "Normal"); } - else if (key == (int)MTLTexMapType::d) { + else if (key == (int)MTLTexMapType::Alpha) { link_sockets(ntree, image_node, "Alpha", bsdf, tex_map_type_to_socket_id[key]); mat->blend_method = MA_BM_BLEND; } 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 0fd711bdac6..dcba78ac99e 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -527,4 +527,27 @@ TEST_F(obj_exporter_regression_test, all_objects_mat_groups) _export.params); } +TEST_F(obj_exporter_regression_test, materials_without_pbr) +{ + OBJExportParamsDefault _export; + _export.params.export_normals = false; + _export.params.path_mode = PATH_REFERENCE_RELATIVE; + compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend", + "io_tests/obj/materials_without_pbr.obj", + "io_tests/obj/materials_without_pbr.mtl", + _export.params); +} + +TEST_F(obj_exporter_regression_test, materials_pbr) +{ + OBJExportParamsDefault _export; + _export.params.export_normals = false; + _export.params.path_mode = PATH_REFERENCE_RELATIVE; + _export.params.export_pbr_extensions = true; + compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend", + "io_tests/obj/materials_pbr.obj", + "io_tests/obj/materials_pbr.mtl", + _export.params); +} + } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 7d3b41ed527..006d86312b6 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -31,6 +31,7 @@ struct OBJExportParamsDefault { params.path_mode = PATH_REFERENCE_AUTO; params.export_triangulated_mesh = false; params.export_curves_as_nurbs = false; + params.export_pbr_extensions = false; params.export_object_groups = false; params.export_material_groups = false; diff --git a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc index 5691aa5bea1..e473d629673 100644 --- a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc @@ -50,15 +50,23 @@ class obj_mtl_parser_test : public testing::Test { } const MTLMaterial &got = *materials.lookup(exp.name); const float tol = 0.0001f; - EXPECT_V3_NEAR(exp.Ka, got.Ka, tol); - EXPECT_V3_NEAR(exp.Kd, got.Kd, tol); - EXPECT_V3_NEAR(exp.Ks, got.Ks, tol); - EXPECT_V3_NEAR(exp.Ke, got.Ke, tol); - EXPECT_NEAR(exp.Ns, got.Ns, tol); - EXPECT_NEAR(exp.Ni, got.Ni, tol); - EXPECT_NEAR(exp.d, got.d, tol); - EXPECT_NEAR(exp.map_Bump_strength, got.map_Bump_strength, tol); - EXPECT_EQ(exp.illum, got.illum); + EXPECT_V3_NEAR(exp.ambient_color, got.ambient_color, tol); + EXPECT_V3_NEAR(exp.color, got.color, tol); + EXPECT_V3_NEAR(exp.spec_color, got.spec_color, tol); + EXPECT_V3_NEAR(exp.emission_color, got.emission_color, tol); + EXPECT_V3_NEAR(exp.transmit_color, got.transmit_color, tol); + EXPECT_NEAR(exp.spec_exponent, got.spec_exponent, tol); + EXPECT_NEAR(exp.ior, got.ior, tol); + EXPECT_NEAR(exp.alpha, got.alpha, tol); + EXPECT_NEAR(exp.normal_strength, got.normal_strength, tol); + EXPECT_EQ(exp.illum_mode, got.illum_mode); + EXPECT_NEAR(exp.roughness, got.roughness, tol); + EXPECT_NEAR(exp.metallic, got.metallic, tol); + EXPECT_NEAR(exp.sheen, got.sheen, tol); + EXPECT_NEAR(exp.cc_thickness, got.cc_thickness, tol); + EXPECT_NEAR(exp.cc_roughness, got.cc_roughness, tol); + EXPECT_NEAR(exp.aniso, got.aniso, tol); + EXPECT_NEAR(exp.aniso_rot, got.aniso_rot, tol); for (int key = 0; key < (int)MTLTexMapType::Count; key++) { const MTLTexMap &exp_tex = exp.texture_maps[key]; const MTLTexMap &got_tex = got.texture_maps[key]; @@ -102,20 +110,20 @@ TEST_F(obj_mtl_parser_test, string_newlines_whitespace) "map_Ks sometex_s_spaces_after_name.png \t \r\n"; MTLMaterial mat[6]; mat[0].name = "simple"; - mat[0].Ka = {0.1f, 0.2f, 0.3f}; - mat[0].illum = 4; + mat[0].ambient_color = {0.1f, 0.2f, 0.3f}; + mat[0].illum_mode = 4; mat[1].name = "tab_indentation"; - mat[1].Kd = {0.2f, 0.3f, 0.4f}; + mat[1].color = {0.2f, 0.3f, 0.4f}; mat[2].name = "space_after_name"; - mat[2].Ks = {0.4f, 0.5f, 0.6f}; + mat[2].spec_color = {0.4f, 0.5f, 0.6f}; mat[3].name = "space_before_name"; mat[4].name = "indented_values"; - mat[4].Ka = {0.5f, 0.6f, 0.7f}; - mat[4].Kd = {0.6f, 0.7f, 0.8f}; + mat[4].ambient_color = {0.5f, 0.6f, 0.7f}; + mat[4].color = {0.6f, 0.7f, 0.8f}; mat[5].name = "crlf_ending"; - mat[5].Ns = 5.0f; - mat[5].tex_map_of_type(MTLTexMapType::Kd).image_path = "sometex_d.png"; - mat[5].tex_map_of_type(MTLTexMapType::Ks).image_path = "sometex_s_spaces_after_name.png"; + mat[5].spec_exponent = 5.0f; + mat[5].tex_map_of_type(MTLTexMapType::Color).image_path = "sometex_d.png"; + mat[5].tex_map_of_type(MTLTexMapType::Specular).image_path = "sometex_s_spaces_after_name.png"; check_string(text, mat, ARRAY_SIZE(mat)); } @@ -123,8 +131,8 @@ TEST_F(obj_mtl_parser_test, cube) { MTLMaterial mat; mat.name = "red"; - mat.Ka = {0.2f, 0.2f, 0.2f}; - mat.Kd = {1, 0, 0}; + mat.ambient_color = {0.2f, 0.2f, 0.2f}; + mat.color = {1, 0, 0}; check("cube.mtl", &mat, 1); } @@ -132,28 +140,28 @@ TEST_F(obj_mtl_parser_test, all_objects) { MTLMaterial mat[7]; for (auto &m : mat) { - m.Ka = {1, 1, 1}; - m.Ks = {0.5f, 0.5f, 0.5f}; - m.Ke = {0, 0, 0}; - m.Ns = 250; - m.Ni = 1; - m.d = 1; - m.illum = 2; + m.ambient_color = {1, 1, 1}; + m.spec_color = {0.5f, 0.5f, 0.5f}; + m.emission_color = {0, 0, 0}; + m.spec_exponent = 250; + m.ior = 1; + m.alpha = 1; + m.illum_mode = 2; } mat[0].name = "Blue"; - mat[0].Kd = {0, 0, 1}; + mat[0].color = {0, 0, 1}; mat[1].name = "BlueDark"; - mat[1].Kd = {0, 0, 0.5f}; + mat[1].color = {0, 0, 0.5f}; mat[2].name = "Green"; - mat[2].Kd = {0, 1, 0}; + mat[2].color = {0, 1, 0}; mat[3].name = "GreenDark"; - mat[3].Kd = {0, 0.5f, 0}; + mat[3].color = {0, 0.5f, 0}; mat[4].name = "Material"; - mat[4].Kd = {0.8f, 0.8f, 0.8f}; + mat[4].color = {0.8f, 0.8f, 0.8f}; mat[5].name = "Red"; - mat[5].Kd = {1, 0, 0}; + mat[5].color = {1, 0, 0}; mat[6].name = "RedDark"; - mat[6].Kd = {0.5f, 0, 0}; + mat[6].color = {0.5f, 0, 0}; check("all_objects.mtl", mat, ARRAY_SIZE(mat)); } @@ -161,92 +169,101 @@ TEST_F(obj_mtl_parser_test, materials) { MTLMaterial mat[6]; mat[0].name = "no_textures_red"; - mat[0].Ka = {0.3f, 0.3f, 0.3f}; - mat[0].Kd = {0.8f, 0.3f, 0.1f}; - mat[0].Ns = 5.624998f; + mat[0].ambient_color = {0.3f, 0.3f, 0.3f}; + mat[0].color = {0.8f, 0.3f, 0.1f}; + mat[0].spec_exponent = 5.624998f; mat[1].name = "four_maps"; - mat[1].Ka = {1, 1, 1}; - mat[1].Kd = {0.8f, 0.8f, 0.8f}; - mat[1].Ks = {0.5f, 0.5f, 0.5f}; - mat[1].Ke = {0, 0, 0}; - mat[1].Ns = 1000; - mat[1].Ni = 1.45f; - mat[1].d = 1; - mat[1].illum = 2; - mat[1].map_Bump_strength = 1; + mat[1].ambient_color = {1, 1, 1}; + mat[1].color = {0.8f, 0.8f, 0.8f}; + mat[1].spec_color = {0.5f, 0.5f, 0.5f}; + mat[1].emission_color = {0, 0, 0}; + mat[1].spec_exponent = 1000; + mat[1].ior = 1.45f; + mat[1].alpha = 1; + mat[1].illum_mode = 2; + mat[1].normal_strength = 1; { - MTLTexMap &kd = mat[1].tex_map_of_type(MTLTexMapType::Kd); + MTLTexMap &kd = mat[1].tex_map_of_type(MTLTexMapType::Color); kd.image_path = "texture.png"; - MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::Ns); + MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::SpecularExponent); ns.image_path = "sometexture_Roughness.png"; - MTLTexMap &refl = mat[1].tex_map_of_type(MTLTexMapType::refl); + MTLTexMap &refl = mat[1].tex_map_of_type(MTLTexMapType::Reflection); refl.image_path = "sometexture_Metallic.png"; - MTLTexMap &bump = mat[1].tex_map_of_type(MTLTexMapType::bump); + MTLTexMap &bump = mat[1].tex_map_of_type(MTLTexMapType::Normal); bump.image_path = "sometexture_Normal.png"; } mat[2].name = "Clay"; - mat[2].Ka = {1, 1, 1}; - mat[2].Kd = {0.8f, 0.682657f, 0.536371f}; - mat[2].Ks = {0.5f, 0.5f, 0.5f}; - mat[2].Ke = {0, 0, 0}; - mat[2].Ns = 440.924042f; - mat[2].Ni = 1.45f; - mat[2].d = 1; - mat[2].illum = 2; + mat[2].ambient_color = {1, 1, 1}; + mat[2].color = {0.8f, 0.682657f, 0.536371f}; + mat[2].spec_color = {0.5f, 0.5f, 0.5f}; + mat[2].emission_color = {0, 0, 0}; + mat[2].spec_exponent = 440.924042f; + mat[2].ior = 1.45f; + mat[2].alpha = 1; + mat[2].illum_mode = 2; mat[3].name = "Hat"; - mat[3].Ka = {1, 1, 1}; - mat[3].Kd = {0.8f, 0.8f, 0.8f}; - mat[3].Ks = {0.5f, 0.5f, 0.5f}; - mat[3].Ns = 800; - mat[3].map_Bump_strength = 0.5f; + mat[3].ambient_color = {1, 1, 1}; + mat[3].color = {0.8f, 0.8f, 0.8f}; + mat[3].spec_color = {0.5f, 0.5f, 0.5f}; + mat[3].spec_exponent = 800; + mat[3].normal_strength = 0.5f; { - MTLTexMap &kd = mat[3].tex_map_of_type(MTLTexMapType::Kd); + MTLTexMap &kd = mat[3].tex_map_of_type(MTLTexMapType::Color); kd.image_path = "someHatTexture_BaseColor.jpg"; - MTLTexMap &ns = mat[3].tex_map_of_type(MTLTexMapType::Ns); + MTLTexMap &ns = mat[3].tex_map_of_type(MTLTexMapType::SpecularExponent); ns.image_path = "someHatTexture_Roughness.jpg"; - MTLTexMap &refl = mat[3].tex_map_of_type(MTLTexMapType::refl); + MTLTexMap &refl = mat[3].tex_map_of_type(MTLTexMapType::Reflection); refl.image_path = "someHatTexture_Metalness.jpg"; - MTLTexMap &bump = mat[3].tex_map_of_type(MTLTexMapType::bump); + MTLTexMap &bump = mat[3].tex_map_of_type(MTLTexMapType::Normal); bump.image_path = "someHatTexture_Normal.jpg"; } mat[4].name = "Parser_Test"; - mat[4].Ka = {0.1f, 0.2f, 0.3f}; - mat[4].Kd = {0.4f, 0.5f, 0.6f}; - mat[4].Ks = {0.7f, 0.8f, 0.9f}; - mat[4].illum = 6; - mat[4].Ns = 15.5; - mat[4].Ni = 1.5; - mat[4].d = 0.5; - mat[4].map_Bump_strength = 0.1f; + mat[4].ambient_color = {0.1f, 0.2f, 0.3f}; + mat[4].color = {0.4f, 0.5f, 0.6f}; + mat[4].spec_color = {0.7f, 0.8f, 0.9f}; + mat[4].illum_mode = 6; + mat[4].spec_exponent = 15.5; + mat[4].ior = 1.5; + mat[4].alpha = 0.5; + mat[4].normal_strength = 0.1f; + mat[4].transmit_color = {0.1f, 0.3f, 0.5f}; + mat[4].normal_strength = 0.1f; + mat[4].roughness = 0.2f; + mat[4].metallic = 0.3f; + mat[4].sheen = 0.4f; + mat[4].cc_thickness = 0.5f; + mat[4].cc_roughness = 0.6f; + mat[4].aniso = 0.7f; + mat[4].aniso_rot = 0.8f; { - MTLTexMap &kd = mat[4].tex_map_of_type(MTLTexMapType::Kd); + MTLTexMap &kd = mat[4].tex_map_of_type(MTLTexMapType::Color); kd.image_path = "sometex_d.png"; - MTLTexMap &ns = mat[4].tex_map_of_type(MTLTexMapType::Ns); + MTLTexMap &ns = mat[4].tex_map_of_type(MTLTexMapType::SpecularExponent); ns.image_path = "sometex_ns.psd"; - MTLTexMap &refl = mat[4].tex_map_of_type(MTLTexMapType::refl); + MTLTexMap &refl = mat[4].tex_map_of_type(MTLTexMapType::Reflection); refl.image_path = "clouds.tiff"; refl.scale = {1.5f, 2.5f, 3.5f}; refl.translation = {4.5f, 5.5f, 6.5f}; refl.projection_type = SHD_PROJ_SPHERE; - MTLTexMap &bump = mat[4].tex_map_of_type(MTLTexMapType::bump); + MTLTexMap &bump = mat[4].tex_map_of_type(MTLTexMapType::Normal); bump.image_path = "somebump.tga"; bump.scale = {3, 4, 5}; } mat[5].name = "Parser_ScaleOffset_Test"; { - MTLTexMap &kd = mat[5].tex_map_of_type(MTLTexMapType::Kd); + MTLTexMap &kd = mat[5].tex_map_of_type(MTLTexMapType::Color); kd.translation = {2.5f, 0.0f, 0.0f}; kd.image_path = "OffsetOneValue.png"; - MTLTexMap &ks = mat[5].tex_map_of_type(MTLTexMapType::Ks); + MTLTexMap &ks = mat[5].tex_map_of_type(MTLTexMapType::Specular); ks.scale = {1.5f, 2.5f, 1.0f}; ks.translation = {3.5f, 4.5f, 0.0f}; ks.image_path = "ScaleOffsetBothTwovalues.png"; - MTLTexMap &ns = mat[5].tex_map_of_type(MTLTexMapType::Ns); + MTLTexMap &ns = mat[5].tex_map_of_type(MTLTexMapType::SpecularExponent); ns.scale = {0.5f, 1.0f, 1.0f}; ns.image_path = "1.Value.png"; } @@ -254,4 +271,75 @@ TEST_F(obj_mtl_parser_test, materials) check("materials.mtl", mat, ARRAY_SIZE(mat)); } +TEST_F(obj_mtl_parser_test, materials_without_pbr) +{ + MTLMaterial mat[2]; + mat[0].name = "Mat1"; + mat[0].spec_exponent = 360.0f; + mat[0].ambient_color = {0.9f, 0.9f, 0.9f}; + mat[0].color = {0.8f, 0.276449f, 0.101911f}; + mat[0].spec_color = {0.25f, 0.25f, 0.25f}; + mat[0].emission_color = {0, 0, 0}; + mat[0].ior = 1.45f; + mat[0].alpha = 1; + mat[0].illum_mode = 3; + + mat[1].name = "Mat2"; + mat[1].ambient_color = {1, 1, 1}; + mat[1].color = {0.8f, 0.8f, 0.8f}; + mat[1].spec_color = {0.5f, 0.5f, 0.5f}; + mat[1].ior = 1.45f; + mat[1].alpha = 1; + mat[1].illum_mode = 2; + { + MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::SpecularExponent); + ns.image_path = "../blend_geometry/texture_roughness.png"; + MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission); + ke.image_path = "../blend_geometry/texture_illum.png"; + } + + check("materials_without_pbr.mtl", mat, ARRAY_SIZE(mat)); +} + +TEST_F(obj_mtl_parser_test, materials_pbr) +{ + MTLMaterial mat[2]; + mat[0].name = "Mat1"; + mat[0].color = {0.8f, 0.276449f, 0.101911f}; + mat[0].spec_color = {0.25f, 0.25f, 0.25f}; + mat[0].emission_color = {0, 0, 0}; + mat[0].ior = 1.45f; + mat[0].alpha = 1; + mat[0].illum_mode = 3; + mat[0].roughness = 0.4f; + mat[0].metallic = 0.9f; + mat[0].sheen = 0.3f; + mat[0].cc_thickness = 0.393182f; + mat[0].cc_roughness = 0.05f; + mat[0].aniso = 0.2f; + mat[0].aniso_rot = 0.0f; + + mat[1].name = "Mat2"; + mat[1].color = {0.8f, 0.8f, 0.8f}; + mat[1].spec_color = {0.5f, 0.5f, 0.5f}; + mat[1].ior = 1.45f; + mat[1].alpha = 1; + mat[1].illum_mode = 2; + mat[1].metallic = 0.0f; + mat[1].cc_thickness = 0.3f; + mat[1].cc_roughness = 0.4f; + mat[1].aniso = 0.8f; + mat[1].aniso_rot = 0.7f; + { + MTLTexMap &pr = mat[1].tex_map_of_type(MTLTexMapType::Roughness); + pr.image_path = "../blend_geometry/texture_roughness.png"; + MTLTexMap &ps = mat[1].tex_map_of_type(MTLTexMapType::Sheen); + ps.image_path = "../blend_geometry/texture_checker.png"; + MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission); + ke.image_path = "../blend_geometry/texture_illum.png"; + } + + check("materials_pbr.mtl", mat, ARRAY_SIZE(mat)); +} + } // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index e2b58cefef6..0ab14988e40 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -1160,6 +1160,9 @@ typedef struct IdAdtTemplate { AnimData *adt; } IdAdtTemplate; +/* From: `DNA_object_types.h`, see it's doc-string there. */ +#define SELECT 1 + /* ************************************************ */ #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 6e18d442ee2..5862c3a6707 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -439,9 +439,9 @@ enum { /* *************** BEZTRIPLE **************** */ -/* BezTriple.f1,2,3 */ +/** #BezTriple.f1, #BezTriple.f2, #BezTriple.f3. */ typedef enum eBezTriple_Flag { - /* SELECT */ + /* `SELECT = (1 << 0)` */ BEZT_FLAG_TEMP_TAG = (1 << 1), /* always clear. */ /* Can be used to ignore keyframe points for certain operations. */ BEZT_FLAG_IGNORE_TAG = (1 << 2), diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index a83262d7639..6a2f25f3975 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -579,7 +579,7 @@ typedef enum eGPDlayer_Flag { GP_LAYER_USE_MASK = (1 << 13), /* TODO: DEPRECATED */ /* Ruler Layer */ GP_LAYER_IS_RULER = (1 << 14), - /* Disable masks in viewlayer render */ + /* Disable masks in view-layer render */ GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER = (1 << 15), } eGPDlayer_Flag; diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 65586b340cf..bfd1a37e782 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -34,10 +34,18 @@ typedef enum eViewLayerEEVEEPassType { EEVEE_RENDER_PASS_AO = (1 << 13), EEVEE_RENDER_PASS_BLOOM = (1 << 14), EEVEE_RENDER_PASS_AOV = (1 << 15), + /* + * TODO(jbakker): Clean up confliting bits after EEVEE has been removed. + * EEVEE_RENDER_PASS_CRYPTOMATTE is for EEVEE, EEVEE_RENDER_PASS_CRYTPOMATTE_* are for + * EEVEE-Next. + */ EEVEE_RENDER_PASS_CRYPTOMATTE = (1 << 16), - EEVEE_RENDER_PASS_VECTOR = (1 << 17), + EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT = (1 << 16), + EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET = (1 << 17), + EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL = (1 << 18), + EEVEE_RENDER_PASS_VECTOR = (1 << 19), } eViewLayerEEVEEPassType; -#define EEVEE_RENDER_PASS_MAX_BIT 18 +#define EEVEE_RENDER_PASS_MAX_BIT 20 /* #ViewLayerAOV.type */ typedef enum eViewLayerAOVType { @@ -198,16 +206,44 @@ enum { BASE_HIDDEN = (1 << 8), /* Object is hidden for editing. */ /* Runtime evaluated flags. */ - BASE_VISIBLE_DEPSGRAPH = (1 << 1), /* Object is enabled and visible for the depsgraph. */ - BASE_SELECTABLE = (1 << 2), /* Object can be selected. */ - BASE_FROM_DUPLI = (1 << 3), /* Object comes from duplicator. */ - BASE_VISIBLE_VIEWLAYER = (1 << 4), /* Object is enabled and visible for the viewlayer. */ - BASE_FROM_SET = (1 << 5), /* Object comes from set. */ - BASE_ENABLED_VIEWPORT = (1 << 6), /* Object is enabled in viewport. */ - BASE_ENABLED_RENDER = (1 << 7), /* Object is enabled in final render */ + + /* Object is enabled and potentially visible in a viewport. Layer collection + * visibility, local collection visibility, and local view are not part of this + * and may cause the object to be hidden depending on the 3D viewport settings. + * + * Objects with this flag will be considered visible by the viewport depsgraph + * and be evaluated as a result. + * + * This implies BASE_ENABLED_VIEWPORT. */ + BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT = (1 << 1), + + /* Object can be selected. */ + BASE_SELECTABLE = (1 << 2), + + /* Object comes from a duplicator. */ + BASE_FROM_DUPLI = (1 << 3), + + /* Object is enabled and visible in a viewport with default viewport settings, + * (so without any local view or local collection visibility overrides). Used + * when editors other than the 3D viewport need to know if an object is visible. */ + BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT = (1 << 4), + + /* Object comes from a scene set. */ + BASE_FROM_SET = (1 << 5), + + /* Object is enabled for viewport or final render respectively. Only enabled + * objects can be pulled into the depsgraph for evaluation, either through being + * directly visible, as a dependency of another object, or as part of colliders + * and effectors for physics. */ + BASE_ENABLED_VIEWPORT = (1 << 6), + BASE_ENABLED_RENDER = (1 << 7), + /* BASE_DEPRECATED = (1 << 9), */ - BASE_HOLDOUT = (1 << 10), /* Object masked out from render */ - BASE_INDIRECT_ONLY = (1 << 11), /* Object only contributes indirectly to render */ + + /* Object masked out from render */ + BASE_HOLDOUT = (1 << 10), + /* Object only contributes indirectly to render */ + BASE_INDIRECT_ONLY = (1 << 11), }; /* LayerCollection->flag */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index f14ec52decc..d335b36950c 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -18,6 +18,10 @@ namespace blender { template<typename T> class Span; template<typename T> class MutableSpan; +namespace bke { +class AttributeAccessor; +class MutableAttributeAccessor; +} // namespace bke } // namespace blender #endif @@ -346,6 +350,9 @@ typedef struct Mesh { /** Write access to loop data. */ blender::MutableSpan<MLoop> loops_for_write(); + blender::bke::AttributeAccessor attributes() const; + blender::bke::MutableAttributeAccessor attributes_for_write(); + /** * Vertex group data, encoded as an array of indices and weights for every vertex. * \warning: May be empty. @@ -433,8 +440,10 @@ enum { /** #Mesh.cd_flag */ enum { +#ifdef DNA_DEPRECATED_ALLOW ME_CDFLAG_VERT_BWEIGHT = 1 << 0, ME_CDFLAG_EDGE_BWEIGHT = 1 << 1, +#endif ME_CDFLAG_EDGE_CREASE = 1 << 2, ME_CDFLAG_VERT_CREASE = 1 << 3, }; diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index e0333f3ef03..77cb27083ab 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -25,7 +25,11 @@ extern "C" { */ typedef struct MVert { float co[3]; - char flag, bweight; + char flag; + /** + * Deprecated bevel weight storage, now located in #CD_BWEIGHT, except for file read and write. + */ + char bweight_legacy; char _pad[2]; } MVert; @@ -47,7 +51,11 @@ enum { typedef struct MEdge { /** Un-ordered vertex indices (cannot match). */ unsigned int v1, v2; - char crease, bweight; + char crease; + /** + * Deprecated bevel weight storage, now located in #CD_BWEIGHT, except for file read and write. + */ + char bweight_legacy; short flag; } MEdge; @@ -75,7 +83,7 @@ typedef struct MPoly { /** Keep signed since we need to subtract when getting the previous loop. */ int totloop; /** Deprecated material index. Now stored in the "material_index" attribute, but kept for IO. */ - short mat_nr DNA_DEPRECATED; + short mat_nr_legacy; char flag, _pad; } MPoly; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 28bbd3a3e4e..735f5c7b20a 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -637,6 +637,9 @@ typedef struct bNodeTree { /** A span containing all nodes in the node tree. */ blender::Span<bNode *> all_nodes(); blender::Span<const bNode *> all_nodes() const; + /** A span containing all group nodes in the node tree. */ + blender::Span<bNode *> group_nodes(); + blender::Span<const bNode *> group_nodes() const; /** A span containing all input sockets in the node tree. */ blender::Span<bNodeSocket *> all_input_sockets(); blender::Span<const bNodeSocket *> all_input_sockets() const; @@ -2029,6 +2032,21 @@ typedef enum CMPNodeFlipMode { CMP_NODE_FLIP_X_Y = 2, } CMPNodeFlipMode; +/* Scale Node. Stored in custom1. */ +typedef enum CMPNodeScaleMethod { + CMP_NODE_SCALE_RELATIVE = 0, + CMP_NODE_SCALE_ABSOLUTE = 1, + CMP_NODE_SCALE_RENDER_PERCENT = 2, + CMP_NODE_SCALE_RENDER_SIZE = 3, +} CMPNodeScaleMethod; + +/* Scale Node. Stored in custom2. */ +typedef enum CMPNodeScaleRenderSizeMethod { + CMP_NODE_SCALE_RENDER_SIZE_STRETCH = 0, + CMP_NODE_SCALE_RENDER_SIZE_FIT = 1, + CMP_NODE_SCALE_RENDER_SIZE_CROP = 2, +} CMPNodeScaleRenderSizeMethod; + /* Filter Node. Stored in custom1. */ typedef enum CMPNodeFilterMethod { CMP_NODE_FILTER_SOFT = 0, diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index ac9e61e03e8..add11d61db8 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -477,7 +477,13 @@ typedef struct ObHook { /* **************** OBJECT ********************* */ -/* used many places, should be specialized. */ +/** + * This is used as a flag for many kinds of data that use selections, examples include: + * - #BezTriple.f1, #BezTriple.f2, #BezTriple.f3 + * - #bNote.flag + * - #MovieTrackingTrack.flag + * And more, ideally this would have a generic location. + */ #define SELECT 1 /** #Object.type */ diff --git a/source/blender/makesdna/DNA_pointcloud_types.h b/source/blender/makesdna/DNA_pointcloud_types.h index ee829ebcf6e..34c5d153165 100644 --- a/source/blender/makesdna/DNA_pointcloud_types.h +++ b/source/blender/makesdna/DNA_pointcloud_types.h @@ -10,6 +10,13 @@ #include "DNA_customdata_types.h" #ifdef __cplusplus +namespace blender::bke { +class AttributeAccessor; +class MutableAttributeAccessor; +} // namespace blender::bke +#endif + +#ifdef __cplusplus extern "C" { #endif @@ -32,6 +39,11 @@ typedef struct PointCloud { short totcol; short _pad3[3]; +#ifdef __cplusplus + blender::bke::AttributeAccessor attributes() const; + blender::bke::MutableAttributeAccessor attributes_for_write(); +#endif + /* Draw Cache */ void *batch_cache; } PointCloud; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 40345c31fef..f184460cba4 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -304,6 +304,10 @@ typedef enum eScenePassType { #define RE_PASSNAME_BLOOM "BloomCol" #define RE_PASSNAME_VOLUME_LIGHT "VolumeDir" +#define RE_PASSNAME_CRYPTOMATTE_OBJECT "CryptoObject" +#define RE_PASSNAME_CRYPTOMATTE_ASSET "CryptoAsset" +#define RE_PASSNAME_CRYPTOMATTE_MATERIAL "CryptoMaterial" + /** View - MultiView. */ typedef struct SceneRenderView { struct SceneRenderView *next, *prev; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index a46d737ba9d..c0f92010c22 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -136,7 +136,7 @@ typedef struct SequenceRuntime { */ typedef struct Sequence { struct Sequence *next, *prev; - /** Tmp var for copying, and tagging for linked selection. */ + /** Temp var for copying, and tagging for linked selection. */ void *tmp; /** Needed (to be like ipo), else it will raise libdata warnings, this should never be used. */ void *lib; @@ -522,8 +522,6 @@ typedef struct SequencerScopes { #define MAXSEQ 128 -#define SELECT 1 - /** #Editor.overlay_frame_flag */ #define SEQ_EDIT_OVERLAY_FRAME_SHOW 1 #define SEQ_EDIT_OVERLAY_FRAME_ABS 2 @@ -549,9 +547,12 @@ typedef struct SequencerScopes { #define SEQ_NAME_MAXSTR 64 +/* From: `DNA_object_types.h`, see it's doc-string there. */ +#define SELECT 1 + /** #Sequence.flag */ enum { - /* SELECT */ + /* `SELECT = (1 << 0)` */ SEQ_LEFTSEL = (1 << 1), SEQ_RIGHTSEL = (1 << 2), SEQ_OVERLAP = (1 << 3), diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index f25ff5fbbb8..257e60eae98 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -97,6 +97,9 @@ 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(Object_Runtime, crazyspace_num_verts, crazyspace_verts_num) +DNA_STRUCT_RENAME_ELEM(MEdge, bweight, bweight_legacy) +DNA_STRUCT_RENAME_ELEM(MPoly, mat_nr, mat_nr_legacy) +DNA_STRUCT_RENAME_ELEM(MVert, bweight, bweight_legacy) 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) diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index c36e53a49cd..28ceb0d1d9d 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -108,13 +108,11 @@ static CustomData *rna_mesh_vdata(const PointerRNA *ptr) Mesh *me = rna_mesh(ptr); return rna_mesh_vdata_helper(me); } -# if 0 static CustomData *rna_mesh_edata(PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); return rna_mesh_edata_helper(me); } -# endif static CustomData *rna_mesh_pdata(const PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); @@ -231,6 +229,16 @@ static bool rna_Mesh_has_custom_normals_get(PointerRNA *ptr) return BKE_mesh_has_custom_loop_normals(me); } +static bool rna_Mesh_has_edge_bevel_weight_get(PointerRNA *ptr) +{ + return CustomData_has_layer(rna_mesh_edata(ptr), CD_BWEIGHT); +} + +static bool rna_Mesh_has_vertex_bevel_weight_get(PointerRNA *ptr) +{ + return CustomData_has_layer(rna_mesh_vdata(ptr), CD_BWEIGHT); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -430,26 +438,36 @@ static void rna_MeshVertex_hide_set(PointerRNA *ptr, bool value) static float rna_MeshVertex_bevel_weight_get(PointerRNA *ptr) { - MVert *mvert = (MVert *)ptr->data; - return mvert->bweight / 255.0f; + const Mesh *mesh = rna_mesh(ptr); + const int index = rna_MeshVertex_index_get(ptr); + const float *values = (const float *)CustomData_get_layer(&mesh->vdata, CD_BWEIGHT); + return values == NULL ? 0.0f : values[index]; } static void rna_MeshVertex_bevel_weight_set(PointerRNA *ptr, float value) { - MVert *mvert = (MVert *)ptr->data; - mvert->bweight = round_fl_to_uchar_clamp(value * 255.0f); + Mesh *mesh = rna_mesh(ptr); + const int index = rna_MeshVertex_index_get(ptr); + float *values = (float *)CustomData_add_layer( + &mesh->vdata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, mesh->totvert); + values[index] = clamp_f(value, 0.0f, 1.0f); } static float rna_MEdge_bevel_weight_get(PointerRNA *ptr) { - MEdge *medge = (MEdge *)ptr->data; - return medge->bweight / 255.0f; + const Mesh *mesh = rna_mesh(ptr); + const int index = rna_MeshEdge_index_get(ptr); + const float *values = (const float *)CustomData_get_layer(&mesh->edata, CD_BWEIGHT); + return values == NULL ? 0.0f : values[index]; } static void rna_MEdge_bevel_weight_set(PointerRNA *ptr, float value) { - MEdge *medge = (MEdge *)ptr->data; - medge->bweight = round_fl_to_uchar_clamp(value * 255.0f); + Mesh *mesh = rna_mesh(ptr); + const int index = rna_MeshEdge_index_get(ptr); + float *values = (float *)CustomData_add_layer( + &mesh->edata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, mesh->totedge); + values[index] = clamp_f(value, 0.0f, 1.0f); } static float rna_MEdge_crease_get(PointerRNA *ptr) @@ -3854,6 +3872,18 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_custom_normals_get", NULL); RNA_define_verify_sdna(true); + prop = RNA_def_property(srna, "has_bevel_weight_edge", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Has Edge Bevel Weight", "True if the mesh has an edge bevel weight layer"); + RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_edge_bevel_weight_get", NULL); + + prop = RNA_def_property(srna, "has_bevel_weight_vertex", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Has Vertex Bevel Weight", "True if the mesh has an vertex bevel weight layer"); + RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_vertex_bevel_weight_get", NULL); + prop = RNA_def_property(srna, "texco_mesh", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "texcomesh"); RNA_def_property_flag(prop, PROP_EDITABLE); @@ -3907,13 +3937,6 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Mesh_update_vertmask"); /* customdata flags */ - prop = RNA_def_property(srna, "use_customdata_vertex_bevel", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "cd_flag", ME_CDFLAG_VERT_BWEIGHT); - RNA_def_property_ui_text(prop, "Store Vertex Bevel Weight", ""); - - prop = RNA_def_property(srna, "use_customdata_edge_bevel", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "cd_flag", ME_CDFLAG_EDGE_BWEIGHT); - RNA_def_property_ui_text(prop, "Store Edge Bevel Weight", ""); prop = RNA_def_property(srna, "use_customdata_vertex_crease", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "cd_flag", ME_CDFLAG_VERT_CREASE); diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 3635759ad4d..6b1df3fc4d4 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -22,6 +22,7 @@ # include "DNA_mesh_types.h" +# include "BKE_anim_data.h" # include "BKE_mesh.h" # include "BKE_mesh_mapping.h" # include "BKE_mesh_runtime.h" @@ -102,10 +103,10 @@ static void rna_Mesh_calc_smooth_groups( static void rna_Mesh_normals_split_custom_do(Mesh *mesh, float (*custom_loopnors)[3], - const bool use_vertices) + const bool use_verts) { - if (use_vertices) { - BKE_mesh_set_custom_normals_from_vertices(mesh, custom_loopnors); + if (use_verts) { + BKE_mesh_set_custom_normals_from_verts(mesh, custom_loopnors); } else { BKE_mesh_set_custom_normals(mesh, custom_loopnors); @@ -165,7 +166,7 @@ static void rna_Mesh_transform(Mesh *mesh, float mat[16], bool shape_keys) static void rna_Mesh_flip_normals(Mesh *mesh) { - BKE_mesh_polygons_flip( + BKE_mesh_polys_flip( BKE_mesh_polys(mesh), BKE_mesh_loops_for_write(mesh), &mesh->ldata, mesh->totpoly); BKE_mesh_tessface_clear(mesh); BKE_mesh_normals_tag_dirty(mesh); @@ -192,6 +193,7 @@ static void rna_Mesh_count_selected_items(Mesh *mesh, int r_count[3]) static void rna_Mesh_clear_geometry(Mesh *mesh) { BKE_mesh_clear_geometry(mesh); + BKE_animdata_free(&mesh->id, false); DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY_ALL_MODES); WM_main_add_notifier(NC_GEOM | ND_DATA, mesh); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 0be1dd3117c..caeee35a80a 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -7190,18 +7190,18 @@ static void def_cmp_scale(StructRNA *srna) PropertyRNA *prop; static const EnumPropertyItem space_items[] = { - {CMP_SCALE_RELATIVE, "RELATIVE", 0, "Relative", ""}, - {CMP_SCALE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", ""}, - {CMP_SCALE_SCENEPERCENT, "SCENE_SIZE", 0, "Scene Size", ""}, - {CMP_SCALE_RENDERPERCENT, "RENDER_SIZE", 0, "Render Size", ""}, + {CMP_NODE_SCALE_RELATIVE, "RELATIVE", 0, "Relative", ""}, + {CMP_NODE_SCALE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", ""}, + {CMP_NODE_SCALE_RENDER_PERCENT, "SCENE_SIZE", 0, "Scene Size", ""}, + {CMP_NODE_SCALE_RENDER_SIZE, "RENDER_SIZE", 0, "Render Size", ""}, {0, NULL, 0, NULL, NULL}, }; /* matching bgpic_camera_frame_items[] */ static const EnumPropertyItem space_frame_items[] = { - {0, "STRETCH", 0, "Stretch", ""}, - {CMP_SCALE_RENDERSIZE_FRAME_ASPECT, "FIT", 0, "Fit", ""}, - {CMP_SCALE_RENDERSIZE_FRAME_ASPECT | CMP_SCALE_RENDERSIZE_FRAME_CROP, "CROP", 0, "Crop", ""}, + {CMP_NODE_SCALE_RENDER_SIZE_STRETCH, "STRETCH", 0, "Stretch", ""}, + {CMP_NODE_SCALE_RENDER_SIZE_FIT, "FIT", 0, "Fit", ""}, + {CMP_NODE_SCALE_RENDER_SIZE_CROP, "CROP", 0, "Crop", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 6cbc24db2d8..cfc3a832166 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -500,7 +500,7 @@ void rna_Object_data_update(Main *bmain, Scene *scene, PointerRNA *ptr) Object *object = (Object *)ptr->data; if (object->mode == OB_MODE_SCULPT) { - BKE_sculpt_ensure_orig_mesh_data(scene, object); + BKE_sculpt_ensure_orig_mesh_data(object); } rna_Object_internal_update_data_dependency(bmain, scene, ptr); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index cd2434ff5be..40e7f6e65c2 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -212,10 +212,10 @@ static void rna_ParticleHairKey_location_object_get(PointerRNA *ptr, float *valu if (pa) { Mesh *hair_mesh = (psmd->psys->flag & PSYS_HAIR_DYNAMICS) ? psmd->psys->hair_out_mesh : NULL; - const MVert *vertices = BKE_mesh_verts(hair_mesh); + const MVert *verts = BKE_mesh_verts(hair_mesh); if (hair_mesh) { - const MVert *mvert = &vertices[pa->hair_index + (hkey - pa->hair)]; - copy_v3_v3(values, mvert->co); + const MVert *mv = &verts[pa->hair_index + (hkey - pa->hair)]; + copy_v3_v3(values, mv->co); } else { float hairmat[4][4]; @@ -279,9 +279,9 @@ static void hair_key_location_object_set(HairKey *hair_key, if (hair_key_index == -1) { return; } - MVert *vertices = BKE_mesh_verts_for_write(hair_mesh); - MVert *mvert = &vertices[particle->hair_index + (hair_key_index)]; - copy_v3_v3(mvert->co, src_co); + MVert *verts = BKE_mesh_verts_for_write(hair_mesh); + MVert *mv = &verts[particle->hair_index + (hair_key_index)]; + copy_v3_v3(mv->co, src_co); return; } @@ -324,9 +324,9 @@ static void rna_ParticleHairKey_co_object(HairKey *hairkey, NULL; if (particle) { if (hair_mesh) { - const MVert *vertices = BKE_mesh_verts(hair_mesh); - const MVert *mvert = &vertices[particle->hair_index + (hairkey - particle->hair)]; - copy_v3_v3(n_co, mvert->co); + const MVert *verts = BKE_mesh_verts(hair_mesh); + const MVert *mv = &verts[particle->hair_index + (hairkey - particle->hair)]; + copy_v3_v3(n_co, mv->co); } else { float hairmat[4][4]; diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc index bc77ca3f7d3..96f46f5dbe6 100644 --- a/source/blender/makesrna/intern/rna_path.cc +++ b/source/blender/makesrna/intern/rna_path.cc @@ -16,6 +16,7 @@ #include "BKE_idprop.h" #include "BKE_idtype.h" +#include "BKE_lib_id.h" #include "DNA_ID.h" /* For ID properties. */ @@ -926,7 +927,6 @@ ID *RNA_find_real_ID_and_path(ID *id, const char **r_path) return id; } - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (r_path) { switch (GS(id->name)) { case ID_NT: @@ -940,11 +940,9 @@ ID *RNA_find_real_ID_and_path(ID *id, const char **r_path) } } - if (id_type->owner_get == nullptr) { - BLI_assert_msg(0, "Missing handling of embedded id type."); - return id; - } - return id_type->owner_get(id); + ID *owner_id = BKE_id_owner_get(id); + BLI_assert_msg(owner_id != nullptr, "Missing handling of embedded id type."); + return (owner_id != nullptr) ? owner_id : id; } static char *rna_prepend_real_ID_path(Main * /*bmain*/, ID *id, char *path, ID **r_real_id) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 4fb8ba2eb1e..27cfe766eef 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1484,8 +1484,7 @@ static void rna_ImageFormatSettings_color_management_set(PointerRNA *ptr, int va 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(owner_id); + owner_id = BKE_id_owner_get(owner_id); } if (owner_id && GS(owner_id->name) == ID_SCE) { BKE_image_format_color_management_copy_from_scene(imf, (Scene *)owner_id); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 2bfd3c5cf27..01b68cbd134 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -455,6 +455,9 @@ static const EnumPropertyItem rna_enum_view3dshading_render_pass_type_items[] = RNA_ENUM_ITEM_HEADING(N_("Data"), NULL), {EEVEE_RENDER_PASS_NORMAL, "NORMAL", 0, "Normal", ""}, {EEVEE_RENDER_PASS_MIST, "MIST", 0, "Mist", ""}, + {EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, "CryptoObject", 0, "CryptoObject", ""}, + {EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, "CryptoAsset", 0, "CryptoAsset", ""}, + {EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL, "CryptoMaterial", 0, "CryptoMaterial", ""}, RNA_ENUM_ITEM_HEADING(N_("Shader AOV"), NULL), {EEVEE_RENDER_PASS_AOV, "AOV", 0, "AOV", ""}, @@ -1423,6 +1426,7 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C, const bool bloom_enabled = scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED; const bool aov_available = BKE_view_layer_has_valid_aov(view_layer); + const bool eevee_next_active = STREQ(scene->r.engine, "BLENDER_EEVEE_NEXT"); int totitem = 0; EnumPropertyItem *result = NULL; @@ -1443,6 +1447,12 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C, aov_template.value++; } } + else if (ELEM(item->value, + EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, + EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, + EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL) && + !eevee_next_active) { + } else if (!((!bloom_enabled && (item->value == EEVEE_RENDER_PASS_BLOOM || STREQ(item->name, "Effects"))) || (!aov_available && STREQ(item->name, "Shader AOV")))) { diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 61d4edccb06..0031e023d39 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6377,7 +6377,7 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_viewport_debug", 1); RNA_def_property_ui_text(prop, "Viewport Debug", - "Enable viewport debugging options for developpers in the overlays " + "Enable viewport debugging options for developers in the overlays " "pop-over"); RNA_def_property_update(prop, 0, "rna_userdef_ui_update"); } diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 73daabec9b3..8bace2e048c 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -65,7 +65,6 @@ set(SRC intern/MOD_mirror.c intern/MOD_multires.c intern/MOD_nodes.cc - intern/MOD_nodes_evaluator.cc intern/MOD_none.c intern/MOD_normal_edit.c intern/MOD_ocean.c @@ -105,7 +104,6 @@ set(SRC MOD_modifiertypes.h MOD_nodes.h intern/MOD_meshcache_util.h - intern/MOD_nodes_evaluator.hh intern/MOD_solidify_util.h intern/MOD_ui_common.h intern/MOD_util.h diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 42fe0107217..7feff30968f 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -294,7 +294,7 @@ static void mesh_merge_transform(Mesh *result, for (i = 0; i < cap_nverts; i++, mv++) { mul_m4_v3(cap_offset, mv->co); /* Reset MVert flags for caps */ - mv->flag = mv->bweight = 0; + mv->flag = 0; } /* We have to correct normals too, if we do not tag them as dirty later! */ @@ -482,7 +482,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, } /* About 67 million vertices max seems a decent limit for now. */ - const size_t max_vertices_num = 1 << 26; + const size_t max_verts_num = 1 << 26; /* calculate the maximum number of copies which will fit within the * prescribed length */ @@ -500,7 +500,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, * vertices. */ if (((size_t)count * (size_t)chunk_nverts + (size_t)start_cap_nverts + - (size_t)end_cap_nverts) > max_vertices_num) { + (size_t)end_cap_nverts) > max_verts_num) { count = 1; offset_is_too_small = true; } @@ -522,7 +522,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, * vertices. */ else if (((size_t)count * (size_t)chunk_nverts + (size_t)start_cap_nverts + - (size_t)end_cap_nverts) > max_vertices_num) { + (size_t)end_cap_nverts) > max_verts_num) { count = 1; BKE_modifier_set_error(ctx->object, &amd->modifier, @@ -560,8 +560,8 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, chunk_nloops); CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, chunk_npolys); - /* Subsurf for eg won't have mesh data in the custom data arrays. - * now add mvert/medge/mpoly layers. */ + /* Subdivision-surface for eg won't have mesh data in the custom-data arrays. + * Now add #MVert/#MEdge/#MPoly layers. */ if (!CustomData_has_layer(&mesh->vdata, CD_MVERT)) { memcpy(result_verts, src_verts, sizeof(MVert) * mesh->totvert); } diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index ee9a2856ab0..668843188ab 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -74,6 +74,10 @@ static void requiredDataMask(Object *UNUSED(ob), if (bmd->defgrp_name[0] != '\0') { r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; } + if (bmd->lim_flags & MOD_BEVEL_WEIGHT) { + r_cddata_masks->vmask |= CD_MASK_BWEIGHT; + r_cddata_masks->emask |= CD_MASK_BWEIGHT; + } } /* diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index e0428042caa..b3ee6a1f4ca 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -143,9 +143,9 @@ static void invert_boolean_array(MutableSpan<bool> array) } } -static void compute_masked_vertices(Span<bool> vertex_mask, - MutableSpan<int> r_vertex_map, - uint *r_verts_masked_num) +static void compute_masked_verts(Span<bool> vertex_mask, + MutableSpan<int> r_vertex_map, + uint *r_verts_masked_num) { BLI_assert(vertex_mask.size() == r_vertex_map.size()); @@ -223,12 +223,12 @@ static void computed_masked_edges_smooth(const Mesh *mesh, *r_verts_add_num = verts_add_num; } -static void computed_masked_polygons(const Mesh *mesh, - Span<bool> vertex_mask, - Vector<int> &r_masked_poly_indices, - Vector<int> &r_loop_starts, - uint *r_polys_masked_num, - uint *r_loops_masked_num) +static void computed_masked_polys(const Mesh *mesh, + Span<bool> vertex_mask, + Vector<int> &r_masked_poly_indices, + Vector<int> &r_loop_starts, + uint *r_polys_masked_num, + uint *r_loops_masked_num) { BLI_assert(mesh->totvert == vertex_mask.size()); const Span<MPoly> polys = mesh->polys(); @@ -261,15 +261,15 @@ static void computed_masked_polygons(const Mesh *mesh, *r_loops_masked_num = loops_masked_num; } -static void compute_interpolated_polygons(const Mesh *mesh, - Span<bool> vertex_mask, - uint verts_add_num, - uint loops_masked_num, - Vector<int> &r_masked_poly_indices, - Vector<int> &r_loop_starts, - uint *r_edges_add_num, - uint *r_polys_add_num, - uint *r_loops_add_num) +static void compute_interpolated_polys(const Mesh *mesh, + Span<bool> vertex_mask, + uint verts_add_num, + uint loops_masked_num, + Vector<int> &r_masked_poly_indices, + Vector<int> &r_loop_starts, + uint *r_edges_add_num, + uint *r_polys_add_num, + uint *r_loops_add_num) { BLI_assert(mesh->totvert == vertex_mask.size()); @@ -333,9 +333,9 @@ static void compute_interpolated_polygons(const Mesh *mesh, *r_loops_add_num = loops_add_num; } -static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map) +static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); const Span<MVert> src_verts = src_mesh.verts(); @@ -692,7 +692,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) Array<int> vertex_map(mesh->totvert); uint verts_masked_num; - compute_masked_vertices(vertex_mask, vertex_map, &verts_masked_num); + compute_masked_verts(vertex_mask, vertex_map, &verts_masked_num); Array<int> edge_map(mesh->totedge); uint edges_masked_num; @@ -709,26 +709,26 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) Vector<int> new_loop_starts; uint polys_masked_num; uint loops_masked_num; - computed_masked_polygons(mesh, - vertex_mask, - masked_poly_indices, - new_loop_starts, - &polys_masked_num, - &loops_masked_num); + computed_masked_polys(mesh, + vertex_mask, + masked_poly_indices, + new_loop_starts, + &polys_masked_num, + &loops_masked_num); uint edges_add_num = 0; uint polys_add_num = 0; uint loops_add_num = 0; if (use_interpolation) { - compute_interpolated_polygons(mesh, - vertex_mask, - verts_add_num, - loops_masked_num, - masked_poly_indices, - new_loop_starts, - &edges_add_num, - &polys_add_num, - &loops_add_num); + compute_interpolated_polys(mesh, + vertex_mask, + verts_add_num, + loops_masked_num, + masked_poly_indices, + new_loop_starts, + &edges_add_num, + &polys_add_num, + &loops_add_num); } Mesh *result = BKE_mesh_new_nomain_from_template(mesh, @@ -738,7 +738,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) loops_masked_num + loops_add_num, polys_masked_num + polys_add_num); - copy_masked_vertices_to_new_mesh(*mesh, *result, vertex_map); + copy_masked_verts_to_new_mesh(*mesh, *result, vertex_map); if (use_interpolation) { add_interp_verts_copy_edges_to_new_mesh(*mesh, *result, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 1a41fa4cd3b..f30e6a95787 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -181,15 +181,15 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * const Span<MVert> mesh_verts = mesh->verts(); const Span<MEdge> mesh_edges = mesh->edges(); const Span<MPoly> mesh_polys = mesh->polys(); - const Span<MVert> me_vertices = me->verts(); + const Span<MVert> me_verts = me->verts(); const Span<MEdge> me_edges = me->edges(); - const Span<MPoly> me_polygons = me->polys(); + const Span<MPoly> me_polys = me->polys(); /* TODO(sybren+bastien): possibly check relevant custom data layers (UV/color depending on * flags) and duplicate those too. * XXX(Hans): This probably isn't true anymore with various CoW improvements, etc. */ - if ((me_vertices.data() == mesh_verts.data()) || (me_edges.data() == mesh_edges.data()) || - (me_polygons.data() == mesh_polys.data())) { + if ((me_verts.data() == mesh_verts.data()) || (me_edges.data() == mesh_edges.data()) || + (me_polys.data() == mesh_polys.data())) { /* We need to duplicate data here, otherwise we'll modify org mesh, see T51701. */ mesh = reinterpret_cast<Mesh *>( BKE_id_copy_ex(nullptr, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 2908fbf5597..ffd78a90638 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -36,6 +36,7 @@ #include "DNA_windowmanager_types.h" #include "BKE_attribute_math.hh" +#include "BKE_compute_contexts.hh" #include "BKE_customdata.h" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set_instances.hh" @@ -73,7 +74,6 @@ #include "MOD_modifiertypes.h" #include "MOD_nodes.h" -#include "MOD_nodes_evaluator.hh" #include "MOD_ui_common.h" #include "ED_object.h" @@ -81,15 +81,18 @@ #include "ED_spreadsheet.h" #include "ED_undo.h" -#include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" -#include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_geometry_nodes_lazy_function.hh" #include "NOD_node_declaration.hh" #include "FN_field.hh" #include "FN_field_cpp_type.hh" +#include "FN_lazy_function_execute.hh" +#include "FN_lazy_function_graph_executor.hh" #include "FN_multi_function.hh" +namespace lf = blender::fn::lazy_function; + using blender::Array; using blender::ColorGeometry4f; using blender::CPPType; @@ -106,6 +109,7 @@ using blender::MultiValueMap; using blender::MutableSpan; using blender::Set; using blender::Span; +using blender::Stack; using blender::StringRef; using blender::StringRefNull; using blender::Vector; @@ -117,11 +121,17 @@ using blender::fn::ValueOrFieldCPPType; using blender::nodes::FieldInferencingInterface; using blender::nodes::GeoNodeExecParams; using blender::nodes::InputSocketFieldType; +using blender::nodes::geo_eval_log::GeoModifierLog; using blender::threading::EnumerableThreadSpecific; using namespace blender::fn::multi_function_types; -using namespace blender::nodes::derived_node_tree_types; -using geo_log::eNamedAttrUsage; -using geo_log::GeometryAttributeInfo; +using blender::nodes::geo_eval_log::GeometryAttributeInfo; +using blender::nodes::geo_eval_log::GeometryInfoLog; +using blender::nodes::geo_eval_log::GeoNodeLog; +using blender::nodes::geo_eval_log::GeoTreeLog; +using blender::nodes::geo_eval_log::NamedAttributeUsage; +using blender::nodes::geo_eval_log::NodeWarning; +using blender::nodes::geo_eval_log::NodeWarningType; +using blender::nodes::geo_eval_log::ValueLog; static void initData(ModifierData *md) { @@ -756,36 +766,37 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) } static void initialize_group_input(NodesModifierData &nmd, - const bNodeSocket &socket, + const bNodeSocket &interface_socket, + const int input_index, void *r_value) { - const bNodeSocketType &socket_type = *socket.typeinfo; - const bNodeSocket &bsocket = socket; - const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(bsocket.type); + const bNodeSocketType &socket_type = *interface_socket.typeinfo; + const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>( + interface_socket.type); if (nmd.settings.properties == nullptr) { - socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); + socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value); return; } const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, - socket.identifier); + interface_socket.identifier); if (property == nullptr) { - socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); + socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value); return; } - if (!id_property_type_matches_socket(bsocket, *property)) { - socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); + if (!id_property_type_matches_socket(interface_socket, *property)) { + socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value); return; } - if (!input_has_attribute_toggle(*nmd.node_group, socket.runtime->index_in_node)) { + if (!input_has_attribute_toggle(*nmd.node_group, input_index)) { init_socket_cpp_value_from_property(*property, socket_data_type, r_value); return; } const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup( - nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str()); + nmd.settings.properties, (interface_socket.identifier + use_attribute_suffix).c_str()); const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( - nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str()); + nmd.settings.properties, (interface_socket.identifier + attribute_name_suffix).c_str()); if (property_use_attribute == nullptr || property_attribute_name == nullptr) { init_socket_cpp_value_from_property(*property, socket_data_type, r_value); return; @@ -831,13 +842,25 @@ static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain) return spreadsheets; } -static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet, - NodesModifierData *nmd, - const ModifierEvalContext *ctx, - const DerivedNodeTree &tree, - Set<DSocket> &r_sockets_to_preview) +static const lf::FunctionNode &find_viewer_lf_node(const bNode &viewer_bnode) +{ + return *blender::nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree()) + ->mapping.viewer_node_map.lookup(&viewer_bnode); +} +static const lf::FunctionNode &find_group_lf_node(const bNode &group_bnode) +{ + return *blender::nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree()) + ->mapping.group_node_map.lookup(&group_bnode); +} + +static void find_side_effect_nodes_for_spreadsheet( + const SpaceSpreadsheet &sspreadsheet, + const NodesModifierData &nmd, + const ModifierEvalContext &ctx, + const bNodeTree &root_tree, + MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes) { - Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path; + Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path; if (context_path.size() < 3) { return; } @@ -848,11 +871,11 @@ static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadshe return; } SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0]; - if (object_context->object != DEG_get_original_object(ctx->object)) { + if (object_context->object != DEG_get_original_object(ctx.object)) { return; } SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1]; - if (StringRef(modifier_context->modifier_name) != nmd->modifier.name) { + if (StringRef(modifier_context->modifier_name) != nmd.modifier.name) { return; } for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { @@ -861,61 +884,77 @@ static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadshe } } - Span<SpreadsheetContextNode *> nested_group_contexts = + blender::ComputeContextBuilder compute_context_builder; + compute_context_builder.push<blender::bke::ModifierComputeContext>(nmd.modifier.name); + + const Span<SpreadsheetContextNode *> nested_group_contexts = context_path.as_span().drop_front(2).drop_back(1).cast<SpreadsheetContextNode *>(); - SpreadsheetContextNode *last_context = (SpreadsheetContextNode *)context_path.last(); + const SpreadsheetContextNode *last_context = (SpreadsheetContextNode *)context_path.last(); - const DTreeContext *context = &tree.root_context(); + Stack<const bNode *> group_node_stack; + const bNodeTree *group = &root_tree; for (SpreadsheetContextNode *node_context : nested_group_contexts) { - const bNodeTree &btree = context->btree(); const bNode *found_node = nullptr; - for (const bNode *bnode : btree.all_nodes()) { - if (STREQ(bnode->name, node_context->node_name)) { - found_node = bnode; + for (const bNode *node : group->group_nodes()) { + if (STREQ(node->name, node_context->node_name)) { + found_node = node; break; } } if (found_node == nullptr) { return; } - context = context->child_context(*found_node); - if (context == nullptr) { + if (found_node->id == nullptr) { return; } + group_node_stack.push(found_node); + group = reinterpret_cast<const bNodeTree *>(found_node->id); + compute_context_builder.push<blender::bke::NodeGroupComputeContext>(node_context->node_name); } - const bNodeTree &btree = context->btree(); - for (const bNode *bnode : btree.nodes_by_type("GeometryNodeViewer")) { - if (STREQ(bnode->name, last_context->node_name)) { - const DNode viewer_node{context, bnode}; - for (const bNodeSocket *input_socket : bnode->input_sockets()) { - if (input_socket->is_available() && input_socket->is_logically_linked()) { - r_sockets_to_preview.add(DSocket{context, input_socket}); - } - } + const bNode *found_viewer_node = nullptr; + for (const bNode *viewer_node : group->nodes_by_type("GeometryNodeViewer")) { + if (STREQ(viewer_node->name, last_context->node_name)) { + found_viewer_node = viewer_node; + break; } } + if (found_viewer_node == nullptr) { + return; + } + + /* Not only mark the viewer node as having side effects, but also all group nodes it is contained + * in. */ + r_side_effect_nodes.add(compute_context_builder.hash(), + &find_viewer_lf_node(*found_viewer_node)); + compute_context_builder.pop(); + while (!compute_context_builder.is_empty()) { + r_side_effect_nodes.add(compute_context_builder.hash(), + &find_group_lf_node(*group_node_stack.pop())); + compute_context_builder.pop(); + } } -static void find_sockets_to_preview(NodesModifierData *nmd, - const ModifierEvalContext *ctx, - const DerivedNodeTree &tree, - Set<DSocket> &r_sockets_to_preview) +static void find_side_effect_nodes( + const NodesModifierData &nmd, + const ModifierEvalContext &ctx, + const bNodeTree &tree, + MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes) { - Main *bmain = DEG_get_bmain(ctx->depsgraph); + Main *bmain = DEG_get_bmain(ctx.depsgraph); /* Based on every visible spreadsheet context path, get a list of sockets that need to have their * intermediate geometries cached for display. */ Vector<SpaceSpreadsheet *> spreadsheets = find_spreadsheet_editors(bmain); for (SpaceSpreadsheet *sspreadsheet : spreadsheets) { - find_sockets_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree, r_sockets_to_preview); + find_side_effect_nodes_for_spreadsheet(*sspreadsheet, nmd, ctx, tree, r_side_effect_nodes); } } static void clear_runtime_data(NodesModifierData *nmd) { if (nmd->runtime_eval_log != nullptr) { - delete (geo_log::ModifierLog *)nmd->runtime_eval_log; + delete static_cast<GeoModifierLog *>(nmd->runtime_eval_log); nmd->runtime_eval_log = nullptr; } } @@ -1079,92 +1118,104 @@ static void store_output_attributes(GeometrySet &geometry, /** * Evaluate a node group to compute the output geometry. */ -static GeometrySet compute_geometry(const DerivedNodeTree &tree, - Span<const bNode *> group_input_nodes, - const bNode &output_node, - GeometrySet input_geometry_set, - NodesModifierData *nmd, - const ModifierEvalContext *ctx) +static GeometrySet compute_geometry( + const bNodeTree &btree, + const blender::nodes::GeometryNodesLazyFunctionGraphInfo &lf_graph_info, + const bNode &output_node, + GeometrySet input_geometry_set, + NodesModifierData *nmd, + const ModifierEvalContext *ctx) { - blender::ResourceScope scope; - blender::LinearAllocator<> &allocator = scope.linear_allocator(); - blender::nodes::NodeMultiFunctions mf_by_node{tree}; + const blender::nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping; + + Span<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets; + Vector<const lf::InputSocket *> graph_outputs; + for (const bNodeSocket *bsocket : output_node.input_sockets().drop_back(1)) { + const lf::InputSocket &socket = mapping.dummy_socket_map.lookup(bsocket)->as_input(); + graph_outputs.append(&socket); + } - Map<DOutputSocket, GMutablePointer> group_inputs; + Array<GMutablePointer> param_inputs(graph_inputs.size()); + Array<GMutablePointer> param_outputs(graph_outputs.size()); + Array<std::optional<lf::ValueUsage>> param_input_usages(graph_inputs.size()); + Array<lf::ValueUsage> param_output_usages(graph_outputs.size(), lf::ValueUsage::Used); + Array<bool> param_set_outputs(graph_outputs.size(), false); - const DTreeContext *root_context = &tree.root_context(); - for (const bNode *group_input_node : group_input_nodes) { - Span<const bNodeSocket *> group_input_sockets = group_input_node->output_sockets().drop_back( - 1); - if (group_input_sockets.is_empty()) { - continue; - } + blender::nodes::GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info); + blender::nodes::GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider( + lf_graph_info); - Span<const bNodeSocket *> remaining_input_sockets = group_input_sockets; + lf::GraphExecutor graph_executor{ + lf_graph_info.graph, graph_inputs, graph_outputs, &lf_logger, &lf_side_effect_provider}; - /* If the group expects a geometry as first input, use the geometry that has been passed to - * modifier. */ - const bNodeSocket *first_input_socket = group_input_sockets[0]; - if (first_input_socket->type == SOCK_GEOMETRY) { - GeometrySet *geometry_set_in = - allocator.construct<GeometrySet>(input_geometry_set).release(); - group_inputs.add_new({root_context, first_input_socket}, geometry_set_in); - remaining_input_sockets = remaining_input_sockets.drop_front(1); + blender::nodes::GeoNodesModifierData geo_nodes_modifier_data; + geo_nodes_modifier_data.depsgraph = ctx->depsgraph; + geo_nodes_modifier_data.self_object = ctx->object; + auto eval_log = std::make_unique<GeoModifierLog>(); + if (logging_enabled(ctx)) { + geo_nodes_modifier_data.eval_log = eval_log.get(); + } + MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes; + find_side_effect_nodes(*nmd, *ctx, btree, r_side_effect_nodes); + geo_nodes_modifier_data.side_effect_nodes = &r_side_effect_nodes; + blender::nodes::GeoNodesLFUserData user_data; + user_data.modifier_data = &geo_nodes_modifier_data; + blender::bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name}; + user_data.compute_context = &modifier_compute_context; + + blender::LinearAllocator<> allocator; + Vector<GMutablePointer> inputs_to_destruct; + + int input_index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, interface_socket, &btree.inputs, input_index) { + if (interface_socket->type == SOCK_GEOMETRY && input_index == 0) { + param_inputs[input_index] = &input_geometry_set; + continue; } - /* Initialize remaining group inputs. */ - for (const bNodeSocket *socket : remaining_input_sockets) { - const CPPType &cpp_type = *socket->typeinfo->geometry_nodes_cpp_type; - void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); - initialize_group_input(*nmd, *socket, value_in); - group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); - } + const CPPType *type = interface_socket->typeinfo->geometry_nodes_cpp_type; + BLI_assert(type != nullptr); + void *value = allocator.allocate(type->size(), type->alignment()); + initialize_group_input(*nmd, *interface_socket, input_index, value); + param_inputs[input_index] = {type, value}; + inputs_to_destruct.append({type, value}); } - Vector<DInputSocket> group_outputs; - for (const bNodeSocket *socket_ref : output_node.input_sockets().drop_back(1)) { - group_outputs.append({root_context, socket_ref}); + for (const int i : graph_outputs.index_range()) { + const lf::InputSocket &socket = *graph_outputs[i]; + const CPPType &type = socket.type(); + void *buffer = allocator.allocate(type.size(), type.alignment()); + param_outputs[i] = {type, buffer}; } - std::optional<geo_log::GeoLogger> geo_logger; - - blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params; - - if (logging_enabled(ctx)) { - Set<DSocket> preview_sockets; - find_sockets_to_preview(nmd, ctx, tree, preview_sockets); - eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end()); - geo_logger.emplace(std::move(preview_sockets)); + lf::Context lf_context; + lf_context.storage = graph_executor.init_storage(allocator); + lf_context.user_data = &user_data; + lf::BasicParams lf_params{graph_executor, + param_inputs, + param_outputs, + param_input_usages, + param_output_usages, + param_set_outputs}; + graph_executor.execute(lf_params, lf_context); + graph_executor.destruct_storage(lf_context.storage); - geo_logger->log_input_geometry(input_geometry_set); + for (GMutablePointer &ptr : inputs_to_destruct) { + ptr.destruct(); } - /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ - input_geometry_set.clear(); - - eval_params.input_values = group_inputs; - eval_params.output_sockets = group_outputs; - eval_params.mf_by_node = &mf_by_node; - eval_params.modifier_ = nmd; - eval_params.depsgraph = ctx->depsgraph; - eval_params.self_object = ctx->object; - eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; - blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); + GeometrySet output_geometry_set = std::move(*static_cast<GeometrySet *>(param_outputs[0].get())); + store_output_attributes(output_geometry_set, *nmd, output_node, param_outputs); - GeometrySet output_geometry_set = std::move(*eval_params.r_output_values[0].get<GeometrySet>()); - - if (geo_logger.has_value()) { - geo_logger->log_output_geometry(output_geometry_set); - NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(ctx->object, - &nmd->modifier); - clear_runtime_data(nmd_orig); - nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); + for (GMutablePointer &ptr : param_outputs) { + ptr.destruct(); } - store_output_attributes(output_geometry_set, *nmd, output_node, eval_params.r_output_values); - - for (GMutablePointer value : eval_params.r_output_values) { - value.destruct(); + if (logging_enabled(ctx)) { + NodesModifierData *nmd_orig = reinterpret_cast<NodesModifierData *>( + BKE_modifier_get_original(ctx->object, &nmd->modifier)); + delete static_cast<GeoModifierLog *>(nmd_orig->runtime_eval_log); + nmd_orig->runtime_eval_log = eval_log.release(); } return output_geometry_set; @@ -1225,27 +1276,18 @@ static void modifyGeometry(ModifierData *md, return; } + const bNodeTree &tree = *nmd->node_group; + tree.ensure_topology_cache(); check_property_socket_sync(ctx->object, md); - const bNodeTree &root_tree_ref = *nmd->node_group; - DerivedNodeTree tree{root_tree_ref}; - - if (tree.has_link_cycles()) { - BKE_modifier_set_error(ctx->object, md, "Node group has cycles"); - geometry_set.clear(); - return; - } - - Span<const bNode *> input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput"); - Span<const bNode *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput"); - if (output_nodes.size() != 1) { - BKE_modifier_set_error(ctx->object, md, "Node group must have a single output node"); + const bNode *output_node = tree.group_output_node(); + if (output_node == nullptr) { + BKE_modifier_set_error(ctx->object, md, "Node group must have a group output node"); geometry_set.clear(); return; } - const bNode &output_node = *output_nodes[0]; - Span<const bNodeSocket *> group_outputs = output_node.input_sockets().drop_back(1); + Span<const bNodeSocket *> group_outputs = output_node->input_sockets().drop_back(1); if (group_outputs.is_empty()) { BKE_modifier_set_error(ctx->object, md, "Node group must have an output socket"); geometry_set.clear(); @@ -1259,6 +1301,14 @@ static void modifyGeometry(ModifierData *md, return; } + const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info = + blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree); + if (lf_graph_info == nullptr) { + BKE_modifier_set_error(ctx->object, md, "Cannot evaluate node group"); + geometry_set.clear(); + return; + } + bool use_orig_index_verts = false; bool use_orig_index_edges = false; bool use_orig_index_polys = false; @@ -1270,7 +1320,7 @@ static void modifyGeometry(ModifierData *md, } geometry_set = compute_geometry( - tree, input_nodes, output_node, std::move(geometry_set), nmd, ctx); + tree, *lf_graph_info, *output_node, std::move(geometry_set), nmd, ctx); if (geometry_set.has_mesh()) { /* Add #CD_ORIGINDEX layers if they don't exist already. This is required because the @@ -1342,6 +1392,16 @@ static NodesModifierData *get_modifier_data(Main &bmain, return reinterpret_cast<NodesModifierData *>(md); } +static GeoTreeLog *get_root_tree_log(const NodesModifierData &nmd) +{ + if (nmd.runtime_eval_log == nullptr) { + return nullptr; + } + GeoModifierLog &modifier_log = *static_cast<GeoModifierLog *>(nmd.runtime_eval_log); + blender::bke::ModifierComputeContext compute_context{nullptr, nmd.modifier.name}; + return &modifier_log.get_tree_log(compute_context.hash()); +} + static void attribute_search_update_fn( const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first) { @@ -1350,27 +1410,52 @@ static void attribute_search_update_fn( if (nmd == nullptr) { return; } - const geo_log::ModifierLog *modifier_log = static_cast<const geo_log::ModifierLog *>( - nmd->runtime_eval_log); - if (modifier_log == nullptr) { + if (nmd->node_group == nullptr) { return; } - const geo_log::GeometryValueLog *geometry_log = data.is_output ? - modifier_log->output_geometry_log() : - modifier_log->input_geometry_log(); - if (geometry_log == nullptr) { + GeoTreeLog *tree_log = get_root_tree_log(*nmd); + if (tree_log == nullptr) { return; } + tree_log->ensure_existing_attributes(); + nmd->node_group->ensure_topology_cache(); - Span<GeometryAttributeInfo> infos = geometry_log->attributes(); - - /* The shared attribute search code expects a span of pointers, so convert to that. */ - Array<const GeometryAttributeInfo *> info_ptrs(infos.size()); - for (const int i : infos.index_range()) { - info_ptrs[i] = &infos[i]; + Vector<const bNodeSocket *> sockets_to_check; + if (data.is_output) { + for (const bNode *node : nmd->node_group->nodes_by_type("NodeGroupOutput")) { + for (const bNodeSocket *socket : node->input_sockets()) { + if (socket->type == SOCK_GEOMETRY) { + sockets_to_check.append(socket); + } + } + } + } + else { + for (const bNode *node : nmd->node_group->nodes_by_type("NodeGroupInput")) { + for (const bNodeSocket *socket : node->output_sockets()) { + if (socket->type == SOCK_GEOMETRY) { + sockets_to_check.append(socket); + } + } + } + } + Set<StringRef> names; + Vector<const GeometryAttributeInfo *> attributes; + for (const bNodeSocket *socket : sockets_to_check) { + const ValueLog *value_log = tree_log->find_socket_value_log(*socket); + if (value_log == nullptr) { + continue; + } + if (const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(value_log)) { + for (const GeometryAttributeInfo &attribute : geo_log->attributes) { + if (names.add(attribute.name)) { + attributes.append(&attribute); + } + } + } } blender::ui::attribute_search_add_items( - str, data.is_output, info_ptrs.as_span(), items, is_first); + str, data.is_output, attributes.as_span(), items, is_first); } static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) @@ -1401,8 +1486,7 @@ static void add_attribute_search_button(const bContext &C, const bNodeSocket &socket, const bool is_output) { - const geo_log::ModifierLog *log = static_cast<geo_log::ModifierLog *>(nmd.runtime_eval_log); - if (log == nullptr) { + if (nmd.runtime_eval_log == nullptr) { uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, "", ICON_NONE); return; } @@ -1627,15 +1711,14 @@ static void panel_draw(const bContext *C, Panel *panel) } /* Draw node warnings. */ - 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::Info) { - uiItemL(layout, warning.message.c_str(), ICON_ERROR); - } + GeoTreeLog *tree_log = get_root_tree_log(*nmd); + if (tree_log != nullptr) { + tree_log->ensure_node_warnings(); + for (const NodeWarning &warning : tree_log->all_warnings) { + if (warning.type != NodeWarningType::Info) { + uiItemL(layout, warning.message.c_str(), ICON_ERROR); } - }); + } } modifier_panel_end(layout, ptr); @@ -1672,17 +1755,14 @@ static void internal_dependencies_panel_draw(const bContext *UNUSED(C), Panel *p PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data); - if (nmd->runtime_eval_log == nullptr) { + GeoTreeLog *tree_log = get_root_tree_log(*nmd); + if (tree_log == nullptr) { return; } - const geo_log::ModifierLog &log = *static_cast<geo_log::ModifierLog *>(nmd->runtime_eval_log); - Map<std::string, eNamedAttrUsage> usage_by_attribute; - log.foreach_node_log([&](const geo_log::NodeLog &node_log) { - for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) { - usage_by_attribute.lookup_or_add_as(used_attribute.name, - used_attribute.usage) |= used_attribute.usage; - } - }); + + tree_log->ensure_used_named_attributes(); + const Map<std::string, NamedAttributeUsage> &usage_by_attribute = + tree_log->used_named_attributes; if (usage_by_attribute.is_empty()) { uiItemL(layout, IFACE_("No named attributes used"), ICON_INFO); @@ -1691,7 +1771,7 @@ static void internal_dependencies_panel_draw(const bContext *UNUSED(C), Panel *p struct NameWithUsage { StringRefNull name; - eNamedAttrUsage usage; + NamedAttributeUsage usage; }; Vector<NameWithUsage> sorted_used_attribute; @@ -1706,20 +1786,20 @@ static void internal_dependencies_panel_draw(const bContext *UNUSED(C), Panel *p for (const NameWithUsage &attribute : sorted_used_attribute) { const StringRefNull attribute_name = attribute.name; - const eNamedAttrUsage usage = attribute.usage; + const NamedAttributeUsage usage = attribute.usage; /* #uiLayoutRowWithHeading doesn't seem to work in this case. */ uiLayout *split = uiLayoutSplit(layout, 0.4f, false); std::stringstream ss; Vector<std::string> usages; - if ((usage & eNamedAttrUsage::Read) != eNamedAttrUsage::None) { + if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) { usages.append(TIP_("Read")); } - if ((usage & eNamedAttrUsage::Write) != eNamedAttrUsage::None) { + if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) { usages.append(TIP_("Write")); } - if ((usage & eNamedAttrUsage::Remove) != eNamedAttrUsage::None) { + if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) { usages.append(TIP_("Remove")); } for (const int i : usages.index_range()) { diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc deleted file mode 100644 index dd7c87ca499..00000000000 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ /dev/null @@ -1,1929 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "MOD_nodes_evaluator.hh" - -#include "BKE_node.h" -#include "BKE_type_conversions.hh" - -#include "NOD_geometry_exec.hh" -#include "NOD_socket_declarations.hh" - -#include "DEG_depsgraph_query.h" - -#include "FN_field.hh" -#include "FN_field_cpp_type.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" -#include "BLI_vector_set.hh" - -#include <chrono> - -namespace blender::modifiers::geometry_nodes { - -using fn::Field; -using fn::GField; -using fn::ValueOrField; -using fn::ValueOrFieldCPPType; -using nodes::GeoNodeExecParams; -using namespace fn::multi_function_types; - -enum class ValueUsage : uint8_t { - /* The value is definitely used. */ - Required, - /* The value may be used. */ - Maybe, - /* The value will definitely not be used. */ - Unused, -}; - -struct SingleInputValue { - /** - * Points either to null or to a value of the type of input. - */ - void *value = nullptr; -}; - -struct MultiInputValue { - /** - * Ordered sockets connected to this multi-input. - */ - Vector<DSocket> origins; - /** - * A value for every origin socket. The order is determined by #origins. - * Note, the same origin can occur multiple times. However, it is guaranteed that values coming - * from the same origin have the same value (the pointer is different, but they point to values - * that would compare equal). - */ - Vector<void *> values; - /** - * Number of non-null values. - */ - int provided_value_count = 0; - - bool all_values_available() const - { - return this->missing_values() == 0; - } - - int missing_values() const - { - return this->values.size() - this->provided_value_count; - } - - void add_value(const DSocket origin, void *value) - { - const int index = this->find_available_index(origin); - this->values[index] = value; - this->provided_value_count++; - } - - private: - int find_available_index(DSocket origin) const - { - for (const int i : origins.index_range()) { - if (values[i] != nullptr) { - continue; - } - if (origins[i] != origin) { - continue; - } - return i; - } - BLI_assert_unreachable(); - return -1; - } -}; - -struct InputState { - - /** - * Type of the socket. If this is null, the socket should just be ignored. - */ - const CPPType *type = nullptr; - - /** - * Value of this input socket. By default, the value is empty. When other nodes are done - * computing their outputs, the computed values will be forwarded to linked input sockets. - * The value will then live here until it is consumed by the node or it was found that the value - * is not needed anymore. - * Whether the `single` or `multi` value is used depends on the socket. - */ - union { - SingleInputValue *single; - MultiInputValue *multi; - } value; - - /** - * How the node intends to use this input. By default all inputs may be used. Based on which - * outputs are used, a node can tell the evaluator that an input will definitely be used or is - * never used. This allows the evaluator to free values early, avoid copies and other unnecessary - * computations. - */ - ValueUsage usage = ValueUsage::Maybe; - - /** - * True when this input is/was used for an execution. While a node is running, only the inputs - * that have this set to true are allowed to be used. This makes sure that inputs created while - * the node is running correctly trigger the node to run again. Furthermore, it gives the node a - * consistent view of which inputs are available that does not change unexpectedly. - * - * While the node is running, this can be checked without a lock, because no one is writing to - * it. If this is true, the value can be read without a lock as well, because the value is not - * changed by others anymore. - */ - bool was_ready_for_execution = false; - - /** - * True when this input has to be computed for logging/debugging purposes, regardless of whether - * it is needed for some output. - */ - bool force_compute = false; -}; - -struct OutputState { - /** - * If this output has been computed and forwarded already. If this is true, the value is not - * computed/forwarded again. - */ - bool has_been_computed = false; - - /** - * Keeps track of how the output value is used. If a connected input becomes required, this - * output has to become required as well. The output becomes ignored when it has zero potential - * users that are counted below. - */ - ValueUsage output_usage = ValueUsage::Maybe; - - /** - * This is a copy of `output_usage` that is done right before node execution starts. This is - * done so that the node gets a consistent view of what outputs are used, even when this changes - * while the node is running (the node might be reevaluated in that case). - * - * While the node is running, this can be checked without a lock, because no one is writing to - * it. - */ - ValueUsage output_usage_for_execution = ValueUsage::Maybe; - - /** - * Counts how many times the value from this output might be used. If this number reaches zero, - * the output is not needed anymore. - */ - int potential_users = 0; -}; - -enum class NodeScheduleState { - /** - * Default state of every node. - */ - NotScheduled, - /** - * The node has been added to the task group and will be executed by it in the future. - */ - Scheduled, - /** - * The node is currently running. - */ - Running, - /** - * The node is running and has been rescheduled while running. In this case the node will run - * again. However, we don't add it to the task group immediately, because then the node might run - * twice at the same time, which is not allowed. Instead, once the node is done running, it will - * reschedule itself. - */ - RunningAndRescheduled, -}; - -struct NodeState { - /** - * Needs to be locked when any data in this state is accessed that is not explicitly marked as - * otherwise. - */ - std::mutex mutex; - - /** - * States of the individual input and output sockets. One can index into these arrays without - * locking. However, to access the data inside a lock is generally necessary. - * - * These spans have to be indexed with the socket index. Unavailable sockets have a state as - * well. Maybe we can handle unavailable sockets differently in Blender in general, so I did not - * want to add complexity around it here. - */ - MutableSpan<InputState> inputs; - MutableSpan<OutputState> outputs; - - /** - * Most nodes have inputs that are always required. Those have special handling to avoid an extra - * call to the node execution function. - */ - bool non_lazy_inputs_handled = false; - - /** - * Used to check that nodes that don't support laziness do not run more than once. - */ - bool has_been_executed = false; - - /** - * Becomes true when the node will never be executed again and its inputs are destructed. - * Generally, a node has finished once all of its outputs with (potential) users have been - * computed. - */ - bool node_has_finished = false; - - /** - * Counts the number of values that still have to be forwarded to this node until it should run - * again. It counts values from a multi input socket separately. - * This is used as an optimization so that nodes are not scheduled unnecessarily in many cases. - */ - int missing_required_inputs = 0; - - /** - * A node is always in one specific schedule state. This helps to ensure that the same node does - * not run twice at the same time accidentally. - */ - NodeScheduleState schedule_state = NodeScheduleState::NotScheduled; -}; - -/** - * Container for a node and its state. Packing them into a single struct allows the use of - * `VectorSet` instead of a `Map` for `node_states_` which simplifies parallel loops over all - * states. - * - * Equality operators and a hash function for `DNode` are provided so that one can lookup this type - * in `node_states_` just with a `DNode`. - */ -struct NodeWithState { - DNode node; - /* Store a pointer instead of `NodeState` directly to keep it small and movable. */ - NodeState *state = nullptr; - - friend bool operator==(const NodeWithState &a, const NodeWithState &b) - { - return a.node == b.node; - } - - friend bool operator==(const NodeWithState &a, const DNode &b) - { - return a.node == b; - } - - friend bool operator==(const DNode &a, const NodeWithState &b) - { - return a == b.node; - } - - uint64_t hash() const - { - return node.hash(); - } - - static uint64_t hash_as(const DNode &node) - { - return node.hash(); - } -}; - -class GeometryNodesEvaluator; - -/** - * Utility class that wraps a node whose state is locked. Having this is a separate class is useful - * because it allows methods to communicate that they expect the node to be locked. - */ -class LockedNode : NonCopyable, NonMovable { - public: - /** - * This is the node that is currently locked. - */ - const DNode node; - NodeState &node_state; - - /** - * Used to delay notifying (and therefore locking) other nodes until the current node is not - * locked anymore. This might not be strictly necessary to avoid deadlocks in the current code, - * but it is a good measure to avoid accidentally adding a deadlock later on. By not locking - * more than one node per thread at a time, deadlocks are avoided. - * - * The notifications will be send right after the node is not locked anymore. - */ - Vector<DOutputSocket> delayed_required_outputs; - Vector<DOutputSocket> delayed_unused_outputs; - Vector<DNode> delayed_scheduled_nodes; - - LockedNode(const DNode node, NodeState &node_state) : node(node), node_state(node_state) - { - } -}; - -static const CPPType *get_socket_cpp_type(const bNodeSocket &socket) -{ - const bNodeSocketType *typeinfo = socket.typeinfo; - if (typeinfo->geometry_nodes_cpp_type == nullptr) { - return nullptr; - } - const CPPType *type = typeinfo->geometry_nodes_cpp_type; - if (type == nullptr) { - return nullptr; - } - /* The evaluator only supports types that have special member functions. */ - if (!type->has_special_member_functions()) { - return nullptr; - } - return type; -} - -static const CPPType *get_socket_cpp_type(const DSocket socket) -{ - return get_socket_cpp_type(*socket); -} - -/** - * \note This is not supposed to be a long term solution. Eventually we want that nodes can - * specify more complex defaults (other than just single values) in their socket declarations. - */ -static bool get_implicit_socket_input(const bNodeSocket &socket, void *r_value) -{ - const bNode &node = socket.owner_node(); - const nodes::NodeDeclaration *node_declaration = node.runtime->declaration; - if (node_declaration == nullptr) { - return false; - } - const nodes::SocketDeclaration &socket_declaration = *node_declaration->inputs()[socket.index()]; - if (socket_declaration.input_field_type() == nodes::InputSocketFieldType::Implicit) { - const bNode &bnode = socket.owner_node(); - if (socket.typeinfo->type == SOCK_VECTOR) { - if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { - StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == - GEO_NODE_CURVE_HANDLE_LEFT ? - "handle_left" : - "handle_right"; - new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side)); - return true; - } - if (bnode.type == GEO_NODE_EXTRUDE_MESH) { - new (r_value) - ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>())); - return true; - } - new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position")); - return true; - } - if (socket.typeinfo->type == SOCK_INT) { - if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { - new (r_value) - ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>())); - return true; - } - new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>())); - return true; - } - } - return false; -} - -static void get_socket_value(const bNodeSocket &socket, void *r_value) -{ - if (get_implicit_socket_input(socket, r_value)) { - return; - } - - const bNodeSocketType *typeinfo = socket.typeinfo; - typeinfo->get_geometry_nodes_cpp_value(socket, r_value); -} - -static bool node_supports_laziness(const DNode node) -{ - return node->typeinfo->geometry_node_execute_supports_laziness; -} - -struct NodeTaskRunState { - /** The node that should be run on the same thread after the current node finished. */ - DNode next_node_to_run; -}; - -/** Implements the callbacks that might be called when a node is executed. */ -class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider { - private: - GeometryNodesEvaluator &evaluator_; - NodeState &node_state_; - NodeTaskRunState *run_state_; - - public: - NodeParamsProvider(GeometryNodesEvaluator &evaluator, - DNode dnode, - NodeState &node_state, - NodeTaskRunState *run_state); - - bool can_get_input(StringRef identifier) const override; - bool can_set_output(StringRef identifier) const override; - GMutablePointer extract_input(StringRef identifier) override; - Vector<GMutablePointer> extract_multi_input(StringRef identifier) override; - GPointer get_input(StringRef identifier) const override; - GMutablePointer alloc_output_value(const CPPType &type) override; - void set_output(StringRef identifier, GMutablePointer value) override; - void set_input_unused(StringRef identifier) override; - bool output_is_required(StringRef identifier) const override; - - bool lazy_require_input(StringRef identifier) override; - bool lazy_output_is_required(StringRef identifier) const override; - - void set_default_remaining_outputs() override; -}; - -class GeometryNodesEvaluator { - private: - /** - * This allocator lives on after the evaluator has been destructed. Therefore outputs of the - * entire evaluator should be allocated here. - */ - LinearAllocator<> &outer_allocator_; - /** - * A local linear allocator for each thread. Only use this for values that do not need to live - * longer than the lifetime of the evaluator itself. Considerations for the future: - * - We could use an allocator that can free here, some temporary values don't live long. - * - If we ever run into false sharing bottlenecks, we could use local allocators that allocate - * on cache line boundaries. Note, just because a value is allocated in one specific thread, - * does not mean that it will only be used by that thread. - */ - threading::EnumerableThreadSpecific<LinearAllocator<>> local_allocators_; - - /** - * Every node that is reachable from the output gets its own state. Once all states have been - * constructed, this map can be used for lookups from multiple threads. - */ - VectorSet<NodeWithState> node_states_; - - /** - * Contains all the tasks for the nodes that are currently scheduled. - */ - TaskPool *task_pool_ = nullptr; - - GeometryNodesEvaluationParams ¶ms_; - const blender::bke::DataTypeConversions &conversions_; - - friend NodeParamsProvider; - - public: - GeometryNodesEvaluator(GeometryNodesEvaluationParams ¶ms) - : outer_allocator_(params.allocator), - params_(params), - conversions_(blender::bke::get_implicit_type_conversions()) - { - } - - void execute() - { - task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH); - - this->create_states_for_reachable_nodes(); - this->forward_group_inputs(); - this->schedule_initial_nodes(); - - /* This runs until all initially requested inputs have been computed. */ - BLI_task_pool_work_and_wait(task_pool_); - BLI_task_pool_free(task_pool_); - - this->extract_group_outputs(); - this->destruct_node_states(); - } - - void create_states_for_reachable_nodes() - { - /* This does a depth first search for all the nodes that are reachable from the group - * outputs. This finds all nodes that are relevant. */ - Stack<DNode> nodes_to_check; - /* Start at the output sockets. */ - for (const DInputSocket &socket : params_.output_sockets) { - nodes_to_check.push(socket.node()); - } - for (const DSocket &socket : params_.force_compute_sockets) { - nodes_to_check.push(socket.node()); - } - /* Use the local allocator because the states do not need to outlive the evaluator. */ - LinearAllocator<> &allocator = local_allocators_.local(); - while (!nodes_to_check.is_empty()) { - const DNode node = nodes_to_check.pop(); - if (node_states_.contains_as(node)) { - /* This node has been handled already. */ - continue; - } - /* Create a new state for the node. */ - NodeState &node_state = *allocator.construct<NodeState>().release(); - node_states_.add_new({node, &node_state}); - - /* Push all linked origins on the stack. */ - for (const bNodeSocket *input : node->input_sockets()) { - const DInputSocket dinput{node.context(), input}; - dinput.foreach_origin_socket( - [&](const DSocket origin) { nodes_to_check.push(origin.node()); }); - } - } - - /* Initialize the more complex parts of the node states in parallel. At this point no new - * node states are added anymore, so it is safe to lookup states from `node_states_` from - * multiple threads. */ - threading::parallel_for( - IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { - LinearAllocator<> &allocator = this->local_allocators_.local(); - for (const NodeWithState &item : node_states_.as_span().slice(range)) { - this->initialize_node_state(item.node, *item.state, allocator); - } - }); - - /* Mark input sockets that have to be computed. */ - for (const DSocket &socket : params_.force_compute_sockets) { - NodeState &node_state = *node_states_.lookup_key_as(socket.node()).state; - if (socket->is_input()) { - node_state.inputs[socket->index()].force_compute = true; - } - } - } - - void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator) - { - /* Construct arrays of the correct size. */ - node_state.inputs = allocator.construct_array<InputState>(node->input_sockets().size()); - node_state.outputs = allocator.construct_array<OutputState>(node->output_sockets().size()); - - /* Initialize input states. */ - for (const int i : node->input_sockets().index_range()) { - InputState &input_state = node_state.inputs[i]; - const DInputSocket socket = node.input(i); - if (!socket->is_available()) { - /* Unavailable sockets should never be used. */ - input_state.type = nullptr; - input_state.usage = ValueUsage::Unused; - continue; - } - const CPPType *type = get_socket_cpp_type(socket); - input_state.type = type; - if (type == nullptr) { - /* This is not a known data socket, it shouldn't be used. */ - input_state.usage = ValueUsage::Unused; - continue; - } - /* Construct the correct struct that can hold the input(s). */ - if (socket->is_multi_input()) { - input_state.value.multi = allocator.construct<MultiInputValue>().release(); - MultiInputValue &multi_value = *input_state.value.multi; - /* Count how many values should be added until the socket is complete. */ - socket.foreach_origin_socket([&](DSocket origin) { multi_value.origins.append(origin); }); - /* If no links are connected, we do read the value from socket itself. */ - if (multi_value.origins.is_empty()) { - multi_value.origins.append(socket); - } - multi_value.values.resize(multi_value.origins.size(), nullptr); - } - else { - input_state.value.single = allocator.construct<SingleInputValue>().release(); - } - } - /* Initialize output states. */ - for (const int i : node->output_sockets().index_range()) { - OutputState &output_state = node_state.outputs[i]; - const DOutputSocket socket = node.output(i); - if (!socket->is_available()) { - /* Unavailable outputs should never be used. */ - output_state.output_usage = ValueUsage::Unused; - continue; - } - const CPPType *type = get_socket_cpp_type(socket); - if (type == nullptr) { - /* Non data sockets should never be used. */ - output_state.output_usage = ValueUsage::Unused; - continue; - } - /* Count the number of potential users for this socket. */ - socket.foreach_target_socket( - [&, this](const DInputSocket target_socket, - const DOutputSocket::TargetSocketPathInfo &UNUSED(path_info)) { - const DNode target_node = target_socket.node(); - if (!this->node_states_.contains_as(target_node)) { - /* The target node is not computed because it is not computed to the output. */ - return; - } - output_state.potential_users += 1; - }); - if (output_state.potential_users == 0) { - /* If it does not have any potential users, it is unused. It might become required again in - * `schedule_initial_nodes`. */ - output_state.output_usage = ValueUsage::Unused; - } - } - } - - void destruct_node_states() - { - threading::parallel_for( - IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) { - for (const NodeWithState &item : node_states_.as_span().slice(range)) { - this->destruct_node_state(item.node, *item.state); - } - }); - } - - void destruct_node_state(const DNode node, NodeState &node_state) - { - /* Need to destruct stuff manually, because it's allocated by a custom allocator. */ - for (const int i : node->input_sockets().index_range()) { - InputState &input_state = node_state.inputs[i]; - if (input_state.type == nullptr) { - continue; - } - const bNodeSocket &bsocket = node->input_socket(i); - if (bsocket.is_multi_input()) { - MultiInputValue &multi_value = *input_state.value.multi; - for (void *value : multi_value.values) { - if (value != nullptr) { - input_state.type->destruct(value); - } - } - multi_value.~MultiInputValue(); - } - else { - SingleInputValue &single_value = *input_state.value.single; - void *value = single_value.value; - if (value != nullptr) { - input_state.type->destruct(value); - } - single_value.~SingleInputValue(); - } - } - - destruct_n(node_state.inputs.data(), node_state.inputs.size()); - destruct_n(node_state.outputs.data(), node_state.outputs.size()); - - node_state.~NodeState(); - } - - void forward_group_inputs() - { - for (auto &&item : params_.input_values.items()) { - const DOutputSocket socket = item.key; - GMutablePointer value = item.value; - - const DNode node = socket.node(); - if (!node_states_.contains_as(node)) { - /* The socket is not connected to any output. */ - this->log_socket_value({socket}, value); - value.destruct(); - continue; - } - this->forward_output(socket, value, nullptr); - } - } - - void schedule_initial_nodes() - { - for (const DInputSocket &socket : params_.output_sockets) { - const DNode node = socket.node(); - NodeState &node_state = this->get_node_state(node); - this->with_locked_node(node, node_state, nullptr, [&](LockedNode &locked_node) { - /* Setting an input as required will schedule any linked node. */ - this->set_input_required(locked_node, socket); - }); - } - for (const DSocket socket : params_.force_compute_sockets) { - const DNode node = socket.node(); - NodeState &node_state = this->get_node_state(node); - this->with_locked_node(node, node_state, nullptr, [&](LockedNode &locked_node) { - if (socket->is_input()) { - this->set_input_required(locked_node, DInputSocket(socket)); - } - else { - OutputState &output_state = node_state.outputs[socket->index()]; - output_state.output_usage = ValueUsage::Required; - this->schedule_node(locked_node); - } - }); - } - } - - void schedule_node(LockedNode &locked_node) - { - switch (locked_node.node_state.schedule_state) { - case NodeScheduleState::NotScheduled: { - /* The node will be scheduled once it is not locked anymore. We could schedule the node - * right here, but that would result in a deadlock if the task pool decides to run the task - * immediately (this only happens when Blender is started with a single thread). */ - locked_node.node_state.schedule_state = NodeScheduleState::Scheduled; - locked_node.delayed_scheduled_nodes.append(locked_node.node); - break; - } - case NodeScheduleState::Scheduled: { - /* Scheduled already, nothing to do. */ - break; - } - case NodeScheduleState::Running: { - /* Reschedule node while it is running. - * The node will reschedule itself when it is done. */ - locked_node.node_state.schedule_state = NodeScheduleState::RunningAndRescheduled; - break; - } - case NodeScheduleState::RunningAndRescheduled: { - /* Scheduled already, nothing to do. */ - break; - } - } - } - - static void run_node_from_task_pool(TaskPool *task_pool, void *task_data) - { - void *user_data = BLI_task_pool_user_data(task_pool); - GeometryNodesEvaluator &evaluator = *(GeometryNodesEvaluator *)user_data; - const NodeWithState *root_node_with_state = (const NodeWithState *)task_data; - - /* First, the node provided by the task pool is executed. During the execution other nodes - * might be scheduled. One of those nodes is not added to the task pool but is executed in the - * loop below directly. This has two main benefits: - * - Fewer round trips through the task pool which add threading overhead. - * - Helps with cpu cache efficiency, because a thread is more likely to process data that it - * has processed shortly before. - */ - DNode next_node_to_run = root_node_with_state->node; - while (next_node_to_run) { - NodeTaskRunState run_state; - evaluator.node_task_run(next_node_to_run, &run_state); - next_node_to_run = run_state.next_node_to_run; - } - } - - void node_task_run(const DNode node, NodeTaskRunState *run_state) - { - /* These nodes are sometimes scheduled. We could also check for them in other places, but - * it's the easiest to do it here. */ - if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { - return; - } - - NodeState &node_state = *node_states_.lookup_key_as(node).state; - - const bool do_execute_node = this->node_task_preprocessing(node, node_state, run_state); - - /* Only execute the node if all prerequisites are met. There has to be an output that is - * required and all required inputs have to be provided already. */ - if (do_execute_node) { - this->execute_node(node, node_state, run_state); - } - - this->node_task_postprocessing(node, node_state, do_execute_node, run_state); - } - - bool node_task_preprocessing(const DNode node, - NodeState &node_state, - NodeTaskRunState *run_state) - { - bool do_execute_node = false; - this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { - BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled); - node_state.schedule_state = NodeScheduleState::Running; - - /* Early return if the node has finished already. */ - if (locked_node.node_state.node_has_finished) { - return; - } - /* Prepare outputs and check if actually any new outputs have to be computed. */ - if (!this->prepare_node_outputs_for_execution(locked_node)) { - return; - } - /* Initialize inputs that don't support laziness. This is done after at least one output is - * required and before we check that all required inputs are provided. This reduces the - * number of "round-trips" through the task pool by one for most nodes. */ - if (!node_state.non_lazy_inputs_handled) { - this->require_non_lazy_inputs(locked_node); - node_state.non_lazy_inputs_handled = true; - } - /* Prepare inputs and check if all required inputs are provided. */ - if (!this->prepare_node_inputs_for_execution(locked_node)) { - return; - } - do_execute_node = true; - }); - return do_execute_node; - } - - /* A node is finished when it has computed all outputs that may be used have been computed and - * when no input is still forced to be computed. */ - bool finish_node_if_possible(LockedNode &locked_node) - { - if (locked_node.node_state.node_has_finished) { - /* Early return in case this node is known to have finished already. */ - return true; - } - - /* Check if there is any output that might be used but has not been computed yet. */ - for (OutputState &output_state : locked_node.node_state.outputs) { - if (output_state.has_been_computed) { - continue; - } - if (output_state.output_usage != ValueUsage::Unused) { - return false; - } - } - - /* Check if there is an input that still has to be computed. */ - for (InputState &input_state : locked_node.node_state.inputs) { - if (input_state.force_compute) { - if (!input_state.was_ready_for_execution) { - return false; - } - } - } - - /* If there are no remaining outputs, all the inputs can be destructed and/or can become - * unused. This can also trigger a chain reaction where nodes to the left become finished - * too. */ - for (const int i : locked_node.node->input_sockets().index_range()) { - const DInputSocket socket = locked_node.node.input(i); - InputState &input_state = locked_node.node_state.inputs[i]; - if (input_state.usage == ValueUsage::Maybe) { - this->set_input_unused(locked_node, socket); - } - else if (input_state.usage == ValueUsage::Required) { - /* The value was required, so it cannot become unused. However, we can destruct the - * value. */ - this->destruct_input_value_if_exists(locked_node, socket); - } - } - locked_node.node_state.node_has_finished = true; - return true; - } - - bool prepare_node_outputs_for_execution(LockedNode &locked_node) - { - bool execution_is_necessary = false; - for (OutputState &output_state : locked_node.node_state.outputs) { - /* Update the output usage for execution to the latest value. */ - output_state.output_usage_for_execution = output_state.output_usage; - if (!output_state.has_been_computed) { - if (output_state.output_usage == ValueUsage::Required) { - /* Only evaluate when there is an output that is required but has not been computed. */ - execution_is_necessary = true; - } - } - } - return execution_is_necessary; - } - - void require_non_lazy_inputs(LockedNode &locked_node) - { - this->foreach_non_lazy_input(locked_node, [&](const DInputSocket socket) { - this->set_input_required(locked_node, socket); - }); - } - - void foreach_non_lazy_input(LockedNode &locked_node, FunctionRef<void(DInputSocket socket)> fn) - { - if (node_supports_laziness(locked_node.node)) { - /* In the future only some of the inputs may support laziness. */ - return; - } - /* Nodes that don't support laziness require all inputs. */ - for (const int i : locked_node.node->input_sockets().index_range()) { - InputState &input_state = locked_node.node_state.inputs[i]; - if (input_state.type == nullptr) { - /* Ignore unavailable/non-data sockets. */ - continue; - } - fn(locked_node.node.input(i)); - } - } - - /** - * Checks if requested inputs are available and "marks" all the inputs that are available - * during the node execution. Inputs that are provided after this function ends but before the - * node is executed, cannot be read by the node in the execution (note that this only affects - * nodes that support lazy inputs). - */ - bool prepare_node_inputs_for_execution(LockedNode &locked_node) - { - for (const int i : locked_node.node_state.inputs.index_range()) { - InputState &input_state = locked_node.node_state.inputs[i]; - if (input_state.type == nullptr) { - /* Ignore unavailable and non-data sockets. */ - continue; - } - const DInputSocket socket = locked_node.node.input(i); - const bool is_required = input_state.usage == ValueUsage::Required; - - /* No need to check this socket again. */ - if (input_state.was_ready_for_execution) { - continue; - } - - if (socket->is_multi_input()) { - MultiInputValue &multi_value = *input_state.value.multi; - /* Checks if all the linked sockets have been provided already. */ - if (multi_value.all_values_available()) { - input_state.was_ready_for_execution = true; - } - else if (is_required) { - /* The input is required but is not fully provided yet. Therefore the node cannot be - * executed yet. */ - return false; - } - } - else { - SingleInputValue &single_value = *input_state.value.single; - if (single_value.value != nullptr) { - input_state.was_ready_for_execution = true; - } - else if (is_required) { - /* The input is required but has not been provided yet. Therefore the node cannot be - * executed yet. */ - return false; - } - } - } - /* All required inputs have been provided. */ - return true; - } - - /** - * Actually execute the node. All the required inputs are available and at least one output is - * required. - */ - void execute_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state) - { - const bNode &bnode = *node; - - if (node_state.has_been_executed) { - if (!node_supports_laziness(node)) { - /* Nodes that don't support laziness must not be executed more than once. */ - BLI_assert_unreachable(); - } - } - node_state.has_been_executed = true; - - /* Use the geometry node execute callback if it exists. */ - if (bnode.typeinfo->geometry_node_execute != nullptr) { - this->execute_geometry_node(node, node_state, run_state); - return; - } - - /* Use the multi-function implementation if it exists. */ - const nodes::NodeMultiFunctions::Item &fn_item = params_.mf_by_node->try_get(node); - if (fn_item.fn != nullptr) { - this->execute_multi_function_node(node, fn_item, node_state, run_state); - return; - } - - this->execute_unknown_node(node, node_state, run_state); - } - - void execute_geometry_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state) - { - using Clock = std::chrono::steady_clock; - const bNode &bnode = *node; - - NodeParamsProvider params_provider{*this, node, node_state, run_state}; - GeoNodeExecParams params{params_provider}; - Clock::time_point begin = Clock::now(); - bnode.typeinfo->geometry_node_execute(params); - Clock::time_point end = Clock::now(); - const std::chrono::microseconds duration = - std::chrono::duration_cast<std::chrono::microseconds>(end - begin); - if (params_.geo_logger != nullptr) { - params_.geo_logger->local().log_execution_time(node, duration); - } - } - - void execute_multi_function_node(const DNode node, - const nodes::NodeMultiFunctions::Item &fn_item, - NodeState &node_state, - NodeTaskRunState *run_state) - { - LinearAllocator<> &allocator = local_allocators_.local(); - - bool any_input_is_field = false; - Vector<const void *, 16> input_values; - Vector<const ValueOrFieldCPPType *, 16> input_types; - for (const int i : node->input_sockets().index_range()) { - const bNodeSocket &bsocket = node->input_socket(i); - if (!bsocket.is_available()) { - continue; - } - BLI_assert(!bsocket.is_multi_input()); - InputState &input_state = node_state.inputs[i]; - BLI_assert(input_state.was_ready_for_execution); - SingleInputValue &single_value = *input_state.value.single; - BLI_assert(single_value.value != nullptr); - const ValueOrFieldCPPType &field_cpp_type = static_cast<const ValueOrFieldCPPType &>( - *input_state.type); - input_values.append(single_value.value); - input_types.append(&field_cpp_type); - if (field_cpp_type.is_field(single_value.value)) { - any_input_is_field = true; - } - } - - if (any_input_is_field) { - this->execute_multi_function_node__field( - node, fn_item, node_state, allocator, input_values, input_types, run_state); - } - else { - this->execute_multi_function_node__value( - node, *fn_item.fn, node_state, allocator, input_values, input_types, run_state); - } - } - - void execute_multi_function_node__field(const DNode node, - const nodes::NodeMultiFunctions::Item &fn_item, - NodeState &node_state, - LinearAllocator<> &allocator, - Span<const void *> input_values, - Span<const ValueOrFieldCPPType *> input_types, - NodeTaskRunState *run_state) - { - Vector<GField> input_fields; - for (const int i : input_values.index_range()) { - const void *input_value_or_field = input_values[i]; - const ValueOrFieldCPPType &field_cpp_type = *input_types[i]; - input_fields.append(field_cpp_type.as_field(input_value_or_field)); - } - - std::shared_ptr<fn::FieldOperation> operation; - if (fn_item.owned_fn) { - operation = std::make_shared<fn::FieldOperation>(fn_item.owned_fn, std::move(input_fields)); - } - else { - operation = std::make_shared<fn::FieldOperation>(*fn_item.fn, std::move(input_fields)); - } - - int output_index = 0; - for (const int i : node->output_sockets().index_range()) { - const bNodeSocket &bsocket = node->output_socket(i); - if (!bsocket.is_available()) { - continue; - } - OutputState &output_state = node_state.outputs[i]; - const DOutputSocket socket{node.context(), &bsocket}; - const ValueOrFieldCPPType *cpp_type = static_cast<const ValueOrFieldCPPType *>( - get_socket_cpp_type(bsocket)); - GField new_field{operation, output_index}; - void *buffer = allocator.allocate(cpp_type->size(), cpp_type->alignment()); - cpp_type->construct_from_field(buffer, std::move(new_field)); - this->forward_output(socket, {cpp_type, buffer}, run_state); - output_state.has_been_computed = true; - output_index++; - } - } - - void execute_multi_function_node__value(const DNode node, - const MultiFunction &fn, - NodeState &node_state, - LinearAllocator<> &allocator, - Span<const void *> input_values, - Span<const ValueOrFieldCPPType *> input_types, - NodeTaskRunState *run_state) - { - MFParamsBuilder params{fn, 1}; - for (const int i : input_values.index_range()) { - const void *input_value_or_field = input_values[i]; - const ValueOrFieldCPPType &field_cpp_type = *input_types[i]; - const CPPType &base_type = field_cpp_type.base_type(); - const void *input_value = field_cpp_type.get_value_ptr(input_value_or_field); - params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, input_value)); - } - - Vector<GMutablePointer, 16> output_buffers; - for (const int i : node->output_sockets().index_range()) { - const DOutputSocket socket = node.output(i); - if (!socket->is_available()) { - output_buffers.append({}); - continue; - } - const ValueOrFieldCPPType *value_or_field_type = static_cast<const ValueOrFieldCPPType *>( - get_socket_cpp_type(socket)); - const CPPType &base_type = value_or_field_type->base_type(); - void *value_or_field_buffer = allocator.allocate(value_or_field_type->size(), - value_or_field_type->alignment()); - value_or_field_type->default_construct(value_or_field_buffer); - void *value_buffer = value_or_field_type->get_value_ptr(value_or_field_buffer); - base_type.destruct(value_buffer); - params.add_uninitialized_single_output(GMutableSpan{base_type, value_buffer, 1}); - output_buffers.append({value_or_field_type, value_or_field_buffer}); - } - - MFContextBuilder context; - fn.call(IndexRange(1), params, context); - - for (const int i : output_buffers.index_range()) { - GMutablePointer buffer = output_buffers[i]; - if (buffer.get() == nullptr) { - continue; - } - const DOutputSocket socket = node.output(i); - this->forward_output(socket, buffer, run_state); - - OutputState &output_state = node_state.outputs[i]; - output_state.has_been_computed = true; - } - } - - void execute_unknown_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state) - { - LinearAllocator<> &allocator = local_allocators_.local(); - for (const bNodeSocket *socket : node->output_sockets()) { - if (!socket->is_available()) { - continue; - } - const CPPType *type = get_socket_cpp_type(*socket); - if (type == nullptr) { - continue; - } - /* Just forward the default value of the type as a fallback. That's typically better than - * crashing or doing nothing. */ - OutputState &output_state = node_state.outputs[socket->index()]; - output_state.has_been_computed = true; - void *buffer = allocator.allocate(type->size(), type->alignment()); - this->construct_default_value(*type, buffer); - this->forward_output({node.context(), socket}, {*type, buffer}, run_state); - } - } - - void node_task_postprocessing(const DNode node, - NodeState &node_state, - bool was_executed, - NodeTaskRunState *run_state) - { - this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { - const bool node_has_finished = this->finish_node_if_possible(locked_node); - const bool reschedule_requested = node_state.schedule_state == - NodeScheduleState::RunningAndRescheduled; - node_state.schedule_state = NodeScheduleState::NotScheduled; - if (reschedule_requested && !node_has_finished) { - /* Either the node rescheduled itself or another node tried to schedule it while it ran. */ - this->schedule_node(locked_node); - } - if (was_executed) { - this->assert_expected_outputs_have_been_computed(locked_node); - } - }); - } - - void assert_expected_outputs_have_been_computed(LockedNode &locked_node) - { -#ifdef DEBUG - /* Outputs can only be computed when all required inputs have been provided. */ - if (locked_node.node_state.missing_required_inputs > 0) { - return; - } - /* If the node is still scheduled, it is not necessary that all its expected outputs are - * computed yet. */ - if (locked_node.node_state.schedule_state == NodeScheduleState::Scheduled) { - return; - } - - const bool supports_laziness = node_supports_laziness(locked_node.node); - /* Iterating over sockets instead of the states directly, because that makes it easier to - * figure out which socket is missing when one of the asserts is hit. */ - for (const bNodeSocket *bsocket : locked_node.node->output_sockets()) { - OutputState &output_state = locked_node.node_state.outputs[bsocket->index()]; - if (supports_laziness) { - /* Expected that at least all required sockets have been computed. If more outputs become - * required later, the node will be executed again. */ - if (output_state.output_usage_for_execution == ValueUsage::Required) { - BLI_assert(output_state.has_been_computed); - } - } - else { - /* Expect that all outputs that may be used have been computed, because the node cannot - * be executed again. */ - if (output_state.output_usage_for_execution != ValueUsage::Unused) { - BLI_assert(output_state.has_been_computed); - } - } - } -#else - UNUSED_VARS(locked_node); -#endif - } - - void extract_group_outputs() - { - for (const DInputSocket &socket : params_.output_sockets) { - BLI_assert(socket->is_available()); - BLI_assert(!socket->is_multi_input()); - - const DNode node = socket.node(); - NodeState &node_state = this->get_node_state(node); - InputState &input_state = node_state.inputs[socket->index()]; - - SingleInputValue &single_value = *input_state.value.single; - void *value = single_value.value; - - /* The value should have been computed by now. If this assert is hit, it means that there - * was some scheduling issue before. */ - BLI_assert(value != nullptr); - - /* Move value into memory owned by the outer allocator. */ - const CPPType &type = *input_state.type; - void *buffer = outer_allocator_.allocate(type.size(), type.alignment()); - type.move_construct(value, buffer); - - params_.r_output_values.append({type, buffer}); - } - } - - /** - * Load the required input from the socket or trigger nodes to the left to compute the value. - * \return True when the node will be triggered by another node again when the value is computed. - */ - bool set_input_required(LockedNode &locked_node, const DInputSocket input_socket) - { - BLI_assert(locked_node.node == input_socket.node()); - InputState &input_state = locked_node.node_state.inputs[input_socket->index()]; - - /* Value set as unused cannot become used again. */ - BLI_assert(input_state.usage != ValueUsage::Unused); - - if (input_state.was_ready_for_execution) { - return false; - } - - if (input_state.usage == ValueUsage::Required) { - /* If the input was not ready for execution but is required, the node will be triggered again - * once the input has been computed. */ - return true; - } - input_state.usage = ValueUsage::Required; - - /* Count how many values still have to be added to this input until it is "complete". */ - int missing_values = 0; - if (input_socket->is_multi_input()) { - MultiInputValue &multi_value = *input_state.value.multi; - missing_values = multi_value.missing_values(); - } - else { - SingleInputValue &single_value = *input_state.value.single; - if (single_value.value == nullptr) { - missing_values = 1; - } - } - if (missing_values == 0) { - return false; - } - /* Increase the total number of missing required inputs. This ensures that the node will be - * scheduled correctly when all inputs have been provided. */ - locked_node.node_state.missing_required_inputs += missing_values; - - /* Get all origin sockets, because we have to tag those as required as well. */ - Vector<DSocket> origin_sockets; - input_socket.foreach_origin_socket( - [&](const DSocket origin_socket) { origin_sockets.append(origin_socket); }); - - if (origin_sockets.is_empty()) { - /* If there are no origin sockets, just load the value from the socket directly. */ - this->load_unlinked_input_value(locked_node, input_socket, input_state, input_socket); - locked_node.node_state.missing_required_inputs -= 1; - return false; - } - bool requested_from_other_node = false; - for (const DSocket &origin_socket : origin_sockets) { - if (origin_socket->is_input()) { - /* Load the value directly from the origin socket. In most cases this is an unlinked - * group input. */ - this->load_unlinked_input_value(locked_node, input_socket, input_state, origin_socket); - locked_node.node_state.missing_required_inputs -= 1; - } - else { - /* The value has not been computed yet, so when it will be forwarded by another node, this - * node will be triggered. */ - requested_from_other_node = true; - locked_node.delayed_required_outputs.append(DOutputSocket(origin_socket)); - } - } - /* If this node will be triggered by another node, we don't have to schedule it now. */ - if (requested_from_other_node) { - return true; - } - return false; - } - - void set_input_unused(LockedNode &locked_node, const DInputSocket socket) - { - InputState &input_state = locked_node.node_state.inputs[socket->index()]; - - /* A required socket cannot become unused. */ - BLI_assert(input_state.usage != ValueUsage::Required); - - if (input_state.usage == ValueUsage::Unused) { - /* Nothing to do in this case. */ - return; - } - input_state.usage = ValueUsage::Unused; - - /* If the input is unused, its value can be destructed now. */ - this->destruct_input_value_if_exists(locked_node, socket); - - if (input_state.was_ready_for_execution) { - /* If the value was already computed, we don't need to notify origin nodes. */ - return; - } - - /* Notify origin nodes that might want to set its inputs as unused as well. */ - socket.foreach_origin_socket([&](const DSocket origin_socket) { - if (origin_socket->is_input()) { - /* Values from these sockets are loaded directly from the sockets, so there is no node to - * notify. */ - return; - } - /* Delay notification of the other node until this node is not locked anymore. */ - locked_node.delayed_unused_outputs.append(DOutputSocket(origin_socket)); - }); - } - - void send_output_required_notification(const DOutputSocket socket, NodeTaskRunState *run_state) - { - const DNode node = socket.node(); - NodeState &node_state = this->get_node_state(node); - OutputState &output_state = node_state.outputs[socket->index()]; - - this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { - if (output_state.output_usage == ValueUsage::Required) { - /* Output is marked as required already. So the node is scheduled already. */ - return; - } - /* The origin node needs to be scheduled so that it provides the requested input - * eventually. */ - output_state.output_usage = ValueUsage::Required; - this->schedule_node(locked_node); - }); - } - - void send_output_unused_notification(const DOutputSocket socket, NodeTaskRunState *run_state) - { - const DNode node = socket.node(); - NodeState &node_state = this->get_node_state(node); - OutputState &output_state = node_state.outputs[socket->index()]; - - this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { - output_state.potential_users -= 1; - if (output_state.potential_users == 0) { - /* The socket might be required even though the output is not used by other sockets. That - * can happen when the socket is forced to be computed. */ - if (output_state.output_usage != ValueUsage::Required) { - /* The output socket has no users anymore. */ - output_state.output_usage = ValueUsage::Unused; - /* Schedule the origin node in case it wants to set its inputs as unused as well. */ - this->schedule_node(locked_node); - } - } - }); - } - - void add_node_to_task_pool(const DNode node) - { - /* Push the task to the pool while it is not locked to avoid a deadlock in case when the task - * is executed immediately. */ - const NodeWithState *node_with_state = node_states_.lookup_key_ptr_as(node); - BLI_task_pool_push( - task_pool_, run_node_from_task_pool, (void *)node_with_state, false, nullptr); - } - - /** - * Moves a newly computed value from an output socket to all the inputs that might need it. - * Takes ownership of the value and destructs if it is unused. - */ - void forward_output(const DOutputSocket from_socket, - GMutablePointer value_to_forward, - NodeTaskRunState *run_state) - { - BLI_assert(value_to_forward.get() != nullptr); - - LinearAllocator<> &allocator = local_allocators_.local(); - - Vector<DSocket> log_original_value_sockets; - Vector<DInputSocket> forward_original_value_sockets; - log_original_value_sockets.append(from_socket); - - from_socket.foreach_target_socket([&](const DInputSocket to_socket, - const DOutputSocket::TargetSocketPathInfo &path_info) { - if (!this->should_forward_to_socket(to_socket)) { - return; - } - BLI_assert(to_socket == path_info.sockets.last()); - GMutablePointer current_value = value_to_forward; - for (const DSocket &next_socket : path_info.sockets) { - const DNode next_node = next_socket.node(); - const bool is_last_socket = to_socket == next_socket; - const bool do_conversion_if_necessary = is_last_socket || - next_node->type == NODE_GROUP_OUTPUT || - (next_node->is_group() && !next_node->is_muted()); - if (do_conversion_if_necessary) { - const CPPType &next_type = *get_socket_cpp_type(next_socket); - if (*current_value.type() != next_type) { - void *buffer = allocator.allocate(next_type.size(), next_type.alignment()); - this->convert_value(*current_value.type(), next_type, current_value.get(), buffer); - if (current_value.get() != value_to_forward.get()) { - current_value.destruct(); - } - current_value = {next_type, buffer}; - } - } - if (current_value.get() == value_to_forward.get()) { - /* Log the original value at the current socket. */ - log_original_value_sockets.append(next_socket); - } - else { - /* Multi-input sockets are logged when all values are available. */ - if (!(next_socket->is_input() && next_socket->is_multi_input())) { - /* Log the converted value at the socket. */ - this->log_socket_value({next_socket}, current_value); - } - } - } - if (current_value.get() == value_to_forward.get()) { - /* The value has not been converted, so forward the original value. */ - forward_original_value_sockets.append(to_socket); - } - else { - /* The value has been converted. */ - this->add_value_to_input_socket(to_socket, from_socket, current_value, run_state); - } - }); - this->log_socket_value(log_original_value_sockets, value_to_forward); - this->forward_to_sockets_with_same_type( - allocator, forward_original_value_sockets, value_to_forward, from_socket, run_state); - } - - bool should_forward_to_socket(const DInputSocket socket) - { - const DNode to_node = socket.node(); - const NodeWithState *target_node_with_state = node_states_.lookup_key_ptr_as(to_node); - if (target_node_with_state == nullptr) { - /* If the socket belongs to a node that has no state, the entire node is not used. */ - return false; - } - NodeState &target_node_state = *target_node_with_state->state; - InputState &target_input_state = target_node_state.inputs[socket->index()]; - - std::lock_guard lock{target_node_state.mutex}; - /* Do not forward to an input socket whose value won't be used. */ - return target_input_state.usage != ValueUsage::Unused; - } - - void forward_to_sockets_with_same_type(LinearAllocator<> &allocator, - Span<DInputSocket> to_sockets, - GMutablePointer value_to_forward, - const DOutputSocket from_socket, - NodeTaskRunState *run_state) - { - if (to_sockets.is_empty()) { - /* Value is not used anymore, so it can be destructed. */ - value_to_forward.destruct(); - } - else if (to_sockets.size() == 1) { - /* Value is only used by one input socket, no need to copy it. */ - const DInputSocket to_socket = to_sockets[0]; - this->add_value_to_input_socket(to_socket, from_socket, value_to_forward, run_state); - } - else { - /* Multiple inputs use the value, make a copy for every input except for one. */ - /* First make the copies, so that the next node does not start modifying the value while we - * are still making copies. */ - const CPPType &type = *value_to_forward.type(); - for (const DInputSocket &to_socket : to_sockets.drop_front(1)) { - void *buffer = allocator.allocate(type.size(), type.alignment()); - type.copy_construct(value_to_forward.get(), buffer); - this->add_value_to_input_socket(to_socket, from_socket, {type, buffer}, run_state); - } - /* Forward the original value to one of the targets. */ - const DInputSocket to_socket = to_sockets[0]; - this->add_value_to_input_socket(to_socket, from_socket, value_to_forward, run_state); - } - } - - void add_value_to_input_socket(const DInputSocket socket, - const DOutputSocket origin, - GMutablePointer value, - NodeTaskRunState *run_state) - { - BLI_assert(socket->is_available()); - - const DNode node = socket.node(); - NodeState &node_state = this->get_node_state(node); - InputState &input_state = node_state.inputs[socket->index()]; - - this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { - if (socket->is_multi_input()) { - /* Add a new value to the multi-input. */ - MultiInputValue &multi_value = *input_state.value.multi; - multi_value.add_value(origin, value.get()); - - if (multi_value.all_values_available()) { - this->log_socket_value({socket}, input_state, multi_value.values); - } - } - else { - /* Assign the value to the input. */ - SingleInputValue &single_value = *input_state.value.single; - BLI_assert(single_value.value == nullptr); - single_value.value = value.get(); - } - - if (input_state.usage == ValueUsage::Required) { - node_state.missing_required_inputs--; - if (node_state.missing_required_inputs == 0) { - /* Schedule node if all the required inputs have been provided. */ - this->schedule_node(locked_node); - } - } - }); - } - - /** - * Loads the value of a socket that is not computed by another node. Note that the socket may - * still be linked to e.g. a Group Input node, but the socket on the outside is not connected to - * anything. - * - * \param input_socket: The socket of the node that wants to use the value. - * \param origin_socket: The socket that we want to load the value from. - */ - void load_unlinked_input_value(LockedNode &locked_node, - const DInputSocket input_socket, - InputState &input_state, - const DSocket origin_socket) - { - /* Only takes locked node as parameter, because the node needs to be locked. */ - UNUSED_VARS(locked_node); - - GMutablePointer value = this->get_value_from_socket(origin_socket, *input_state.type); - if (input_socket->is_multi_input()) { - MultiInputValue &multi_value = *input_state.value.multi; - multi_value.add_value(origin_socket, value.get()); - if (multi_value.all_values_available()) { - this->log_socket_value({input_socket}, input_state, multi_value.values); - } - } - else { - SingleInputValue &single_value = *input_state.value.single; - single_value.value = value.get(); - Vector<DSocket> sockets_to_log_to = {input_socket}; - if (origin_socket != input_socket) { - /* This might log the socket value for the #origin_socket more than once, but this is - * handled by the logging system gracefully. */ - sockets_to_log_to.append(origin_socket); - } - /* TODO: Log to the intermediate sockets between the group input and where the value is - * actually used as well. */ - this->log_socket_value(sockets_to_log_to, value); - } - } - - void destruct_input_value_if_exists(LockedNode &locked_node, const DInputSocket socket) - { - InputState &input_state = locked_node.node_state.inputs[socket->index()]; - if (socket->is_multi_input()) { - MultiInputValue &multi_value = *input_state.value.multi; - for (void *&value : multi_value.values) { - if (value != nullptr) { - input_state.type->destruct(value); - value = nullptr; - } - } - multi_value.provided_value_count = 0; - } - else { - SingleInputValue &single_value = *input_state.value.single; - if (single_value.value != nullptr) { - input_state.type->destruct(single_value.value); - single_value.value = nullptr; - } - } - } - - GMutablePointer get_value_from_socket(const DSocket socket, const CPPType &required_type) - { - LinearAllocator<> &allocator = local_allocators_.local(); - - const CPPType &type = *get_socket_cpp_type(socket); - void *buffer = allocator.allocate(type.size(), type.alignment()); - get_socket_value(*socket.bsocket(), buffer); - - if (type == required_type) { - return {type, buffer}; - } - void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); - this->convert_value(type, required_type, buffer, converted_buffer); - type.destruct(buffer); - return {required_type, converted_buffer}; - } - - void convert_value(const CPPType &from_type, - const CPPType &to_type, - const void *from_value, - void *to_value) - { - if (from_type == to_type) { - from_type.copy_construct(from_value, to_value); - return; - } - const ValueOrFieldCPPType *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>( - &from_type); - const ValueOrFieldCPPType *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type); - - if (from_field_type != nullptr && to_field_type != nullptr) { - const CPPType &from_base_type = from_field_type->base_type(); - const CPPType &to_base_type = to_field_type->base_type(); - if (conversions_.is_convertible(from_base_type, to_base_type)) { - if (from_field_type->is_field(from_value)) { - const GField &from_field = *from_field_type->get_field_ptr(from_value); - to_field_type->construct_from_field(to_value, - conversions_.try_convert(from_field, to_base_type)); - } - else { - to_field_type->default_construct(to_value); - const void *from_value_ptr = from_field_type->get_value_ptr(from_value); - void *to_value_ptr = to_field_type->get_value_ptr(to_value); - conversions_.get_conversion_functions(from_base_type, to_base_type) - ->convert_single_to_initialized(from_value_ptr, to_value_ptr); - } - return; - } - } - if (conversions_.is_convertible(from_type, to_type)) { - /* Do the conversion if possible. */ - conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value); - } - else { - /* Cannot convert, use default value instead. */ - this->construct_default_value(to_type, to_value); - } - } - - void construct_default_value(const CPPType &type, void *r_value) - { - type.value_initialize(r_value); - } - - NodeState &get_node_state(const DNode node) - { - return *node_states_.lookup_key_as(node).state; - } - - void log_socket_value(DSocket socket, InputState &input_state, Span<void *> values) - { - if (params_.geo_logger == nullptr) { - return; - } - - Vector<GPointer, 16> value_pointers; - value_pointers.reserve(values.size()); - const CPPType &type = *input_state.type; - for (const void *value : values) { - value_pointers.append({type, value}); - } - params_.geo_logger->local().log_multi_value_socket(socket, value_pointers); - } - - void log_socket_value(Span<DSocket> sockets, GPointer value) - { - if (params_.geo_logger == nullptr) { - return; - } - params_.geo_logger->local().log_value_for_sockets(sockets, value); - } - - void log_debug_message(DNode node, std::string message) - { - if (params_.geo_logger == nullptr) { - return; - } - params_.geo_logger->local().log_debug_message(node, std::move(message)); - } - - /* In most cases when `NodeState` is accessed, the node has to be locked first to avoid race - * conditions. */ - template<typename Function> - void with_locked_node(const DNode node, - NodeState &node_state, - NodeTaskRunState *run_state, - const Function &function) - { - LockedNode locked_node{node, node_state}; - - node_state.mutex.lock(); - /* Isolate this thread because we don't want it to start executing another node. This other - * node might want to lock the same mutex leading to a deadlock. */ - threading::isolate_task([&] { function(locked_node); }); - node_state.mutex.unlock(); - - /* Then send notifications to the other nodes after the node state is unlocked. This avoids - * locking two nodes at the same time on this thread and helps to prevent deadlocks. */ - for (const DOutputSocket &socket : locked_node.delayed_required_outputs) { - this->send_output_required_notification(socket, run_state); - } - for (const DOutputSocket &socket : locked_node.delayed_unused_outputs) { - this->send_output_unused_notification(socket, run_state); - } - for (const DNode &node_to_schedule : locked_node.delayed_scheduled_nodes) { - if (run_state != nullptr && !run_state->next_node_to_run) { - /* Execute the node on the same thread after the current node finished. */ - /* Currently, this assumes that it is always best to run the first node that is scheduled - * on the same thread. That is usually correct, because the geometry socket which carries - * the most data usually comes first in nodes. */ - run_state->next_node_to_run = node_to_schedule; - } - else { - /* Push the node to the task pool so that another thread can start working on it. */ - this->add_node_to_task_pool(node_to_schedule); - } - } - } -}; - -NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator, - DNode dnode, - NodeState &node_state, - NodeTaskRunState *run_state) - : evaluator_(evaluator), node_state_(node_state), run_state_(run_state) -{ - this->dnode = dnode; - this->self_object = evaluator.params_.self_object; - this->modifier = &evaluator.params_.modifier_->modifier; - this->depsgraph = evaluator.params_.depsgraph; - this->logger = evaluator.params_.geo_logger; -} - -bool NodeParamsProvider::can_get_input(StringRef identifier) const -{ - const DInputSocket socket = this->dnode.input_by_identifier(identifier); - BLI_assert(socket); - - InputState &input_state = node_state_.inputs[socket->index()]; - if (!input_state.was_ready_for_execution) { - return false; - } - - if (socket->is_multi_input()) { - MultiInputValue &multi_value = *input_state.value.multi; - return multi_value.all_values_available(); - } - SingleInputValue &single_value = *input_state.value.single; - return single_value.value != nullptr; -} - -bool NodeParamsProvider::can_set_output(StringRef identifier) const -{ - const DOutputSocket socket = this->dnode.output_by_identifier(identifier); - BLI_assert(socket); - - OutputState &output_state = node_state_.outputs[socket->index()]; - return !output_state.has_been_computed; -} - -GMutablePointer NodeParamsProvider::extract_input(StringRef identifier) -{ - const DInputSocket socket = this->dnode.input_by_identifier(identifier); - BLI_assert(socket); - BLI_assert(!socket->is_multi_input()); - BLI_assert(this->can_get_input(identifier)); - - InputState &input_state = node_state_.inputs[socket->index()]; - SingleInputValue &single_value = *input_state.value.single; - void *value = single_value.value; - single_value.value = nullptr; - return {*input_state.type, value}; -} - -Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identifier) -{ - const DInputSocket socket = this->dnode.input_by_identifier(identifier); - BLI_assert(socket); - BLI_assert(socket->is_multi_input()); - BLI_assert(this->can_get_input(identifier)); - - InputState &input_state = node_state_.inputs[socket->index()]; - MultiInputValue &multi_value = *input_state.value.multi; - - Vector<GMutablePointer> ret_values; - for (void *&value : multi_value.values) { - BLI_assert(value != nullptr); - ret_values.append({*input_state.type, value}); - value = nullptr; - } - return ret_values; -} - -GPointer NodeParamsProvider::get_input(StringRef identifier) const -{ - const DInputSocket socket = this->dnode.input_by_identifier(identifier); - BLI_assert(socket); - BLI_assert(!socket->is_multi_input()); - BLI_assert(this->can_get_input(identifier)); - - InputState &input_state = node_state_.inputs[socket->index()]; - SingleInputValue &single_value = *input_state.value.single; - return {*input_state.type, single_value.value}; -} - -GMutablePointer NodeParamsProvider::alloc_output_value(const CPPType &type) -{ - LinearAllocator<> &allocator = evaluator_.local_allocators_.local(); - return {type, allocator.allocate(type.size(), type.alignment())}; -} - -void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value) -{ - const DOutputSocket socket = this->dnode.output_by_identifier(identifier); - BLI_assert(socket); - - OutputState &output_state = node_state_.outputs[socket->index()]; - BLI_assert(!output_state.has_been_computed); - evaluator_.forward_output(socket, value, run_state_); - output_state.has_been_computed = true; -} - -bool NodeParamsProvider::lazy_require_input(StringRef identifier) -{ - BLI_assert(node_supports_laziness(this->dnode)); - const DInputSocket socket = this->dnode.input_by_identifier(identifier); - BLI_assert(socket); - - InputState &input_state = node_state_.inputs[socket->index()]; - if (input_state.was_ready_for_execution) { - return false; - } - evaluator_.with_locked_node(this->dnode, node_state_, run_state_, [&](LockedNode &locked_node) { - if (!evaluator_.set_input_required(locked_node, socket)) { - /* Schedule the currently executed node again because the value is available now but was not - * ready for the current execution. */ - evaluator_.schedule_node(locked_node); - } - }); - return true; -} - -void NodeParamsProvider::set_input_unused(StringRef identifier) -{ - BLI_assert(node_supports_laziness(this->dnode)); - const DInputSocket socket = this->dnode.input_by_identifier(identifier); - BLI_assert(socket); - - evaluator_.with_locked_node(this->dnode, node_state_, run_state_, [&](LockedNode &locked_node) { - evaluator_.set_input_unused(locked_node, socket); - }); -} - -bool NodeParamsProvider::output_is_required(StringRef identifier) const -{ - const DOutputSocket socket = this->dnode.output_by_identifier(identifier); - BLI_assert(socket); - - OutputState &output_state = node_state_.outputs[socket->index()]; - if (output_state.has_been_computed) { - return false; - } - return output_state.output_usage_for_execution != ValueUsage::Unused; -} - -bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const -{ - BLI_assert(node_supports_laziness(this->dnode)); - const DOutputSocket socket = this->dnode.output_by_identifier(identifier); - BLI_assert(socket); - - OutputState &output_state = node_state_.outputs[socket->index()]; - if (output_state.has_been_computed) { - return false; - } - return output_state.output_usage_for_execution == ValueUsage::Required; -} - -void NodeParamsProvider::set_default_remaining_outputs() -{ - LinearAllocator<> &allocator = evaluator_.local_allocators_.local(); - - for (const int i : this->dnode->output_sockets().index_range()) { - OutputState &output_state = node_state_.outputs[i]; - if (output_state.has_been_computed) { - continue; - } - if (output_state.output_usage_for_execution == ValueUsage::Unused) { - continue; - } - - const DOutputSocket socket = this->dnode.output(i); - const CPPType *type = get_socket_cpp_type(socket); - BLI_assert(type != nullptr); - void *buffer = allocator.allocate(type->size(), type->alignment()); - type->value_initialize(buffer); - evaluator_.forward_output(socket, {type, buffer}, run_state_); - output_state.has_been_computed = true; - } -} - -void evaluate_geometry_nodes(GeometryNodesEvaluationParams ¶ms) -{ - GeometryNodesEvaluator evaluator{params}; - evaluator.execute(); -} - -} // namespace blender::modifiers::geometry_nodes diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh deleted file mode 100644 index cbcbcab5679..00000000000 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#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 "DNA_modifier_types.h" - -#include "FN_multi_function.hh" - -namespace geo_log = blender::nodes::geometry_nodes_eval_log; - -namespace blender::modifiers::geometry_nodes { - -using namespace nodes::derived_node_tree_types; - -struct GeometryNodesEvaluationParams { - blender::LinearAllocator<> allocator; - - Map<DOutputSocket, GMutablePointer> input_values; - Vector<DInputSocket> output_sockets; - /* These sockets will be computed but are not part of the output. Their value can be retrieved in - * `log_socket_value_fn`. These sockets are not part of `output_sockets` because then the - * evaluator would have to keep the socket values in memory until the end, which might not be - * necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value - * and then it can be freed. */ - Vector<DSocket> force_compute_sockets; - nodes::NodeMultiFunctions *mf_by_node; - const NodesModifierData *modifier_; - Depsgraph *depsgraph; - Object *self_object; - geo_log::GeoLogger *geo_logger; - - Vector<GMutablePointer> r_output_values; -}; - -void evaluate_geometry_nodes(GeometryNodesEvaluationParams ¶ms); - -} // namespace blender::modifiers::geometry_nodes diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 9c6f7ff4069..d3b02659380 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -169,9 +169,9 @@ typedef struct GenerateOceanGeometryData { float ix, iy; } GenerateOceanGeometryData; -static void generate_ocean_geometry_vertices(void *__restrict userdata, - const int y, - const TaskParallelTLS *__restrict UNUSED(tls)) +static void generate_ocean_geometry_verts(void *__restrict userdata, + const int y, + const TaskParallelTLS *__restrict UNUSED(tls)) { GenerateOceanGeometryData *gogd = userdata; int x; @@ -185,9 +185,9 @@ static void generate_ocean_geometry_vertices(void *__restrict userdata, } } -static void generate_ocean_geometry_polygons(void *__restrict userdata, - const int y, - const TaskParallelTLS *__restrict UNUSED(tls)) +static void generate_ocean_geometry_polys(void *__restrict userdata, + const int y, + const TaskParallelTLS *__restrict UNUSED(tls)) { GenerateOceanGeometryData *gogd = userdata; int x; @@ -282,10 +282,10 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co settings.use_threading = use_threading; /* create vertices */ - BLI_task_parallel_range(0, gogd.res_y + 1, &gogd, generate_ocean_geometry_vertices, &settings); + BLI_task_parallel_range(0, gogd.res_y + 1, &gogd, generate_ocean_geometry_verts, &settings); /* create faces */ - BLI_task_parallel_range(0, gogd.res_y, &gogd, generate_ocean_geometry_polygons, &settings); + BLI_task_parallel_range(0, gogd.res_y, &gogd, generate_ocean_geometry_polys, &settings); BKE_mesh_calc_edges(result, false, false); diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 343aa3920d9..1456254c31f 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -340,11 +340,6 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex MPoly *mpoly = BKE_mesh_polys_for_write(result); MLoop *mloop = BKE_mesh_loops_for_write(result); - if (do_bevel_convex) { - /* Make sure bweight is enabled. */ - result->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; - } - if (do_shell) { CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)verts_num); CustomData_copy_data(&mesh->vdata, &result->vdata, 0, (int)verts_num, (int)verts_num); @@ -392,6 +387,12 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)polys_num); } + float *result_edge_bweight = NULL; + if (do_bevel_convex) { + result_edge_bweight = CustomData_add_layer( + &result->edata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, result->totedge); + } + /* initializes: (i_end, do_shell_align, mv). */ #define INIT_VERT_ARRAY_OFFSETS(test) \ if (((ofs_new >= ofs_orig) == do_flip) == test) { \ @@ -671,20 +672,18 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex for (uint i = 0; i < edges_num; i++) { if (edge_users[i] == INVALID_PAIR) { float angle = edge_angs[i]; - medge[i].bweight = (char)clamp_i( - (int)medge[i].bweight + (int)((angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) : - clamp_f(bevel_convex, -1.0f, 0.0f)) * - 255), - 0, - 255); + result_edge_bweight[i] = clamp_f(result_edge_bweight[i] + + (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) : + clamp_f(bevel_convex, -1.0f, 0.0f)), + 0.0f, + 1.0f); if (do_shell) { - medge[i + edges_num].bweight = (char)clamp_i( - (int)medge[i + edges_num].bweight + - (int)((angle > M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) : - clamp_f(bevel_convex, -1.0f, 0.0f)) * - 255), + result_edge_bweight[i + edges_num] = clamp_f( + result_edge_bweight[i + edges_num] + (angle > M_PI ? + clamp_f(bevel_convex, 0.0f, 1.0f) : + clamp_f(bevel_convex, -1.0f, 0.0f)), 0, - 255); + 1.0f); } } } @@ -900,20 +899,17 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex for (i = 0; i < edges_num; i++) { if (edge_users[i] == INVALID_PAIR) { float angle = edge_angs[i]; - medge[i].bweight = (char)clamp_i( - (int)medge[i].bweight + (int)((angle < M_PI ? clamp_f(bevel_convex, 0, 1) : - clamp_f(bevel_convex, -1, 0)) * - 255), - 0, - 255); + result_edge_bweight[i] = clamp_f(result_edge_bweight[i] + + (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) : + clamp_f(bevel_convex, -1.0f, 0.0f)), + 0.0f, + 1.0f); if (do_shell) { - medge[i + edges_num].bweight = (char)clamp_i( - (int)medge[i + edges_num].bweight + - (int)((angle > M_PI ? clamp_f(bevel_convex, 0, 1) : - clamp_f(bevel_convex, -1, 0)) * - 255), - 0, - 255); + result_edge_bweight[i + edges_num] = clamp_f( + result_edge_bweight[i + edges_num] + + (angle > M_PI ? clamp_f(bevel_convex, 0, 1) : clamp_f(bevel_convex, -1, 0)), + 0.0f, + 1.0f); } } } diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index e73df0d1c12..d3aff5c58c5 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -189,6 +189,10 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, const MPoly *orig_mpoly = BKE_mesh_polys(mesh); const MLoop *orig_mloop = BKE_mesh_loops(mesh); + /* These might be null. */ + const float *orig_vert_bweight = CustomData_get_layer(&mesh->vdata, CD_BWEIGHT); + const float *orig_edge_bweight = CustomData_get_layer(&mesh->edata, CD_BWEIGHT); + uint new_verts_num = 0; uint new_edges_num = 0; uint new_loops_num = 0; @@ -1965,9 +1969,10 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, int *origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX); int *origindex_poly = CustomData_get_layer(&result->pdata, CD_ORIGINDEX); - if (bevel_convex != 0.0f || (result->cd_flag & ME_CDFLAG_VERT_BWEIGHT) != 0) { - /* make sure bweight is enabled */ - result->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; + float *result_edge_bweight = CustomData_get_layer(&result->edata, CD_BWEIGHT); + if (bevel_convex != 0.0f || orig_vert_bweight != NULL) { + result_edge_bweight = CustomData_add_layer( + &result->edata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, result->totedge); } /* Checks that result has dvert data. */ @@ -2038,17 +2043,18 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, medge[insert].v2 = v2; medge[insert].flag = orig_medge[(*l)->old_edge].flag | ME_EDGEDRAW | ME_EDGERENDER; medge[insert].crease = orig_medge[(*l)->old_edge].crease; - medge[insert].bweight = orig_medge[(*l)->old_edge].bweight; + if (result_edge_bweight) { + result_edge_bweight[insert] = orig_edge_bweight[(*l)->old_edge]; + } if (bevel_convex != 0.0f && (*l)->faces[1] != NULL) { - medge[insert].bweight = (char)clamp_i( - (int)medge[insert].bweight + (int)(((*l)->angle > M_PI + FLT_EPSILON ? - clamp_f(bevel_convex, 0.0f, 1.0f) : - ((*l)->angle < M_PI - FLT_EPSILON ? - clamp_f(bevel_convex, -1.0f, 0.0f) : - 0)) * - 255), - 0, - 255); + result_edge_bweight[insert] = clamp_f( + result_edge_bweight[insert] + + ((*l)->angle > M_PI + FLT_EPSILON ? + clamp_f(bevel_convex, 0.0f, 1.0f) : + ((*l)->angle < M_PI - FLT_EPSILON ? clamp_f(bevel_convex, -1.0f, 0.0f) : + 0)), + 0.0f, + 1.0f); } (*l)->new_edge = insert; } @@ -2113,13 +2119,14 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, EdgeGroup *last_g = NULL; EdgeGroup *first_g = NULL; char mv_crease = vertex_crease ? (char)(vertex_crease[i] * 255.0f) : 0; + float mv_bweight = orig_vert_bweight ? orig_vert_bweight[i] : 0.0f; /* Data calculation cache. */ char max_crease; char last_max_crease = 0; char first_max_crease = 0; - char max_bweight; - char last_max_bweight = 0; - char first_max_bweight = 0; + float max_bweight; + float last_max_bweight = 0.0f; + float first_max_bweight = 0.0f; short flag; short last_flag = 0; short first_flag = 0; @@ -2142,20 +2149,24 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, max_crease = ed->crease; } if (g->edges[k]->new_edge != MOD_SOLIDIFY_EMPTY_TAG) { - char bweight = medge[g->edges[k]->new_edge].bweight; - if (bweight > max_bweight) { - max_bweight = bweight; + if (result_edge_bweight) { + float bweight = result_edge_bweight[g->edges[k]->new_edge]; + if (bweight > max_bweight) { + max_bweight = bweight; + } } } flag |= ed->flag; } } - const char bweight_open_edge = min_cc( - orig_medge[g->edges[0]->old_edge].bweight, - orig_medge[g->edges[g->edges_len - 1]->old_edge].bweight); + const float bweight_open_edge = + orig_edge_bweight ? + min_ff(orig_edge_bweight[g->edges[0]->old_edge], + orig_edge_bweight[g->edges[g->edges_len - 1]->old_edge]) : + 0.0f; if (bweight_open_edge > 0) { - max_bweight = min_cc(bweight_open_edge, max_bweight); + max_bweight = min_ff(bweight_open_edge, max_bweight); } else { if (bevel_convex < 0.0f) { @@ -2183,8 +2194,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER | ((last_flag | flag) & (ME_SEAM | ME_SHARP)); 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)); + if (result_edge_bweight) { + result_edge_bweight[edge_index] = max_ff(mv_bweight, + min_ff(last_max_bweight, max_bweight)); + } + edge_index++; } last_g = g; last_max_crease = max_crease; @@ -2212,8 +2226,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, ((last_flag | first_flag) & (ME_SEAM | ME_SHARP)); 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)); + if (result_edge_bweight) { + result_edge_bweight[edge_index] = max_ff( + mv_bweight, min_ff(last_max_bweight, first_max_bweight)); + } + edge_index++; /* Loop data. */ int *loops = MEM_malloc_arrayN(j, sizeof(*loops), "loops in solidify"); diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index 215436e4a8d..0065012db97 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -51,7 +51,7 @@ static void initData(ModifierData *md) VolumeToMeshModifierData *vmmd = reinterpret_cast<VolumeToMeshModifierData *>(md); vmmd->object = nullptr; vmmd->threshold = 0.1f; - strncpy(vmmd->grid_name, "density", MAX_NAME); + STRNCPY(vmmd->grid_name, "density"); vmmd->adaptivity = 0.0f; vmmd->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; vmmd->voxel_amount = 32; diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index 05c6eea9f8b..ba441581770 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -362,7 +362,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, clnors); } else { - /* TODO: Ideally, we could add an option to BKE_mesh_normals_loop_custom_[from_vertices_]set() + /* TODO: Ideally, we could add an option to `BKE_mesh_normals_loop_custom_[from_verts_]set()` * to keep current clnors instead of resetting them to default auto-computed ones, * when given new custom normal is zero-vec. * But this is not exactly trivial change, better to keep this optimization for later... @@ -379,18 +379,18 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal); } - BKE_mesh_normals_loop_custom_from_vertices_set(mvert, - wn_data->vert_normals, - vert_normals, - verts_num, - medge, - edges_num, - mloop, - loops_num, - mpoly, - polynors, - polys_num, - clnors); + BKE_mesh_normals_loop_custom_from_verts_set(mvert, + wn_data->vert_normals, + vert_normals, + verts_num, + medge, + edges_num, + mloop, + loops_num, + mpoly, + polynors, + polys_num, + clnors); MEM_freeN(vert_normals); } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index ff8bd27f8d7..e042458ca19 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -40,7 +40,8 @@ set(INC set(SRC intern/derived_node_tree.cc - intern/geometry_nodes_eval_log.cc + intern/geometry_nodes_lazy_function.cc + intern/geometry_nodes_log.cc intern/math_functions.cc intern/node_common.cc intern/node_declaration.cc @@ -58,7 +59,7 @@ set(SRC NOD_function.h NOD_geometry.h NOD_geometry_exec.hh - NOD_geometry_nodes_eval_log.hh + NOD_geometry_nodes_lazy_function.hh NOD_math_functions.hh NOD_multi_function.hh NOD_node_declaration.hh diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index b5ffd3a317c..16669f7cfce 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -3,6 +3,7 @@ #pragma once #include "FN_field.hh" +#include "FN_lazy_function.hh" #include "FN_multi_function_builder.hh" #include "BKE_geometry_fields.hh" @@ -11,9 +12,8 @@ #include "DNA_node_types.h" #include "NOD_derived_node_tree.hh" -#include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_geometry_nodes_lazy_function.hh" -struct Depsgraph; struct ModifierData; namespace blender::nodes { @@ -40,75 +40,18 @@ using fn::FieldInput; using fn::FieldOperation; using fn::GField; using fn::ValueOrField; -using geometry_nodes_eval_log::eNamedAttrUsage; -using geometry_nodes_eval_log::NodeWarningType; - -/** - * This class exists to separate the memory management details of the geometry nodes evaluator - * from the node execution functions and related utilities. - */ -class GeoNodeExecParamsProvider { - public: - DNode dnode; - const Object *self_object = nullptr; - const ModifierData *modifier = nullptr; - Depsgraph *depsgraph = nullptr; - geometry_nodes_eval_log::GeoLogger *logger = nullptr; - - /** - * Returns true when the node is allowed to get/extract the input value. The identifier is - * expected to be valid. This may return false if the input value has been consumed already. - */ - virtual bool can_get_input(StringRef identifier) const = 0; - - /** - * Returns true when the node is allowed to set the output value. The identifier is expected to - * be valid. This may return false if the output value has been set already. - */ - virtual bool can_set_output(StringRef identifier) const = 0; - - /** - * Take ownership of an input value. The caller is responsible for destructing the value. It does - * not have to be freed, because the memory is managed by the geometry nodes evaluator. - */ - virtual GMutablePointer extract_input(StringRef identifier) = 0; - - /** - * Similar to #extract_input, but has to be used for multi-input sockets. - */ - virtual Vector<GMutablePointer> extract_multi_input(StringRef identifier) = 0; - - /** - * Get the input value for the identifier without taking ownership of it. - */ - virtual GPointer get_input(StringRef identifier) const = 0; - - /** - * Prepare a memory buffer for an output value of the node. The returned memory has to be - * initialized by the caller. The identifier and type are expected to be correct. - */ - virtual GMutablePointer alloc_output_value(const CPPType &type) = 0; - - /** - * The value has been allocated with #alloc_output_value. - */ - virtual void set_output(StringRef identifier, GMutablePointer value) = 0; - - /* A description for these methods is provided in GeoNodeExecParams. */ - virtual void set_input_unused(StringRef identifier) = 0; - virtual bool output_is_required(StringRef identifier) const = 0; - virtual bool lazy_require_input(StringRef identifier) = 0; - virtual bool lazy_output_is_required(StringRef identifier) const = 0; - - virtual void set_default_remaining_outputs() = 0; -}; +using geo_eval_log::NamedAttributeUsage; +using geo_eval_log::NodeWarningType; class GeoNodeExecParams { private: - GeoNodeExecParamsProvider *provider_; + const bNode &node_; + lf::Params ¶ms_; + const lf::Context &lf_context_; public: - GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider) + GeoNodeExecParams(const bNode &node, lf::Params ¶ms, const lf::Context &lf_context) + : node_(node), params_(params), lf_context_(lf_context) { } @@ -119,20 +62,6 @@ class GeoNodeExecParams { /** * Get the input value for the input socket with the given identifier. * - * The node calling becomes responsible for destructing the value before it is done - * executing. This method can only be called once for each identifier. - */ - GMutablePointer extract_input(StringRef identifier) - { -#ifdef DEBUG - this->check_input_access(identifier); -#endif - return provider_->extract_input(identifier); - } - - /** - * Get the input value for the input socket with the given identifier. - * * This method can only be called once for each identifier. */ template<typename T> T extract_input(StringRef identifier) @@ -151,8 +80,8 @@ class GeoNodeExecParams { #ifdef DEBUG this->check_input_access(identifier, &CPPType::get<T>()); #endif - GMutablePointer gvalue = this->extract_input(identifier); - T value = gvalue.relocate_out<T>(); + const int index = this->get_input_index(identifier); + T value = params_.extract_input<T>(index); if constexpr (std::is_same_v<T, GeometrySet>) { this->check_input_geometry_set(identifier, value); } @@ -164,27 +93,6 @@ class GeoNodeExecParams { void check_output_geometry_set(const GeometrySet &geometry_set) const; /** - * Get input as vector for multi input socket with the given identifier. - * - * This method can only be called once for each identifier. - */ - template<typename T> Vector<T> extract_multi_input(StringRef identifier) - { - Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier); - Vector<T> values; - for (GMutablePointer gvalue : gvalues) { - if constexpr (is_field_base_type_v<T>) { - const ValueOrField<T> value_or_field = gvalue.relocate_out<ValueOrField<T>>(); - values.append(value_or_field.as_value()); - } - else { - values.append(gvalue.relocate_out<T>()); - } - } - return values; - } - - /** * Get the input value for the input socket with the given identifier. */ template<typename T> T get_input(StringRef identifier) const @@ -202,9 +110,8 @@ class GeoNodeExecParams { #ifdef DEBUG this->check_input_access(identifier, &CPPType::get<T>()); #endif - GPointer gvalue = provider_->get_input(identifier); - BLI_assert(gvalue.is_type<T>()); - const T &value = *(const T *)gvalue.get(); + const int index = this->get_input_index(identifier); + const T &value = params_.get_input<T>(index); if constexpr (std::is_same_v<T, GeometrySet>) { this->check_input_geometry_set(identifier, value); } @@ -226,17 +133,28 @@ class GeoNodeExecParams { this->set_output(identifier, ValueOrField<BaseType>(std::forward<T>(value))); } else { - const CPPType &type = CPPType::get<StoredT>(); #ifdef DEBUG + const CPPType &type = CPPType::get<StoredT>(); this->check_output_access(identifier, type); #endif if constexpr (std::is_same_v<StoredT, GeometrySet>) { this->check_output_geometry_set(value); } - GMutablePointer gvalue = provider_->alloc_output_value(type); - new (gvalue.get()) StoredT(std::forward<T>(value)); - provider_->set_output(identifier, gvalue); + const int index = this->get_output_index(identifier); + params_.set_output(index, std::forward<T>(value)); + } + } + + geo_eval_log::GeoTreeLogger *get_local_tree_logger() const + { + GeoNodesLFUserData *user_data = this->user_data(); + BLI_assert(user_data != nullptr); + const ComputeContext *compute_context = user_data->compute_context; + BLI_assert(compute_context != nullptr); + if (user_data->modifier_data->eval_log == nullptr) { + return nullptr; } + return &user_data->modifier_data->eval_log->get_local_tree_logger(*compute_context); } /** @@ -244,7 +162,8 @@ class GeoNodeExecParams { */ void set_input_unused(StringRef identifier) { - provider_->set_input_unused(identifier); + const int index = this->get_input_index(identifier); + params_.set_input_unused(index); } /** @@ -254,7 +173,8 @@ class GeoNodeExecParams { */ bool output_is_required(StringRef identifier) const { - return provider_->output_is_required(identifier); + const int index = this->get_output_index(identifier); + return params_.get_output_usage(index) != lf::ValueUsage::Unused; } /** @@ -265,7 +185,8 @@ class GeoNodeExecParams { */ bool lazy_require_input(StringRef identifier) { - return provider_->lazy_require_input(identifier); + const int index = this->get_input_index(identifier); + return params_.try_get_input_data_ptr_or_request(index) == nullptr; } /** @@ -275,7 +196,8 @@ class GeoNodeExecParams { */ bool lazy_output_is_required(StringRef identifier) { - return provider_->lazy_output_is_required(identifier); + const int index = this->get_output_index(identifier); + return params_.get_output_usage(index) == lf::ValueUsage::Used; } /** @@ -283,17 +205,32 @@ class GeoNodeExecParams { */ const bNode &node() const { - return *provider_->dnode; + return node_; } const Object *self_object() const { - return provider_->self_object; + if (const auto *data = this->user_data()) { + if (data->modifier_data) { + return data->modifier_data->self_object; + } + } + return nullptr; } Depsgraph *depsgraph() const { - return provider_->depsgraph; + if (const auto *data = this->user_data()) { + if (data->modifier_data) { + return data->modifier_data->depsgraph; + } + } + return nullptr; + } + + GeoNodesLFUserData *user_data() const + { + return dynamic_cast<GeoNodesLFUserData *>(lf_context_.user_data); } /** @@ -306,7 +243,7 @@ class GeoNodeExecParams { void set_default_remaining_outputs(); - void used_named_attribute(std::string attribute_name, eNamedAttrUsage usage); + void used_named_attribute(std::string attribute_name, NamedAttributeUsage usage); private: /* Utilities for detecting common errors at when using this class. */ @@ -315,6 +252,38 @@ class GeoNodeExecParams { /* Find the active socket with the input name (not the identifier). */ const bNodeSocket *find_available_socket(const StringRef name) const; + + int get_input_index(const StringRef identifier) const + { + int counter = 0; + for (const bNodeSocket *socket : node_.input_sockets()) { + if (!socket->is_available()) { + continue; + } + if (socket->identifier == identifier) { + return counter; + } + counter++; + } + BLI_assert_unreachable(); + return -1; + } + + int get_output_index(const StringRef identifier) const + { + int counter = 0; + for (const bNodeSocket *socket : node_.output_sockets()) { + if (!socket->is_available()) { + continue; + } + if (socket->identifier == identifier) { + return counter; + } + counter++; + } + BLI_assert_unreachable(); + return -1; + } }; } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh deleted file mode 100644 index 46ba72d14d8..00000000000 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ /dev/null @@ -1,411 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** - * Many geometry nodes related UI features need access to data produced during evaluation. Not only - * is the final output required but also the intermediate results. Those features include - * attribute search, node warnings, socket inspection and the viewer node. - * - * This file provides the framework for logging data during evaluation and accessing the data after - * evaluation. - * - * During logging every thread gets its own local logger to avoid too much locking (logging - * generally happens for every socket). After geometry nodes evaluation is done, the thread-local - * logging information is combined and post-processed to make it easier for the UI to lookup. - * necessary information. - */ - -#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 "NOD_derived_node_tree.hh" - -#include "FN_field.hh" - -#include <chrono> - -struct SpaceNode; -struct SpaceSpreadsheet; - -namespace blender::nodes::geometry_nodes_eval_log { - -/** Contains information about a value that has been computed during geometry nodes evaluation. */ -class ValueLog { - public: - virtual ~ValueLog() = default; -}; - -/** Contains an owned copy of a value of a generic type. */ -class GenericValueLog : public ValueLog { - private: - GMutablePointer data_; - - public: - GenericValueLog(GMutablePointer data) : data_(data) - { - } - - ~GenericValueLog() - { - data_.destruct(); - } - - GPointer value() const - { - return data_; - } -}; - -class GFieldValueLog : public ValueLog { - private: - fn::GField field_; - const CPPType &type_; - Vector<std::string> input_tooltips_; - - public: - GFieldValueLog(fn::GField field, bool log_full_field); - - const fn::GField &field() const - { - return field_; - } - - Span<std::string> input_tooltips() const - { - return input_tooltips_; - } - - const CPPType &type() const - { - return type_; - } -}; - -struct GeometryAttributeInfo { - std::string name; - /** Can be empty when #name does not actually exist on a geometry yet. */ - std::optional<eAttrDomain> domain; - std::optional<eCustomDataType> data_type; -}; - -/** Contains information about a geometry set. In most cases this does not store the entire - * geometry set as this would require too much memory. */ -class GeometryValueLog : public ValueLog { - private: - Vector<GeometryAttributeInfo> attributes_; - Vector<GeometryComponentType> component_types_; - std::unique_ptr<GeometrySet> full_geometry_; - - public: - struct MeshInfo { - int verts_num, edges_num, faces_num; - }; - struct CurveInfo { - int splines_num; - }; - struct PointCloudInfo { - int points_num; - }; - struct InstancesInfo { - int instances_num; - }; - struct EditDataInfo { - bool has_deformed_positions; - bool has_deform_matrices; - }; - - std::optional<MeshInfo> mesh_info; - std::optional<CurveInfo> curve_info; - std::optional<PointCloudInfo> pointcloud_info; - std::optional<InstancesInfo> instances_info; - std::optional<EditDataInfo> edit_data_info; - - GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false); - - Span<GeometryAttributeInfo> attributes() const - { - return attributes_; - } - - Span<GeometryComponentType> component_types() const - { - return component_types_; - } - - const GeometrySet *full_geometry() const - { - return full_geometry_.get(); - } -}; - -enum class NodeWarningType { - Error, - Warning, - Info, -}; - -struct NodeWarning { - NodeWarningType type; - std::string message; -}; - -struct NodeWithWarning { - DNode node; - NodeWarning warning; -}; - -struct NodeWithExecutionTime { - DNode node; - std::chrono::microseconds exec_time; -}; - -struct NodeWithDebugMessage { - DNode node; - std::string message; -}; - -/** The same value can be referenced by multiple sockets when they are linked. */ -struct ValueOfSockets { - Span<DSocket> sockets; - destruct_ptr<ValueLog> value; -}; - -enum class eNamedAttrUsage { - None = 0, - Read = 1 << 0, - Write = 1 << 1, - Remove = 1 << 2, -}; -ENUM_OPERATORS(eNamedAttrUsage, eNamedAttrUsage::Remove); - -struct UsedNamedAttribute { - std::string name; - eNamedAttrUsage usage; -}; - -struct NodeWithUsedNamedAttribute { - DNode node; - UsedNamedAttribute attribute; -}; - -class GeoLogger; -class ModifierLog; - -/** Every thread has its own local logger to avoid having to communicate between threads during - * evaluation. After evaluation the individual logs are combined. */ -class LocalGeoLogger { - private: - /* Back pointer to the owner of this local logger. */ - GeoLogger *main_logger_; - /* Allocator for the many small allocations during logging. This is in a `unique_ptr` so that - * ownership can be transferred later on. */ - std::unique_ptr<LinearAllocator<>> allocator_; - Vector<ValueOfSockets> values_; - Vector<NodeWithWarning> node_warnings_; - Vector<NodeWithExecutionTime> node_exec_times_; - Vector<NodeWithDebugMessage> node_debug_messages_; - Vector<NodeWithUsedNamedAttribute> used_named_attributes_; - - friend ModifierLog; - - public: - LocalGeoLogger(GeoLogger &main_logger) : main_logger_(&main_logger) - { - this->allocator_ = std::make_unique<LinearAllocator<>>(); - } - - void log_value_for_sockets(Span<DSocket> sockets, GPointer value); - void log_multi_value_socket(DSocket socket, Span<GPointer> values); - void log_node_warning(DNode node, NodeWarningType type, std::string message); - void log_execution_time(DNode node, std::chrono::microseconds exec_time); - void log_used_named_attribute(DNode node, std::string attribute_name, eNamedAttrUsage usage); - /** - * Log a message that will be displayed in the node editor next to the node. - * This should only be used for debugging purposes and not to display information to users. - */ - void log_debug_message(DNode node, std::string message); -}; - -/** The root logger class. */ -class GeoLogger { - private: - /** - * Log the entire value for these sockets, because they may be inspected afterwards. - * We don't log everything, because that would take up too much memory and cause significant - * slowdowns. - */ - Set<DSocket> log_full_sockets_; - threading::EnumerableThreadSpecific<LocalGeoLogger> threadlocals_; - - /* These are only optional since they don't have a default constructor. */ - std::unique_ptr<GeometryValueLog> input_geometry_log_; - std::unique_ptr<GeometryValueLog> output_geometry_log_; - - friend LocalGeoLogger; - friend ModifierLog; - - public: - GeoLogger(Set<DSocket> log_full_sockets) - : log_full_sockets_(std::move(log_full_sockets)), - threadlocals_([this]() { return LocalGeoLogger(*this); }) - { - } - - void log_input_geometry(const GeometrySet &geometry) - { - input_geometry_log_ = std::make_unique<GeometryValueLog>(geometry); - } - - void log_output_geometry(const GeometrySet &geometry) - { - output_geometry_log_ = std::make_unique<GeometryValueLog>(geometry); - } - - LocalGeoLogger &local() - { - return threadlocals_.local(); - } - - auto begin() - { - return threadlocals_.begin(); - } - - auto end() - { - return threadlocals_.end(); - } -}; - -/** Contains information that has been logged for one specific socket. */ -class SocketLog { - private: - ValueLog *value_ = nullptr; - - friend ModifierLog; - - public: - const ValueLog *value() const - { - return value_; - } -}; - -/** Contains information that has been logged for one specific node. */ -class NodeLog { - private: - Vector<SocketLog> input_logs_; - Vector<SocketLog> output_logs_; - Vector<NodeWarning, 0> warnings_; - Vector<std::string, 0> debug_messages_; - Vector<UsedNamedAttribute, 0> used_named_attributes_; - std::chrono::microseconds exec_time_; - - friend ModifierLog; - - public: - const SocketLog *lookup_socket_log(eNodeSocketInOut in_out, int index) const; - const SocketLog *lookup_socket_log(const bNode &node, const bNodeSocket &socket) const; - void execution_time(std::chrono::microseconds exec_time); - - Span<SocketLog> input_logs() const - { - return input_logs_; - } - - Span<SocketLog> output_logs() const - { - return output_logs_; - } - - Span<NodeWarning> warnings() const - { - return warnings_; - } - - Span<std::string> debug_messages() const - { - return debug_messages_; - } - - Span<UsedNamedAttribute> used_named_attributes() const - { - return used_named_attributes_; - } - - std::chrono::microseconds execution_time() const - { - return exec_time_; - } - - Vector<const GeometryAttributeInfo *> lookup_available_attributes() const; -}; - -/** Contains information that has been logged for one specific tree. */ -class TreeLog { - private: - Map<std::string, destruct_ptr<NodeLog>> node_logs_; - Map<std::string, destruct_ptr<TreeLog>> child_logs_; - - friend ModifierLog; - - public: - const NodeLog *lookup_node_log(StringRef node_name) const; - const NodeLog *lookup_node_log(const bNode &node) const; - const TreeLog *lookup_child_log(StringRef node_name) const; - void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const; -}; - -/** Contains information about an entire geometry nodes evaluation. */ -class ModifierLog { - private: - LinearAllocator<> allocator_; - /* Allocators of the individual loggers. */ - Vector<std::unique_ptr<LinearAllocator<>>> logger_allocators_; - destruct_ptr<TreeLog> root_tree_logs_; - Vector<destruct_ptr<ValueLog>> logged_values_; - - std::unique_ptr<GeometryValueLog> input_geometry_log_; - std::unique_ptr<GeometryValueLog> output_geometry_log_; - - public: - ModifierLog(GeoLogger &logger); - - const TreeLog &root_tree() const - { - return *root_tree_logs_; - } - - /* Utilities to find logged information for a specific context. */ - static const ModifierLog *find_root_by_node_editor_context(const SpaceNode &snode); - static const TreeLog *find_tree_by_node_editor_context(const SpaceNode &snode); - static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, - const bNode &node); - static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, - const StringRef node_name); - static const SocketLog *find_socket_by_node_editor_context(const SpaceNode &snode, - const bNode &node, - const bNodeSocket &socket); - static const NodeLog *find_node_by_spreadsheet_editor_context( - const SpaceSpreadsheet &sspreadsheet); - void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const; - - const GeometryValueLog *input_geometry_log() const; - const GeometryValueLog *output_geometry_log() const; - - private: - using LogByTreeContext = Map<const DTreeContext *, TreeLog *>; - - TreeLog &lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, - const DTreeContext &tree_context); - NodeLog &lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node); - SocketLog &lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, DSocket socket); -}; - -} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh new file mode 100644 index 00000000000..3137dc41857 --- /dev/null +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * For evaluation, geometry node groups are converted to a lazy-function graph. The generated graph + * is cached per node group, so it only has to be generated once after a change. + * + * Node groups are *not* inlined into the lazy-function graph. This could be added in the future as + * it might improve performance in some cases, but generally does not seem necessary. Inlining node + * groups also has disadvantages like making per-node-group caches less useful, resulting in more + * overhead. + * + * Instead, group nodes are just like all other nodes in the lazy-function graph. What makes them + * special is that they reference the lazy-function graph of the group they reference. + * + * During lazy-function graph generation, a mapping between the #bNodeTree and + * #lazy_function::Graph is build that can be used when evaluating the graph (e.g. for logging). + */ + +#include "FN_lazy_function_graph.hh" +#include "FN_lazy_function_graph_executor.hh" + +#include "NOD_geometry_nodes_log.hh" +#include "NOD_multi_function.hh" + +#include "BLI_compute_context.hh" + +struct Object; +struct Depsgraph; + +namespace blender::nodes { + +namespace lf = fn::lazy_function; +using lf::LazyFunction; + +/** + * Data that is passed into geometry nodes evaluation from the modifier. + */ +struct GeoNodesModifierData { + /** Object that is currently evaluated. */ + const Object *self_object = nullptr; + /** Depsgraph that is evaluating the modifier. */ + Depsgraph *depsgraph = nullptr; + /** Optional logger. */ + geo_eval_log::GeoModifierLog *eval_log = nullptr; + /** + * Some nodes should be executed even when their output is not used (e.g. active viewer nodes and + * the node groups they are contained in). + */ + const MultiValueMap<ComputeContextHash, const lf::FunctionNode *> *side_effect_nodes; +}; + +/** + * Custom user data that is passed to every geometry nodes related lazy-function evaluation. + */ +struct GeoNodesLFUserData : public lf::UserData { + /** + * Data from the modifier that is being evaluated. + */ + GeoNodesModifierData *modifier_data = nullptr; + /** + * Current compute context. This is different depending in the (nested) node group that is being + * evaluated. + */ + const ComputeContext *compute_context = nullptr; +}; + +/** + * Contains the mapping between the #bNodeTree and the corresponding lazy-function graph. + * This is *not* a one-to-one mapping. + */ +struct GeometryNodeLazyFunctionGraphMapping { + /** + * Contains mapping of sockets for special nodes like group input and group output. + */ + Map<const bNodeSocket *, lf::Socket *> dummy_socket_map; + /** + * The inputs sockets in the graph. Multiple group input nodes are combined into one in the + * lazy-function graph. + */ + Vector<lf::OutputSocket *> group_input_sockets; + /** + * A mapping used for logging intermediate values. + */ + MultiValueMap<const lf::Socket *, const bNodeSocket *> bsockets_by_lf_socket_map; + /** + * Mappings for some special node types. Generally, this mapping does not exist for all node + * types, so better have more specialized mappings for now. + */ + Map<const bNode *, const lf::FunctionNode *> group_node_map; + Map<const bNode *, const lf::FunctionNode *> viewer_node_map; +}; + +/** + * Data that is cached for every #bNodeTree. + */ +struct GeometryNodesLazyFunctionGraphInfo { + /** + * Allocator used for many things contained in this struct. + */ + LinearAllocator<> allocator; + /** + * Many nodes are implemented as multi-functions. So this contains a mapping from nodes to their + * corresponding multi-functions. + */ + std::unique_ptr<NodeMultiFunctions> node_multi_functions; + /** + * Many lazy-functions are build for the lazy-function graph. Since the graph does not own them, + * we have to keep track of them separately. + */ + Vector<std::unique_ptr<LazyFunction>> functions; + /** + * Many sockets have default values. Since those are not owned by the lazy-function graph, we + * have to keep track of them separately. This only owns the values, the memory is owned by the + * allocator above. + */ + Vector<GMutablePointer> values_to_destruct; + /** + * The actual lazy-function graph. + */ + lf::Graph graph; + /** + * Mappings between the lazy-function graph and the #bNodeTree. + */ + GeometryNodeLazyFunctionGraphMapping mapping; + + GeometryNodesLazyFunctionGraphInfo(); + ~GeometryNodesLazyFunctionGraphInfo(); +}; + +/** + * Logs intermediate values from the lazy-function graph evaluation into #GeoModifierLog based on + * the mapping between the lazy-function graph and the corresponding #bNodeTree. + */ +class GeometryNodesLazyFunctionLogger : public fn::lazy_function::GraphExecutor::Logger { + private: + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info_; + + public: + GeometryNodesLazyFunctionLogger(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info); + void log_socket_value(const fn::lazy_function::Socket &lf_socket, + GPointer value, + const fn::lazy_function::Context &context) const override; + void dump_when_outputs_are_missing(const lf::FunctionNode &node, + Span<const lf::OutputSocket *> missing_sockets, + const lf::Context &context) const override; + void dump_when_input_is_set_twice(const lf::InputSocket &target_socket, + const lf::OutputSocket &from_socket, + const lf::Context &context) const override; +}; + +/** + * Tells the lazy-function graph evaluator which nodes have side effects based on the current + * context. For example, the same viewer node can have side effects in one context, but not in + * another (depending on e.g. which tree path is currently viewed in the node editor). + */ +class GeometryNodesLazyFunctionSideEffectProvider + : public fn::lazy_function::GraphExecutor::SideEffectProvider { + private: + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info_; + + public: + GeometryNodesLazyFunctionSideEffectProvider( + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info); + Vector<const lf::FunctionNode *> get_nodes_with_side_effects( + const lf::Context &context) const override; +}; + +/** + * Main function that converts a #bNodeTree into a lazy-function graph. If the graph has been + * generated already, nothing is done. Under some circumstances a valid graph cannot be created. In + * those cases null is returned. + */ +const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( + const bNodeTree &btree); + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh new file mode 100644 index 00000000000..dd4868b6ba0 --- /dev/null +++ b/source/blender/nodes/NOD_geometry_nodes_log.hh @@ -0,0 +1,340 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * Many geometry nodes related UI features need access to data produced during evaluation. Not only + * is the final output required but also the intermediate results. Those features include attribute + * search, node warnings, socket inspection and the viewer node. + * + * This file provides the system for logging data during evaluation and accessing the data after + * evaluation. Geometry nodes is executed by a modifier, therefore the "root" of logging is + * #GeoModifierLog which will contain all data generated in a modifier. + * + * The system makes a distinction between "loggers" and the "log": + * - Logger (#GeoTreeLogger): Is used during geometry nodes evaluation. Each thread logs data + * independently to avoid communication between threads. Logging should generally be fast. + * Generally, the logged data is just dumped into simple containers. Any processing of the data + * happens later if necessary. This is important for performance, because in practice, most of + * the logged data is never used again. So any processing of the data is likely to be a waste of + * resources. + * - Log (#GeoTreeLog, #GeoNodeLog): Those are used when accessing logged data in UI code. They + * contain and cache preprocessed data produced during logging. The log combines data from all + * thread-local loggers to provide simple access. Importantly, the (preprocessed) log is only + * created when it is actually used by UI code. + */ + +#include <chrono> + +#include "BLI_compute_context.hh" +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_generic_pointer.hh" +#include "BLI_multi_value_map.hh" + +#include "BKE_attribute.h" +#include "BKE_geometry_set.hh" + +#include "FN_field.hh" + +#include "DNA_node_types.h" + +struct SpaceNode; +struct SpaceSpreadsheet; +struct NodesModifierData; + +namespace blender::nodes::geo_eval_log { + +using fn::GField; + +enum class NodeWarningType { + Error, + Warning, + Info, +}; + +struct NodeWarning { + NodeWarningType type; + std::string message; +}; + +enum class NamedAttributeUsage { + None = 0, + Read = 1 << 0, + Write = 1 << 1, + Remove = 1 << 2, +}; +ENUM_OPERATORS(NamedAttributeUsage, NamedAttributeUsage::Remove); + +/** + * Values of different types are logged differently. This is necessary because some types are so + * simple that we can log them entirely (e.g. `int`), while we don't want to log all intermediate + * geometries in their entirety. + * + * #ValueLog is a base class for the different ways we log values. + */ +class ValueLog { + public: + virtual ~ValueLog() = default; +}; + +/** + * Simplest logger. It just stores a copy of the entire value. This is used for most simple types + * like `int`. + */ +class GenericValueLog : public ValueLog { + public: + /** + * This is owning the value, but not the memory. + */ + GMutablePointer value; + + GenericValueLog(const GMutablePointer value) : value(value) + { + } + + ~GenericValueLog(); +}; + +/** + * Fields are not logged entirely, because they might contain arbitrarily large data (e.g. + * geometries that are sampled). Instead, only the data needed for UI features is logged. + */ +class FieldInfoLog : public ValueLog { + public: + const CPPType &type; + Vector<std::string> input_tooltips; + + FieldInfoLog(const GField &field); +}; + +struct GeometryAttributeInfo { + std::string name; + /** Can be empty when #name does not actually exist on a geometry yet. */ + std::optional<eAttrDomain> domain; + std::optional<eCustomDataType> data_type; +}; + +/** + * Geometries are not logged entirely, because that would result in a lot of time and memory + * overhead. Instead, only the data needed for UI features is logged. + */ +class GeometryInfoLog : public ValueLog { + public: + Vector<GeometryAttributeInfo> attributes; + Vector<GeometryComponentType> component_types; + + struct MeshInfo { + int verts_num, edges_num, faces_num; + }; + struct CurveInfo { + int splines_num; + }; + struct PointCloudInfo { + int points_num; + }; + struct InstancesInfo { + int instances_num; + }; + struct EditDataInfo { + bool has_deformed_positions; + bool has_deform_matrices; + }; + + std::optional<MeshInfo> mesh_info; + std::optional<CurveInfo> curve_info; + std::optional<PointCloudInfo> pointcloud_info; + std::optional<InstancesInfo> instances_info; + std::optional<EditDataInfo> edit_data_info; + + GeometryInfoLog(const GeometrySet &geometry_set); +}; + +/** + * Data logged by a viewer node when it is executed. In this case, we do want to log the entire + * geometry. + */ +class ViewerNodeLog { + public: + GeometrySet geometry; + GField field; +}; + +using Clock = std::chrono::steady_clock; +using TimePoint = Clock::time_point; + +/** + * Logs all data for a specific geometry node tree in a specific context. When the same node group + * is used in multiple times each instantiation will have a separate logger. + */ +class GeoTreeLogger { + public: + std::optional<ComputeContextHash> parent_hash; + std::optional<std::string> group_node_name; + Vector<ComputeContextHash> children_hashes; + + LinearAllocator<> *allocator = nullptr; + + struct WarningWithNode { + std::string node_name; + NodeWarning warning; + }; + struct SocketValueLog { + std::string node_name; + std::string socket_identifier; + destruct_ptr<ValueLog> value; + }; + struct NodeExecutionTime { + std::string node_name; + TimePoint start; + TimePoint end; + }; + struct ViewerNodeLogWithNode { + std::string node_name; + destruct_ptr<ViewerNodeLog> viewer_log; + }; + struct AttributeUsageWithNode { + std::string node_name; + std::string attribute_name; + NamedAttributeUsage usage; + }; + struct DebugMessage { + std::string node_name; + std::string message; + }; + + Vector<WarningWithNode> node_warnings; + Vector<SocketValueLog> input_socket_values; + Vector<SocketValueLog> output_socket_values; + Vector<NodeExecutionTime> node_execution_times; + Vector<ViewerNodeLogWithNode, 0> viewer_node_logs; + Vector<AttributeUsageWithNode, 0> used_named_attributes; + Vector<DebugMessage, 0> debug_messages; + + GeoTreeLogger(); + ~GeoTreeLogger(); + + void log_value(const bNode &node, const bNodeSocket &socket, GPointer value); + void log_viewer_node(const bNode &viewer_node, const GeometrySet &geometry, const GField &field); +}; + +/** + * Contains data that has been logged for a specific node in a context. So when the node is in a + * node group that is used multiple times, there will be a different #GeoNodeLog for every + * instance. + * + * By default, not all of the info below is valid. A #GeoTreeLog::ensure_* method has to be called + * first. + */ +class GeoNodeLog { + public: + /** Warnings generated for that node. */ + Vector<NodeWarning> warnings; + /** + * Time spend in that node. For node groups this is the sum of the run times of the nodes + * inside. + */ + std::chrono::nanoseconds run_time{0}; + /** Maps from socket identifiers to their values. */ + Map<std::string, ValueLog *> input_values_; + Map<std::string, ValueLog *> output_values_; + /** Maps from attribute name to their usage flags. */ + Map<std::string, NamedAttributeUsage> used_named_attributes; + /** Messages that are used for debugging purposes during development. */ + Vector<std::string> debug_messages; + + GeoNodeLog(); + ~GeoNodeLog(); +}; + +class GeoModifierLog; + +/** + * Contains data that has been logged for a specific node group in a context. If the same node + * group is used multiple times, there will be a different #GeoTreeLog for every instance. + * + * This contains lazily evaluated data. Call the corresponding `ensure_*` methods before accessing + * data. + */ +class GeoTreeLog { + private: + GeoModifierLog *modifier_log_; + Vector<GeoTreeLogger *> tree_loggers_; + VectorSet<ComputeContextHash> children_hashes_; + bool reduced_node_warnings_ = false; + bool reduced_node_run_times_ = false; + bool reduced_socket_values_ = false; + bool reduced_viewer_node_logs_ = false; + bool reduced_existing_attributes_ = false; + bool reduced_used_named_attributes_ = false; + bool reduced_debug_messages_ = false; + + public: + Map<std::string, GeoNodeLog> nodes; + Map<std::string, ViewerNodeLog *, 0> viewer_node_logs; + Vector<NodeWarning> all_warnings; + std::chrono::nanoseconds run_time_sum{0}; + Vector<const GeometryAttributeInfo *> existing_attributes; + Map<std::string, NamedAttributeUsage> used_named_attributes; + + GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers); + ~GeoTreeLog(); + + void ensure_node_warnings(); + void ensure_node_run_time(); + void ensure_socket_values(); + void ensure_viewer_node_logs(); + void ensure_existing_attributes(); + void ensure_used_named_attributes(); + void ensure_debug_messages(); + + ValueLog *find_socket_value_log(const bNodeSocket &query_socket); +}; + +/** + * There is one #GeoModifierLog for every modifier that evaluates geometry nodes. It contains all + * the loggers that are used during evaluation as well as the preprocessed logs that are used by UI + * code. + */ +class GeoModifierLog { + private: + /** Data that is stored for each thread. */ + struct LocalData { + /** Each thread has its own allocator. */ + LinearAllocator<> allocator; + /** + * Store a separate #GeoTreeLogger for each instance of the corresponding node group (e.g. + * when the same node group is used multiple times). + */ + Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> tree_logger_by_context; + }; + + /** Container for all thread-local data. */ + threading::EnumerableThreadSpecific<LocalData> data_per_thread_; + /** + * A #GeoTreeLog for every compute context. Those are created lazily when requested by UI code. + */ + Map<ComputeContextHash, std::unique_ptr<GeoTreeLog>> tree_logs_; + + public: + GeoModifierLog(); + ~GeoModifierLog(); + + /** + * Get a thread-local logger for the current node tree. + */ + GeoTreeLogger &get_local_tree_logger(const ComputeContext &compute_context); + + /** + * Get a log a specific node tree instance. + */ + GeoTreeLog &get_tree_log(const ComputeContextHash &compute_context_hash); + + /** + * Utility accessor to logged data. + */ + static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode); + static const ViewerNodeLog *find_viewer_node_log_for_spreadsheet( + const SpaceSpreadsheet &sspreadsheet); +}; + +} // namespace blender::nodes::geo_eval_log diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index 21a94d9192b..676bf03927e 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -6,8 +6,6 @@ #include "DNA_node_types.h" -#include "NOD_derived_node_tree.hh" - namespace blender::nodes { using namespace fn::multi_function_types; @@ -60,9 +58,9 @@ class NodeMultiFunctions { Map<const bNode *, Item> map_; public: - NodeMultiFunctions(const DerivedNodeTree &tree); + NodeMultiFunctions(const bNodeTree &tree); - const Item &try_get(const DNode &node) const; + const Item &try_get(const bNode &node) const; }; /* -------------------------------------------------------------------- */ @@ -107,10 +105,10 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar /** \name #NodeMultiFunctions Inline Methods * \{ */ -inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const +inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const bNode &node) const { static Item empty_item; - const Item *item = map_.lookup_ptr(node.bnode()); + const Item *item = map_.lookup_ptr(&node); if (item == nullptr) { return empty_item; } diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index d8b8c354230..42755b2e8dd 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -92,6 +92,10 @@ class SocketDeclaration { * realtime_compositor::InputDescriptor for more information. */ int compositor_domain_priority_ = 0; + /** This input shouldn't be realized on the operation domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + bool compositor_skip_realization_ = false; + /** This input expects a single value and can't operate on non-single values. See * realtime_compositor::InputDescriptor for more information. */ bool compositor_expects_single_value_ = false; @@ -133,6 +137,7 @@ class SocketDeclaration { const OutputFieldDependency &output_field_dependency() const; int compositor_domain_priority() const; + bool compositor_skip_realization() const; bool compositor_expects_single_value() const; protected: @@ -257,6 +262,14 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { return *(Self *)this; } + /** This input shouldn't be realized on the operation domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + Self &compositor_skip_realization(bool value = true) + { + decl_->compositor_skip_realization_ = value; + return *(Self *)this; + } + /** This input expects a single value and can't operate on non-single values. See * realtime_compositor::InputDescriptor for more information. */ Self &compositor_expects_single_value(bool value = true) @@ -460,6 +473,11 @@ inline int SocketDeclaration::compositor_domain_priority() const return compositor_domain_priority_; } +inline bool SocketDeclaration::compositor_skip_realization() const +{ + return compositor_skip_realization_; +} + inline bool SocketDeclaration::compositor_expects_single_value() const { return compositor_expects_single_value_; diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc index cb1d93fe10b..630f18361e3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -5,12 +5,27 @@ * \ingroup cmpnodes */ +#include <cstdint> + +#include "BLI_array.hh" +#include "BLI_assert.h" +#include "BLI_index_range.hh" +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "RE_pipeline.h" + +#include "GPU_state.h" +#include "GPU_texture.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -18,6 +33,8 @@ namespace blender::nodes::node_composite_blur_cc { +NODE_STORAGE_FUNCS(NodeBlurData) + static void cmp_node_blur_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); @@ -75,13 +92,395 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point using namespace blender::realtime_compositor; +/* A helper class that computes and caches a 1D GPU texture containing the weights of the separable + * filter of the given type and radius. The filter is assumed to be symmetric, because the filter + * functions are all even functions. Consequently, only the positive half of the filter is computed + * and the shader takes that into consideration. */ +class SymmetricSeparableBlurWeights { + private: + float radius_ = 1.0f; + int type_ = R_FILTER_GAUSS; + GPUTexture *texture_ = nullptr; + + public: + ~SymmetricSeparableBlurWeights() + { + if (texture_) { + GPU_texture_free(texture_); + } + } + + /* Check if a texture containing the weights was already computed for the given filter type and + * radius. If such texture exists, do nothing, otherwise, free the already computed texture and + * recompute it with the given filter type and radius. */ + void update(float radius, int type) + { + if (texture_ && type == type_ && radius == radius_) { + return; + } + + if (texture_) { + GPU_texture_free(texture_); + } + + /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only + * compute half of it and no doubling happens. We add 1 to make sure the filter size is always + * odd and there is a center weight. */ + const int size = math::ceil(radius) + 1; + Array<float> weights(size); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(type, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Second, compute the other weights in the positive direction, making sure to add double the + * weight to the sum of weights because the filter is symmetric and we only loop over half of + * it. Skip the center weight already computed by dropping the front index. */ + const float scale = radius > 0.0f ? 1.0f / radius : 0.0f; + for (const int i : weights.index_range().drop_front(1)) { + const float weight = RE_filter_value(type, i * scale); + weights[i] = weight; + sum += weight * 2.0f; + } + + /* Finally, normalize the weights. */ + for (const int i : weights.index_range()) { + weights[i] /= sum; + } + + texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data()); + + type_ = type; + radius_ = radius; + } + + void bind_as_texture(GPUShader *shader, const char *texture_name) + { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); + } + + void unbind_as_texture() + { + GPU_texture_unbind(texture_); + } +}; + +/* A helper class that computes and caches a 2D GPU texture containing the weights of the filter of + * the given type and radius. The filter is assumed to be symmetric, because the filter functions + * are evaluated on the normalized distance to the center. Consequently, only the upper right + * quadrant are computed and the shader takes that into consideration. */ +class SymmetricBlurWeights { + private: + int type_ = R_FILTER_GAUSS; + float2 radius_ = float2(1.0f); + GPUTexture *texture_ = nullptr; + + public: + ~SymmetricBlurWeights() + { + if (texture_) { + GPU_texture_free(texture_); + } + } + + /* Check if a texture containing the weights was already computed for the given filter type and + * radius. If such texture exists, do nothing, otherwise, free the already computed texture and + * recompute it with the given filter type and radius. */ + void update(float2 radius, int type) + { + if (texture_ && type == type_ && radius == radius_) { + return; + } + + if (texture_) { + GPU_texture_free(texture_); + } + + /* The full size of filter is double the radius plus 1, but since the filter is symmetric, we + * only compute a single quadrant of it and so no doubling happens. We add 1 to make sure the + * filter size is always odd and there is a center weight. */ + const float2 scale = math::safe_divide(float2(1.0f), radius); + const int2 size = int2(math::ceil(radius)) + int2(1); + Array<float> weights(size.x * size.y); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(type, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Then, compute the weights along the positive x axis, making sure to add double the weight to + * the sum of weights because the filter is symmetric and we only loop over the positive half + * of the x axis. Skip the center weight already computed by dropping the front index. */ + for (const int x : IndexRange(size.x).drop_front(1)) { + const float weight = RE_filter_value(type, x * scale.x); + weights[x] = weight; + sum += weight * 2.0f; + } + + /* Then, compute the weights along the positive y axis, making sure to add double the weight to + * the sum of weights because the filter is symmetric and we only loop over the positive half + * of the y axis. Skip the center weight already computed by dropping the front index. */ + for (const int y : IndexRange(size.y).drop_front(1)) { + const float weight = RE_filter_value(type, y * scale.y); + weights[size.x * y] = weight; + sum += weight * 2.0f; + } + + /* Then, compute the other weights in the upper right quadrant, making sure to add quadruple + * the weight to the sum of weights because the filter is symmetric and we only loop over one + * quadrant of it. Skip the weights along the y and x axis already computed by dropping the + * front index. */ + for (const int y : IndexRange(size.y).drop_front(1)) { + for (const int x : IndexRange(size.x).drop_front(1)) { + const float weight = RE_filter_value(type, math::length(float2(x, y) * scale)); + weights[size.x * y + x] = weight; + sum += weight * 4.0f; + } + } + + /* Finally, normalize the weights. */ + for (const int y : IndexRange(size.y)) { + for (const int x : IndexRange(size.x)) { + weights[size.x * y + x] /= sum; + } + } + + texture_ = GPU_texture_create_2d("Weights", size.x, size.y, 1, GPU_R16F, weights.data()); + + type_ = type; + radius_ = radius; + } + + void bind_as_texture(GPUShader *shader, const char *texture_name) + { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); + } + + void unbind_as_texture() + { + GPU_texture_unbind(texture_); + } +}; + class BlurOperation : public NodeOperation { + private: + /* Cached symmetric blur weights. */ + SymmetricBlurWeights blur_weights_; + /* Cached symmetric blur weights for the separable horizontal pass. */ + SymmetricSeparableBlurWeights blur_horizontal_weights_; + /* Cached symmetric blur weights for the separable vertical pass. */ + SymmetricSeparableBlurWeights blur_vertical_weights_; + public: using NodeOperation::NodeOperation; void execute() override { - get_input("Image").pass_through(get_result("Image")); + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + if (use_separable_filter()) { + GPUTexture *horizontal_pass_result = execute_separable_blur_horizontal_pass(); + execute_separable_blur_vertical_pass(horizontal_pass_result); + } + else { + execute_blur(); + } + } + + void execute_blur() + { + GPUShader *shader = shader_manager().get("compositor_symmetric_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamma); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + blur_weights_.update(compute_blur_radius(), node_storage(bnode()).filtertype); + blur_weights_.bind_as_texture(shader, "weights_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ + domain.size += int2(math::ceil(compute_blur_radius())) * 2; + } + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + blur_weights_.unbind_as_texture(); + } + + GPUTexture *execute_separable_blur_horizontal_pass() + { + GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + GPU_shader_uniform_1b(shader, "gamma_correct_input", node_storage(bnode()).gamma); + GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", false); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + blur_horizontal_weights_.update(compute_blur_radius().x, node_storage(bnode()).filtertype); + blur_horizontal_weights_.bind_as_texture(shader, "weights_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + domain.size.x += static_cast<int>(math::ceil(compute_blur_radius().x)) * 2; + } + + /* We allocate an output image of a transposed size, that is, with a height equivalent to the + * width of the input and vice versa. This is done as a performance optimization. The shader + * will blur the image horizontally and write it to the intermediate output transposed. Then + * the vertical pass will execute the same horizontal blur shader, but since its input is + * transposed, it will effectively do a vertical blur and write to the output transposed, + * effectively undoing the transposition in the horizontal pass. This is done to improve + * spatial cache locality in the shader and to avoid having two separate shaders for each blur + * pass. */ + const int2 transposed_domain = int2(domain.size.y, domain.size.x); + + GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(horizontal_pass_result, image_unit); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + input_image.unbind_as_texture(); + blur_horizontal_weights_.unbind_as_texture(); + GPU_texture_image_unbind(horizontal_pass_result); + + return horizontal_pass_result; + } + + void execute_separable_blur_vertical_pass(GPUTexture *horizontal_pass_result) + { + GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + GPU_shader_uniform_1b(shader, "gamma_correct_input", false); + GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", node_storage(bnode()).gamma); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(horizontal_pass_result, texture_image_unit); + + blur_vertical_weights_.update(compute_blur_radius().y, node_storage(bnode()).filtertype); + blur_vertical_weights_.bind_as_texture(shader, "weights_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ + domain.size += int2(math::ceil(compute_blur_radius())) * 2; + } + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + /* Notice that the domain is transposed, see the note on the horizontal pass method for more + * information on the reasoning behind this. */ + compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x)); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + blur_vertical_weights_.unbind_as_texture(); + GPU_texture_unbind(horizontal_pass_result); + } + + float2 compute_blur_radius() + { + const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 1.0f); + + if (!node_storage(bnode()).relative) { + return float2(node_storage(bnode()).sizex, node_storage(bnode()).sizey) * size; + } + + int2 image_size = get_input("Image").domain().size; + switch (node_storage(bnode()).aspect) { + case CMP_NODE_BLUR_ASPECT_Y: + image_size.y = image_size.x; + break; + case CMP_NODE_BLUR_ASPECT_X: + image_size.x = image_size.y; + break; + default: + BLI_assert(node_storage(bnode()).aspect == CMP_NODE_BLUR_ASPECT_NONE); + break; + } + + return float2(image_size) * get_size_factor() * size; + } + + /* Returns true if the operation does nothing and the input can be passed through. */ + bool is_identity() + { + const Result &input = get_input("Image"); + /* Single value inputs can't be blurred and are returned as is. */ + if (input.is_single_value()) { + return true; + } + + /* Zero blur radius. The operation does nothing and the input can be passed through. */ + if (compute_blur_radius() == float2(0.0)) { + return true; + } + + return false; + } + + /* The blur node can operate with different filter types, evaluated on the normalized distance to + * the center of the filter. Some of those filters are separable and can be computed as such. If + * the bokeh member is disabled in the node, then the filter is always computed as separable even + * if it is not in fact separable, in which case, the used filter is a cheaper approximation to + * the actual filter. If the bokeh member is enabled, then the filter is computed as separable if + * it is in fact separable and as a normal 2D filter otherwise. */ + bool use_separable_filter() + { + if (!node_storage(bnode()).bokeh) { + return true; + } + + /* Both Box and Gaussian filters are separable. The rest is not. */ + switch (node_storage(bnode()).filtertype) { + case R_FILTER_BOX: + case R_FILTER_GAUSS: + case R_FILTER_FAST_GAUSS: + return true; + default: + return false; + } + } + + float2 get_size_factor() + { + return float2(node_storage(bnode()).percentx, node_storage(bnode()).percenty) / 100.0f; + } + + bool get_extend_bounds() + { + return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index 538f00af12d..9c0617ee8c3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -5,10 +5,16 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_texture.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -18,10 +24,22 @@ namespace blender::nodes::node_composite_bokehblur_cc { static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::Color>(N_("Bokeh")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).max(10.0f); - b.add_input<decl::Float>(N_("Bounding box")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Bokeh")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_skip_realization(); + b.add_input<decl::Float>(N_("Size")) + .default_value(1.0f) + .min(0.0f) + .max(10.0f) + .compositor_domain_priority(1); + b.add_input<decl::Float>(N_("Bounding box")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(2); b.add_output<decl::Color>(N_("Image")); } @@ -47,7 +65,82 @@ class BokehBlurOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + GPUShader *shader = shader_manager().get("compositor_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1i(shader, "radius", compute_blur_radius()); + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Result &input_weights = get_input("Bokeh"); + input_weights.bind_as_texture(shader, "weights_tx"); + + const Result &input_mask = get_input("Bounding box"); + input_mask.bind_as_texture(shader, "mask_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ + domain.size += int2(compute_blur_radius() * 2); + } + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + input_weights.unbind_as_texture(); + input_mask.unbind_as_texture(); + } + + int compute_blur_radius() + { + const int2 image_size = get_input("Image").domain().size; + const int max_size = math::max(image_size.x, image_size.y); + + /* The [0, 10] range of the size is arbitrary and is merely in place to avoid very long + * computations of the bokeh blur. */ + const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 10.0f); + + /* The 100 divisor is arbitrary and was chosen using visual judgment. */ + return size * (max_size / 100.0f); + } + + bool is_identity() + { + const Result &input = get_input("Image"); + if (input.is_single_value()) { + return true; + } + + if (compute_blur_radius() == 0) { + return true; + } + + /* This input is, in fact, a boolean mask. If it is zero, no blurring will take place. + * Otherwise, the blurring will take place ignoring the value of the input entirely. */ + const Result &bounding_box = get_input("Bounding box"); + if (bounding_box.is_single_value() && bounding_box.get_float_value() == 0.0) { + return true; + } + + return false; + } + + bool get_extend_bounds() + { + return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc index 4567464a547..c4e42f8247d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -5,6 +5,9 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + #include "COM_node_operation.hh" #include "node_composite_util.hh" @@ -27,8 +30,34 @@ class PixelateOperation : public NodeOperation { void execute() override { + /* It might seems strange that the input is passed through without any processing, but note + * that the actual processing happens inside the domain realization input processor of the + * input. Indeed, the pixelate node merely realizes its input on a smaller-sized domain that + * matches its apparent size, that is, its size after the domain transformation. The pixelate + * node has no effect if the input is scaled-up. See the compute_domain method for more + * information. */ get_input("Color").pass_through(get_result("Color")); } + + /* Compute a smaller-sized domain that matches the apparent size of the input while having a unit + * scale transformation, see the execute method for more information. */ + Domain compute_domain() override + { + Domain domain = get_input("Color").domain(); + + /* Get the scaling component of the domain transformation, but make sure it doesn't exceed 1, + * because pixelation should only happen if the input is scaled down. */ + const float2 scale = math::min(float2(1.0f), domain.transformation.scale_2d()); + + /* Multiply the size of the domain by its scale to match its apparent size, but make sure it is + * at least 1 pixel in both axis. */ + domain.size = math::max(int2(float2(domain.size) * scale), int2(1)); + + /* Reset the scale of the transformation by transforming it with the inverse of the scale. */ + domain.transformation *= float3x3::from_scale(math::safe_divide(float2(1.0f), scale)); + + return domain; + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index 8b43ae8c9ca..eb2d7162c69 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -5,6 +5,11 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_float3x3.hh" +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" + #include "RNA_access.h" #include "UI_interface.h" @@ -20,16 +25,26 @@ namespace blender::nodes::node_composite_scale_cc { static void cmp_node_scale_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("X")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX); - b.add_input<decl::Float>(N_("Y")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("X")) + .default_value(1.0f) + .min(0.0001f) + .max(CMP_SCALE_MAX) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Y")) + .default_value(1.0f) + .min(0.0001f) + .max(CMP_SCALE_MAX) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } static void node_composite_update_scale(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; - bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE); + bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE); /* Only show X/Y scale factor inputs for modes using them! */ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { @@ -43,7 +58,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin { uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); - if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) { + if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) { uiLayout *row; uiItemR(layout, ptr, @@ -65,7 +80,129 @@ class ScaleOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + const float3x3 transformation = float3x3::from_translation_rotation_scale( + get_translation(), 0.0f, get_scale()); + + result.transform(transformation); + result.get_realization_options().interpolation = Interpolation::Bilinear; + } + + float2 get_scale() + { + switch (get_scale_method()) { + case CMP_NODE_SCALE_RELATIVE: + return get_scale_relative(); + case CMP_NODE_SCALE_ABSOLUTE: + return get_scale_absolute(); + case CMP_NODE_SCALE_RENDER_PERCENT: + return get_scale_render_percent(); + case CMP_NODE_SCALE_RENDER_SIZE: + return get_scale_render_size(); + default: + BLI_assert_unreachable(); + return float2(1.0f); + } + } + + /* Scale by the input factors. */ + float2 get_scale_relative() + { + return float2(get_input("X").get_float_value_default(1.0f), + get_input("Y").get_float_value_default(1.0f)); + } + + /* Scale such that the new size matches the input absolute size. */ + float2 get_scale_absolute() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f), + get_input("Y").get_float_value_default(1.0f)); + return absolute_size / input_size; + } + + /* Scale by the render resolution percentage. */ + float2 get_scale_render_percent() + { + return float2(context().get_scene()->r.size / 100.0f); + } + + float2 get_scale_render_size() + { + switch (get_scale_render_size_method()) { + case CMP_NODE_SCALE_RENDER_SIZE_STRETCH: + return get_scale_render_size_stretch(); + case CMP_NODE_SCALE_RENDER_SIZE_FIT: + return get_scale_render_size_fit(); + case CMP_NODE_SCALE_RENDER_SIZE_CROP: + return get_scale_render_size_crop(); + default: + BLI_assert_unreachable(); + return float2(1.0f); + } + } + + /* Scale such that the new size matches the render size. Since the input is freely scaled, it is + * potentially stretched, hence the name. */ + float2 get_scale_render_size_stretch() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 render_size = float2(context().get_output_size()); + return render_size / input_size; + } + + /* Scale such that the dimension with the smaller scaling factor matches that of the render size + * while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to + * exceed the render size region due to its larger scaling factor, the image is said to be fit + * inside that region, hence the name. */ + float2 get_scale_render_size_fit() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 render_size = float2(context().get_output_size()); + const float2 scale = render_size / input_size; + return float2(math::min(scale.x, scale.y)); + } + + /* Scale such that the dimension with the larger scaling factor matches that of the render size + * while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed + * the render size region due to its lower scaling factor, the image will be cropped inside that + * region, hence the name. */ + float2 get_scale_render_size_crop() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 render_size = float2(context().get_output_size()); + const float2 scale = render_size / input_size; + return float2(math::max(scale.x, scale.y)); + } + + float2 get_translation() + { + /* Only the render size option supports offset translation. */ + if (get_scale_method() != CMP_NODE_SCALE_RENDER_SIZE) { + return float2(0.0f); + } + + /* Translate by the offset factor relative to the new size. */ + const float2 input_size = float2(get_input("Image").domain().size); + return get_offset() * input_size * get_scale(); + } + + CMPNodeScaleMethod get_scale_method() + { + return (CMPNodeScaleMethod)bnode().custom1; + } + + CMPNodeScaleRenderSizeMethod get_scale_render_size_method() + { + return (CMPNodeScaleRenderSizeMethod)bnode().custom2; + } + + float2 get_offset() + { + return float2(bnode().custom3, bnode().custom4); } }; diff --git a/source/blender/nodes/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc index 58ded7aadd2..ef4daf94bbe 100644 --- a/source/blender/nodes/geometry/node_geometry_exec.cc +++ b/source/blender/nodes/geometry/node_geometry_exec.cc @@ -4,3 +4,4 @@ #include "NOD_geometry_exec.hh" BLI_CPP_TYPE_MAKE(GeometrySet, GeometrySet, CPPTypeFlags::Printable); +BLI_CPP_TYPE_MAKE(GeometrySetVector, blender::Vector<GeometrySet>, CPPTypeFlags::None); diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 69938f3e447..c8c58945bce 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -93,7 +93,7 @@ static void node_geo_exec(GeoNodeExecParams params) /* The instance transform matrices are owned by the instance group, so we have to * keep all of them around for use during the boolean operation. */ Vector<bke::GeometryInstanceGroup> set_groups; - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Mesh 2"); + Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Mesh 2"); for (const GeometrySet &geometry_set : geometry_sets) { bke::geometry_set_gather_instances(geometry_set, set_groups); } @@ -154,7 +154,7 @@ static void node_geo_exec(GeoNodeExecParams params) /* Store intersecting edges in attribute. */ if (attribute_outputs.intersecting_edges_id) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*result); + MutableAttributeAccessor attributes = result->attributes_for_write(); SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 443f67be421..0d3ae47e712 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -9,12 +9,12 @@ #include "NOD_socket_search_link.hh" +#include "GEO_trim_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_trim_cc { -using blender::attribute_math::mix2; - NODE_STORAGE_FUNCS(NodeGeometryCurveTrim) static void node_declare(NodeDeclarationBuilder &b) @@ -108,394 +108,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -struct TrimLocation { - /* Control point index at the start side of the trim location. */ - int left_index; - /* Control point index at the end of the trim location's segment. */ - int right_index; - /* The factor between the left and right indices. */ - float factor; -}; - -template<typename T> -static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num) -{ - BLI_assert(start_index + num - 1 <= data.size()); - memmove(data.data(), &data[start_index], sizeof(T) * num); -} - -/* Shift slice to start of span and modifies start and end data. */ -template<typename T> -static void linear_trim_data(const TrimLocation &start, - const TrimLocation &end, - MutableSpan<T> data) -{ - const int num = end.right_index - start.left_index + 1; - - if (start.left_index > 0) { - shift_slice_to_start<T>(data, start.left_index, num); - } - - const T start_data = mix2<T>(start.factor, data.first(), data[1]); - const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]); - - data.first() = start_data; - data[num - 1] = end_data; -} - -/** - * Identical operation as #linear_trim_data, but copy data to a new #MutableSpan rather than - * modifying the original data. - */ -template<typename T> -static void linear_trim_to_output_data(const TrimLocation &start, - const TrimLocation &end, - Span<T> src, - MutableSpan<T> dst) -{ - const int num = end.right_index - start.left_index + 1; - - const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); - const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); - - dst.copy_from(src.slice(start.left_index, num)); - dst.first() = start_data; - dst.last() = end_data; -} - -/* Look up the control points to the left and right of factor, and get the factor between them. */ -static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup, - const BezierSpline &spline) -{ - Span<int> offsets = spline.control_point_offsets(); - - const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index); - const int index = offset - offsets.begin(); - - const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index; - const int right = left == (spline.size() - 1) ? 0 : left + 1; - - const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; - const int segment_eval_num = offsets[left + 1] - offsets[left]; - const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f); - - return {left, right, factor}; -} - -static void trim_poly_spline(Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - /* Poly splines have a 1 to 1 mapping between control points and evaluated points. */ - const TrimLocation start = { - start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; - const TrimLocation end = { - end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - - const int num = end.right_index - start.left_index + 1; - - linear_trim_data<float3>(start, end, spline.positions()); - linear_trim_data<float>(start, end, spline.radii()); - linear_trim_data<float>(start, end, spline.tilts()); - - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); - BLI_assert(src); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - linear_trim_data<T>(start, end, src->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - spline.resize(num); -} - -/** - * Trim NURB splines by converting to a poly spline. - */ -static PolySpline trim_nurbs_spline(const Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ - const TrimLocation start = { - start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; - const TrimLocation end = { - end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - - const int num = end.right_index - start.left_index + 1; - - /* Create poly spline and copy trimmed data to it. */ - PolySpline new_spline; - new_spline.resize(num); - - /* Copy generic attribute data. */ - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); - BLI_assert(src); - if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; - } - std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); - BLI_assert(dst); - - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); - linear_trim_to_output_data<T>( - start, end, eval_data.get_internal_span(), dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - linear_trim_to_output_data<float3>( - start, end, spline.evaluated_positions(), new_spline.positions()); - - VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); - linear_trim_to_output_data<float>( - start, end, evaluated_radii.get_internal_span(), new_spline.radii()); - - VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); - linear_trim_to_output_data<float>( - start, end, evaluated_tilts.get_internal_span(), new_spline.tilts()); - - return new_spline; -} - -/** - * Trim Bezier splines by adjusting the first and last handles - * and control points to maintain the original shape. - */ -static void trim_bezier_spline(Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); - - const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline); - TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline); - - const Span<int> control_offsets = bezier_spline.control_point_offsets(); - - /* The number of control points in the resulting spline. */ - const int num = end.right_index - start.left_index + 1; - - /* Trim the spline attributes. Done before end.factor recalculation as it needs - * the original end.factor value. */ - linear_trim_data<float>(start, end, bezier_spline.radii()); - linear_trim_data<float>(start, end, bezier_spline.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); - BLI_assert(src); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - linear_trim_data<T>(start, end, src->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - /* Recalculate end.factor if the `num` is two, because the adjustment in the - * position of the control point of the spline to the left of the new end point will change the - * factor between them. */ - if (num == 2) { - if (start_lookup.factor == 1.0f) { - end.factor = 0.0f; - } - else { - end.factor = (end_lookup.evaluated_index + end_lookup.factor - - (start_lookup.evaluated_index + start_lookup.factor)) / - (control_offsets[end.right_index] - - (start_lookup.evaluated_index + start_lookup.factor)); - end.factor = std::clamp(end.factor, 0.0f, 1.0f); - } - } - - BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion( - start.left_index, start.right_index, start.factor); - - /* Update the start control point parameters so they are used calculating the new end point. */ - bezier_spline.positions()[start.left_index] = start_point.position; - bezier_spline.handle_positions_right()[start.left_index] = start_point.right_handle; - bezier_spline.handle_positions_left()[start.right_index] = start_point.handle_next; - - const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( - end.left_index, end.right_index, end.factor); - - /* If `num` is two, then the start point right handle needs to change to reflect the end point - * previous handle update. */ - if (num == 2) { - start_point.right_handle = end_point.handle_prev; - } - - /* Shift control point position data to start at beginning of array. */ - if (start.left_index > 0) { - shift_slice_to_start(bezier_spline.positions(), start.left_index, num); - shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num); - shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num); - } - - bezier_spline.positions().first() = start_point.position; - bezier_spline.positions()[num - 1] = end_point.position; - - bezier_spline.handle_positions_left().first() = start_point.left_handle; - bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle; - - bezier_spline.handle_positions_right().first() = start_point.right_handle; - bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle; - - /* If there is at least one control point between the endpoints, update the control - * point handle to the right of the start point and to the left of the end point. */ - if (num > 2) { - bezier_spline.handle_positions_left()[start.right_index - start.left_index] = - start_point.handle_next; - bezier_spline.handle_positions_right()[end.left_index - start.left_index] = - end_point.handle_prev; - } - - bezier_spline.resize(num); -} - -static void trim_spline(SplinePtr &spline, - const Spline::LookupResult start, - const Spline::LookupResult end) -{ - switch (spline->type()) { - case CURVE_TYPE_BEZIER: - trim_bezier_spline(*spline, start, end); - break; - case CURVE_TYPE_POLY: - trim_poly_spline(*spline, start, end); - break; - case CURVE_TYPE_NURBS: - spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end)); - break; - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - spline = {}; - } - spline->mark_cache_invalid(); -} - -template<typename T> -static void to_single_point_data(const TrimLocation &trim, MutableSpan<T> data) -{ - data.first() = mix2<T>(trim.factor, data[trim.left_index], data[trim.right_index]); -} -template<typename T> -static void to_single_point_data(const TrimLocation &trim, Span<T> src, MutableSpan<T> dst) -{ - dst.first() = mix2<T>(trim.factor, src[trim.left_index], src[trim.right_index]); -} - -static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup) -{ - BezierSpline &bezier = static_cast<BezierSpline &>(spline); - - const TrimLocation trim = lookup_control_point_position(lookup, bezier); - - const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( - trim.left_index, trim.right_index, trim.factor); - bezier.positions().first() = new_point.position; - bezier.handle_types_left().first() = BEZIER_HANDLE_FREE; - bezier.handle_types_right().first() = BEZIER_HANDLE_FREE; - bezier.handle_positions_left().first() = new_point.left_handle; - bezier.handle_positions_right().first() = new_point.right_handle; - - to_single_point_data<float>(trim, bezier.radii()); - to_single_point_data<float>(trim, bezier.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { - using T = decltype(dummy); - to_single_point_data<T>(trim, data->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - spline.resize(1); -} - -static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup) -{ - const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; - - to_single_point_data<float3>(trim, spline.positions()); - to_single_point_data<float>(trim, spline.radii()); - to_single_point_data<float>(trim, spline.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { - using T = decltype(dummy); - to_single_point_data<T>(trim, data->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - spline.resize(1); -} - -static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup) -{ - /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ - const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; - - /* Create poly spline and copy trimmed data to it. */ - PolySpline new_spline; - new_spline.resize(1); - - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - new_spline.attributes.create(attribute_id, meta_data.data_type); - std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); - std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); - to_single_point_data<T>(trim, eval_data.get_internal_span(), dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - to_single_point_data<float3>(trim, spline.evaluated_positions(), new_spline.positions()); - - VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); - to_single_point_data<float>(trim, evaluated_radii.get_internal_span(), new_spline.radii()); - - VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); - to_single_point_data<float>(trim, evaluated_tilts.get_internal_span(), new_spline.tilts()); - - return new_spline; -} - -static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) -{ - switch (spline->type()) { - case CURVE_TYPE_BEZIER: - to_single_point_bezier(*spline, lookup); - break; - case CURVE_TYPE_POLY: - to_single_point_poly(*spline, lookup); - break; - case CURVE_TYPE_NURBS: - spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup)); - break; - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - spline = {}; - } -} - static void geometry_set_curve_trim(GeometrySet &geometry_set, const GeometryNodeCurveSampleMode mode, Field<float> &start_field, @@ -505,68 +117,49 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, return; } const Curves &src_curves_id = *geometry_set.get_curves_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + if (src_curves.curves_num() == 0) { + return; + } - bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, curves.curves_num()}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; evaluator.add(start_field); evaluator.add(end_field); evaluator.evaluate(); const VArray<float> starts = evaluator.get_evaluated<float>(0); const VArray<float> ends = evaluator.get_evaluated<float>(1); - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(src_curves_id); - MutableSpan<SplinePtr> splines = curve->splines(); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - SplinePtr &spline = splines[i]; - - /* Currently trimming cyclic splines is not supported. It could be in the future though. */ - if (spline->is_cyclic()) { - continue; - } - - if (spline->evaluated_edges_num() == 0) { - continue; - } - - const float length = spline->length(); - if (length == 0.0f) { - continue; - } - - const float start = starts[i]; - const float end = ends[i]; - - /* When the start and end samples are reversed, instead of implicitly reversing the spline - * or switching the parameters, create a single point spline with the end sample point. */ - if (end <= start) { - if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - to_single_point_spline(spline, - spline->lookup_evaluated_length(std::clamp(start, 0.0f, length))); - } - else { - to_single_point_spline(spline, - spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f))); - } - continue; - } - - if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - trim_spline(spline, - spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)), - spline->lookup_evaluated_length(std::clamp(end, 0.0f, length))); - } - else { - trim_spline(spline, - spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)), - spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f))); - } + const VArray<bool> cyclic = src_curves.cyclic(); + + /* If node length input is on form [0, 1] instead of [0, length]*/ + const bool normalized_length_lookup = mode == GEO_NODE_CURVE_SAMPLE_FACTOR; + + /* Stack start + end field. */ + Vector<float> length_factors(src_curves.curves_num() * 2); + Vector<int64_t> lookup_indices(src_curves.curves_num() * 2); + threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange curve_range) { + for (const int64_t curve_i : curve_range) { + const bool negative_trim = !cyclic[curve_i] && starts[curve_i] > ends[curve_i]; + length_factors[curve_i] = starts[curve_i]; + length_factors[curve_i + src_curves.curves_num()] = negative_trim ? starts[curve_i] : + ends[curve_i]; + lookup_indices[curve_i] = curve_i; + lookup_indices[curve_i + src_curves.curves_num()] = curve_i; } }); - Curves *dst_curves_id = curve_eval_to_curves(*curve); + /* Create curve trim lookup table. */ + Array<bke::curves::CurvePoint, 12> point_lookups = geometry::lookup_curve_points( + src_curves, length_factors, lookup_indices, normalized_length_lookup); + + bke::CurvesGeometry dst_curves = geometry::trim_curves( + src_curves, + src_curves.curves_range().as_span(), + point_lookups.as_span().slice(0, src_curves.curves_num()), + point_lookups.as_span().slice(src_curves.curves_num(), src_curves.curves_num())); + + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); bke::curves_copy_parameters(src_curves_id, *dst_curves_id); geometry_set.replace_curves(dst_curves_id); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index e8dbfbde401..3f6155460ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -270,8 +270,8 @@ static void node_geo_exec(GeoNodeExecParams params) BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval); - const AttributeAccessor mesh_attributes_eval = bke::mesh_attributes(*surface_mesh_eval); - const AttributeAccessor mesh_attributes_orig = bke::mesh_attributes(*surface_mesh_orig); + const AttributeAccessor mesh_attributes_eval = surface_mesh_eval->attributes(); + const AttributeAccessor mesh_attributes_orig = surface_mesh_orig->attributes(); Curves &curves_id = *curves_geometry.get_curves_for_write(); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 1c8a34dab1f..851ca622d6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -176,9 +176,9 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> attributes, src_attributes, dst_attributes, ATTR_DOMAIN_CORNER, IndexMask(indices)); } -static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map) +static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); const Span<MVert> src_verts = src_mesh.verts(); @@ -239,16 +239,16 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { - const Span<MPoly> src_polygons = src_mesh.polys(); + const Span<MPoly> src_polys = src_mesh.polys(); const Span<MLoop> src_loops = src_mesh.loops(); - MutableSpan<MPoly> dst_polygons = dst_mesh.polys_for_write(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_polygons[i_src]; - MPoly &mp_dst = dst_polygons[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; @@ -270,16 +270,16 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { - const Span<MPoly> src_polygons = src_mesh.polys(); + const Span<MPoly> src_polys = src_mesh.polys(); const Span<MLoop> src_loops = src_mesh.loops(); - MutableSpan<MPoly> dst_polygons = dst_mesh.polys_for_write(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_polygons[i_src]; - MPoly &mp_dst = dst_polygons[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; @@ -302,16 +302,16 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { - const Span<MPoly> src_polygons = src_mesh.polys(); + const Span<MPoly> src_polys = src_mesh.polys(); const Span<MLoop> src_loops = src_mesh.loops(); - MutableSpan<MPoly> dst_polygons = dst_mesh.polys_for_write(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_polygons[i_src]; - MPoly &mp_dst = dst_polygons[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; @@ -382,8 +382,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); copy_attributes_based_on_mask(attributes, - bke::pointcloud_attributes(src_pointcloud), - bke::pointcloud_attributes_for_write(*pointcloud), + src_pointcloud.attributes(), + pointcloud->attributes_for_write(), ATTR_DOMAIN_POINT, selection); geometry_set.replace_pointcloud(pointcloud); @@ -407,9 +407,9 @@ static void delete_selected_instances(GeometrySet &geometry_set, instances.remove_instances(selection); } -static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection, - MutableSpan<int> r_vertex_map, - int *r_selected_vertices_num) +static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection, + MutableSpan<int> r_vertex_map, + int *r_selected_verts_num) { BLI_assert(vertex_selection.size() == r_vertex_map.size()); @@ -424,7 +424,7 @@ static void compute_selected_vertices_from_vertex_selection(const Span<bool> ver } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; } static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, @@ -452,12 +452,12 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, *r_selected_edges_num = selected_edges_num; } -static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, - const Span<bool> vertex_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_vertex_selection(const Mesh &mesh, + const Span<bool> vertex_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totvert == vertex_selection.size()); const Span<MPoly> polys = mesh.polys(); @@ -494,13 +494,12 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, * 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 Span<bool> edge_selection, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - int *r_selected_vertices_num, - int *r_selected_edges_num) +static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + int *r_selected_verts_num, + int *r_selected_edges_num) { BLI_assert(mesh.totedge == edge_selection.size()); const Span<MEdge> edges = mesh.edges(); @@ -526,7 +525,7 @@ static void compute_selected_vertices_and_edges_from_edge_selection( } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; *r_selected_edges_num = selected_edges_num; } @@ -558,12 +557,12 @@ static void compute_selected_edges_from_edge_selection(const Mesh &mesh, * 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 Span<bool> edge_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { const Span<MPoly> polys = mesh.polys(); const Span<MLoop> loops = mesh.loops(); @@ -612,12 +611,12 @@ static void compute_selected_mesh_data_from_vertex_selection_edge_face( compute_selected_edges_from_vertex_selection( mesh, vertex_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh, + vertex_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** @@ -630,23 +629,23 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { - compute_selected_vertices_from_vertex_selection( - vertex_selection, r_vertex_map, r_selected_vertices_num); + compute_selected_verts_from_vertex_selection( + vertex_selection, r_vertex_map, r_selected_verts_num); compute_selected_edges_from_vertex_selection( mesh, vertex_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh, + vertex_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** @@ -665,12 +664,12 @@ static void compute_selected_mesh_data_from_edge_selection_edge_face( { compute_selected_edges_from_edge_selection( mesh, edge_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_edge_selection(mesh, + edge_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** @@ -683,35 +682,31 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { r_vertex_map.fill(-1); - compute_selected_vertices_and_edges_from_edge_selection(mesh, - edge_selection, - r_vertex_map, - r_edge_map, - r_selected_vertices_num, - r_selected_edges_num); - compute_selected_polygons_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_verts_and_edges_from_edge_selection( + mesh, edge_selection, r_vertex_map, r_edge_map, r_selected_verts_num, r_selected_edges_num); + compute_selected_polys_from_edge_selection(mesh, + edge_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** * Checks for every polygon if it is in `poly_selection`. */ -static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, - const Span<bool> poly_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_poly_selection(const Mesh &mesh, + const Span<bool> poly_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); const Span<MPoly> polys = mesh.polys(); @@ -792,7 +787,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) @@ -834,7 +829,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, } } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; *r_selected_edges_num = selected_edges_num; *r_selected_polys_num = r_selected_poly_indices.size(); *r_selected_loops_num = selected_loops_num; @@ -919,30 +914,30 @@ static void do_mesh_separation(GeometrySet &geometry_set, selected_polys_num); /* Copy the selected parts of the mesh over to the new mesh. */ - copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map); + copy_masked_verts_to_new_mesh(mesh_in, *mesh_out, vertex_map); copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); copy_masked_polys_to_new_mesh( mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_POINT, vertex_map); copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -1002,23 +997,21 @@ static void do_mesh_separation(GeometrySet &geometry_set, mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ - copy_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), - {ATTR_DOMAIN_POINT}); + copy_attributes( + attributes, mesh_in.attributes(), mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT}); copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -1028,28 +1021,28 @@ static void do_mesh_separation(GeometrySet &geometry_set, /* Fill all the maps based on the selection. */ switch (domain) { case ATTR_DOMAIN_POINT: - compute_selected_polygons_from_vertex_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_EDGE: - compute_selected_polygons_from_edge_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_edge_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_FACE: - compute_selected_polygons_from_poly_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_poly_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; default: BLI_assert_unreachable(); @@ -1065,17 +1058,17 @@ static void do_mesh_separation(GeometrySet &geometry_set, /* Copy attributes. */ copy_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE}); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -1094,8 +1087,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set, { const Mesh &src_mesh = *geometry_set.get_mesh_for_read(); bke::MeshFieldContext field_context{src_mesh, selection_domain}; - fn::FieldEvaluator evaluator{field_context, - bke::mesh_attributes(src_mesh).domain_size(selection_domain)}; + fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)}; evaluator.add(selection_field); evaluator.evaluate(); const VArray<bool> selection = evaluator.get_evaluated<bool>(0); 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 aaab259fc51..b84ee33e26f 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 @@ -291,8 +291,8 @@ BLI_NOINLINE static void propagate_existing_attributes( const Span<float3> bary_coords, const Span<int> looptri_indices) { - const AttributeAccessor mesh_attributes = bke::mesh_attributes(mesh); - MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write(points); + const AttributeAccessor mesh_attributes = mesh.attributes(); + MutableAttributeAccessor point_attributes = points.attributes_for_write(); for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -333,7 +333,7 @@ BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh, const Span<int> looptri_indices, const AttributeOutputs &attribute_outputs) { - MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write(points); + MutableAttributeAccessor point_attributes = points.attributes_for_write(); SpanAttributeWriter<int> ids = point_attributes.lookup_or_add_for_write_only_span<int>( "id", ATTR_DOMAIN_POINT); @@ -396,7 +396,7 @@ static Array<float> calc_full_density_factors_with_selection(const Mesh &mesh, const Field<bool> &selection_field) { const eAttrDomain domain = ATTR_DOMAIN_CORNER; - const int domain_size = bke::mesh_attributes(mesh).domain_size(domain); + const int domain_size = mesh.attributes().domain_size(domain); Array<float> densities(domain_size, 0.0f); bke::MeshFieldContext field_context{mesh, domain}; @@ -491,8 +491,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set, } PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); - bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); bke::SpanAttributeWriter<float3> point_positions = point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); bke::SpanAttributeWriter<float> point_radii = diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index 7d81ee91a1c..84e63845b84 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -263,10 +263,10 @@ static void calc_boundaries(const Mesh &mesh, static void create_vertex_poly_map(const Mesh &mesh, MutableSpan<Vector<int>> r_vertex_poly_indices) { - const Span<MPoly> polygons = mesh.polys(); + const Span<MPoly> polys = mesh.polys(); const Span<MLoop> loops = mesh.loops(); - for (const int i : polygons.index_range()) { - const MPoly &poly = polygons[i]; + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); for (const MLoop &loop : poly_loops) { r_vertex_poly_indices[loop.v].append(i); @@ -335,18 +335,18 @@ static bool sort_vertex_polys(const Span<MEdge> edges, const int vertex_index, const bool boundary_vertex, const Span<EdgeType> edge_types, - MutableSpan<int> connected_polygons, + MutableSpan<int> connected_polys, MutableSpan<int> r_shared_edges, MutableSpan<int> r_sorted_corners) { - if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) { + if (connected_polys.size() <= 2 && (!boundary_vertex || connected_polys.size() == 0)) { return true; } /* For each polygon store the two corners whose edge contains the vertex. */ - Array<std::pair<int, int>> poly_vertex_corners(connected_polygons.size()); - for (const int i : connected_polygons.index_range()) { - const MPoly &poly = polys[connected_polygons[i]]; + Array<std::pair<int, int>> poly_vertex_corners(connected_polys.size()); + for (const int i : connected_polys.index_range()) { + const MPoly &poly = polys[connected_polys[i]]; bool first_edge_done = false; for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { const MLoop &loop = loops[loop_index]; @@ -369,20 +369,20 @@ static bool sort_vertex_polys(const Span<MEdge> edges, * the loop to determine the 'average' orientation. */ if (boundary_vertex) { /* Our first polygon needs to be one which has a boundary edge. */ - for (const int i : connected_polygons.index_range()) { + for (const int i : connected_polys.index_range()) { const MLoop &first_loop = loops[poly_vertex_corners[i].first]; const MLoop &second_loop = loops[poly_vertex_corners[i].second]; if (edge_types[first_loop.e] == EdgeType::Boundary && first_loop.v == vertex_index) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].first; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } if (edge_types[second_loop.e] == EdgeType::Boundary && second_loop.v == vertex_index) { shared_edge_i = first_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].second; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } @@ -390,20 +390,20 @@ static bool sort_vertex_polys(const Span<MEdge> edges, if (shared_edge_i == -1) { /* The rotation is inconsistent between the two polygons on the boundary. Just choose one * of the polygon's orientation. */ - for (const int i : connected_polygons.index_range()) { + for (const int i : connected_polys.index_range()) { const MLoop &first_loop = loops[poly_vertex_corners[i].first]; const MLoop &second_loop = loops[poly_vertex_corners[i].second]; if (edge_types[first_loop.e] == EdgeType::Boundary) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].first; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } if (edge_types[second_loop.e] == EdgeType::Boundary) { shared_edge_i = first_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].second; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } @@ -425,12 +425,12 @@ static bool sort_vertex_polys(const Span<MEdge> edges, } BLI_assert(shared_edge_i != -1); - for (const int i : IndexRange(connected_polygons.size() - 1)) { + for (const int i : IndexRange(connected_polys.size() - 1)) { r_shared_edges[i] = shared_edge_i; /* Look at the other polys to see if it has this shared edge. */ int j = i + 1; - for (; j < connected_polygons.size(); ++j) { + for (; j < connected_polys.size(); ++j) { const MLoop &first_loop = loops[poly_vertex_corners[j].first]; const MLoop &second_loop = loops[poly_vertex_corners[j].second]; if (first_loop.e == shared_edge_i) { @@ -444,13 +444,13 @@ static bool sort_vertex_polys(const Span<MEdge> edges, break; } } - if (j == connected_polygons.size()) { + if (j == connected_polys.size()) { /* The vertex is not manifold because the polygons around the vertex don't form a loop, and * hence can't be sorted. */ return false; } - std::swap(connected_polygons[i + 1], connected_polygons[j]); + std::swap(connected_polys[i + 1], connected_polys[j]); std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]); } @@ -918,8 +918,8 @@ static void calc_dual_mesh(GeometrySet &geometry_set, new_to_old_edges_map, new_to_old_face_corners_map, boundary_vertex_to_relevant_face_map, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out)); + mesh_in.attributes(), + mesh_out->attributes_for_write()); MutableSpan<MVert> dst_verts = mesh_out->verts_for_write(); MutableSpan<MEdge> dst_edges = mesh_out->edges_for_write(); 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 f0018e91478..d2a3c339301 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -593,22 +593,15 @@ static void duplicate_faces(GeometrySet &geometry_set, loop_mapping, offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_faces(mesh, - selection, - offsets, - vert_mapping, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_faces( + mesh, selection, offsets, vert_mapping, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), - ATTR_DOMAIN_FACE, - selection, - attribute_outputs, - offsets); + create_duplicate_index_attribute( + new_mesh->attributes_for_write(), ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets); } geometry_set.replace_mesh(new_mesh); @@ -769,17 +762,14 @@ static void duplicate_edges(GeometrySet &geometry_set, vert_orig_indices, edge_offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_edges(mesh, - selection, - edge_offsets, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_edges( + mesh, selection, edge_offsets, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), + create_duplicate_index_attribute(new_mesh->attributes_for_write(), ATTR_DOMAIN_EDGE, selection, attribute_outputs, @@ -926,14 +916,13 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_point( - offsets, bke::mesh_attributes(mesh), bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_point(offsets, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), + create_duplicate_index_attribute(new_mesh->attributes_for_write(), ATTR_DOMAIN_POINT, selection, attribute_outputs, @@ -973,15 +962,13 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, - bke::pointcloud_attributes(src_points), - bke::pointcloud_attributes_for_write(*pointcloud)); + src_points.attributes(), + pointcloud->attributes_for_write()); - copy_stable_id_point(offsets, - bke::pointcloud_attributes(src_points), - bke::pointcloud_attributes_for_write(*pointcloud)); + copy_stable_id_point(offsets, src_points.attributes(), pointcloud->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::pointcloud_attributes_for_write(*pointcloud), + create_duplicate_index_attribute(pointcloud->attributes_for_write(), ATTR_DOMAIN_POINT, selection, attribute_outputs, diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc index ac66e3906a7..9ef9ee8ad6e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc @@ -59,9 +59,9 @@ class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput { Field<int> next_vertex_; public: - PathToEdgeSelectionFieldInput(Field<bool> start_vertices, Field<int> next_vertex) + PathToEdgeSelectionFieldInput(Field<bool> start_verts, Field<int> next_vertex) : bke::MeshFieldInput(CPPType::get<bool>(), "Edge Selection"), - start_vertices_(start_vertices), + start_vertices_(start_verts), next_vertex_(next_vertex) { category_ = Category::Generated; @@ -88,7 +88,7 @@ class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput { edge_paths_to_selection(mesh, start_verts, next_vert, selection_span); - return bke::mesh_attributes(mesh).adapt_domain<bool>( + return mesh.attributes().adapt_domain<bool>( VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_EDGE, domain); } @@ -110,10 +110,10 @@ class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput { static void node_geo_exec(GeoNodeExecParams params) { - Field<bool> start_vertices = params.extract_input<Field<bool>>("Start Vertices"); + Field<bool> start_verts = params.extract_input<Field<bool>>("Start Vertices"); Field<int> next_vertex = params.extract_input<Field<int>>("Next Vertex Index"); Field<bool> selection_field{ - std::make_shared<PathToEdgeSelectionFieldInput>(start_vertices, next_vertex)}; + std::make_shared<PathToEdgeSelectionFieldInput>(start_verts, next_vertex)}; params.set_output("Selection", std::move(selection_field)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index d335a162776..c7f4b78946d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -66,7 +66,7 @@ static void save_selection_as_attribute(Mesh &mesh, const eAttrDomain domain, const IndexMask selection) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); BLI_assert(!attributes.contains(id)); SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(id, domain); @@ -94,28 +94,24 @@ static void expand_mesh(Mesh &mesh, const int loop_expand) { if (vert_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert); + const int old_verts_num = mesh.totvert; mesh.totvert += vert_expand; - CustomData_realloc(&mesh.vdata, mesh.totvert); - } - else { - /* Even when the number of vertices is not changed, the mesh can still be deformed. */ - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert); + CustomData_realloc(&mesh.vdata, old_verts_num, mesh.totvert); } if (edge_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge); + const int old_edges_num = mesh.totedge; mesh.totedge += edge_expand; - CustomData_realloc(&mesh.edata, mesh.totedge); + CustomData_realloc(&mesh.edata, old_edges_num, mesh.totedge); } if (poly_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly); + const int old_polys_num = mesh.totpoly; mesh.totpoly += poly_expand; - CustomData_realloc(&mesh.pdata, mesh.totpoly); + CustomData_realloc(&mesh.pdata, old_polys_num, mesh.totpoly); } if (loop_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop); + const int old_loops_num = mesh.totloop; mesh.totloop += loop_expand; - CustomData_realloc(&mesh.ldata, mesh.totloop); + CustomData_realloc(&mesh.ldata, old_loops_num, mesh.totloop); } } @@ -138,7 +134,7 @@ static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain) static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domain) { - const bke::AttributeAccessor attributes = bke::mesh_attributes(mesh); + const bke::AttributeAccessor attributes = mesh.attributes(); CustomData &custom_data = get_customdata(mesh, domain); if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) { return {orig_indices, attributes.domain_size(domain)}; @@ -151,6 +147,7 @@ static MEdge new_edge(const int v1, const int v2) MEdge edge; edge.v1 = v1; edge.v2 = v2; + edge.crease = 0; edge.flag = (ME_EDGEDRAW | ME_EDGERENDER); return edge; } @@ -160,6 +157,7 @@ static MEdge new_loose_edge(const int v1, const int v2) MEdge edge; edge.v1 = v1; edge.v2 = v2; + edge.crease = 0; edge.flag = ME_LOOSEEDGE; return edge; } @@ -252,7 +250,7 @@ static void extrude_mesh_vertices(Mesh &mesh, new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]); } - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) { @@ -290,6 +288,7 @@ static void extrude_mesh_vertices(Mesh &mesh, for (const int i : range) { const float3 offset = offsets[selection[i]]; add_v3_v3(new_verts[i].co, offset); + new_verts[i].flag = 0; } }); }); @@ -498,7 +497,7 @@ static void extrude_mesh_edges(Mesh &mesh, const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( new_vert_range.size(), duplicate_edges, orig_vert_size); - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( @@ -612,6 +611,7 @@ static void extrude_mesh_edges(Mesh &mesh, threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { for (const int i : range) { add_v3_v3(new_verts[i].co, offset); + new_verts[i].flag = 0; } }); } @@ -619,6 +619,7 @@ static void extrude_mesh_edges(Mesh &mesh, threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { for (const int i : range) { add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]); + new_verts[i].flag = 0; } }); } @@ -878,7 +879,7 @@ static void extrude_mesh_face_regions(Mesh &mesh, const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( new_vert_range.size(), boundary_edges, orig_vert_size); - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( @@ -1000,6 +1001,10 @@ static void extrude_mesh_face_regions(Mesh &mesh, }); } + for (MVert &vert : verts.slice(new_vert_range)) { + vert.flag = 0; + } + MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT); vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE); @@ -1132,7 +1137,7 @@ static void extrude_individual_mesh_faces(Mesh &mesh, } }); - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( @@ -1257,6 +1262,7 @@ static void extrude_individual_mesh_faces(Mesh &mesh, const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection); for (MVert &vert : new_verts.slice(poly_corner_range)) { add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]); + vert.flag = 0; } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index fc9c9870c5c..613425716d4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -44,7 +44,7 @@ static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field) } } - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { if (meta_data.domain == ATTR_DOMAIN_CORNER) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc index 1f84f8f288d..8e64209a418 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc @@ -12,7 +12,7 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { - Vector<GeometrySet> geometries = params.extract_multi_input<GeometrySet>("Geometry"); + Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry"); GeometrySet instances_geometry; InstancesComponent &instances_component = instances_geometry.get_component_for_write<InstancesComponent>(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc index f2304849cbc..f2e7379b3a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc @@ -82,8 +82,7 @@ class AngleFieldInput final : public bke::MeshFieldInput { }; VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn); - return bke::mesh_attributes(mesh).adapt_domain<float>( - std::move(angles), ATTR_DOMAIN_EDGE, domain); + return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override @@ -150,8 +149,7 @@ class SignedAngleFieldInput final : public bke::MeshFieldInput { }; VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn); - return bke::mesh_attributes(mesh).adapt_domain<float>( - std::move(angles), ATTR_DOMAIN_EDGE, domain); + return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc index 716cbf589d9..bfe8753c039 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc @@ -34,7 +34,7 @@ class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput { face_count[loop.e]++; } - return bke::mesh_attributes(mesh).adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc index 50ebf78e58f..c8ceae239a4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc @@ -27,9 +27,9 @@ static void node_declare(NodeDeclarationBuilder &b) enum VertexNumber { VERTEX_ONE, VERTEX_TWO }; -static VArray<int> construct_edge_vertices_gvarray(const Mesh &mesh, - const VertexNumber vertex, - const eAttrDomain domain) +static VArray<int> construct_edge_verts_gvarray(const Mesh &mesh, + const VertexNumber vertex, + const eAttrDomain domain) { const Span<MEdge> edges = mesh.edges(); if (domain == ATTR_DOMAIN_EDGE) { @@ -57,7 +57,7 @@ class EdgeVerticesFieldInput final : public bke::MeshFieldInput { const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - return construct_edge_vertices_gvarray(mesh, vertex_, domain); + return construct_edge_verts_gvarray(mesh, vertex_, domain); } uint64_t hash() const override @@ -83,13 +83,13 @@ static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh, const Span<MEdge> edges = mesh.edges(); if (vertex == VERTEX_ONE) { - return bke::mesh_attributes(mesh).adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForFunc(edges.size(), [verts, edges](const int i) { return verts[edges[i].v1].co; }), ATTR_DOMAIN_EDGE, domain); } - return bke::mesh_attributes(mesh).adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForFunc(edges.size(), [verts, edges](const int i) { return verts[edges[i].v2].co; }), ATTR_DOMAIN_EDGE, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc index c4d792c6c9a..be921c1f1c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc @@ -27,7 +27,7 @@ static VArray<float> construct_face_area_varray(const Mesh &mesh, const eAttrDom return BKE_mesh_calc_poly_area(&poly, &loops[poly.loopstart], verts.data()); }; - return bke::mesh_attributes(mesh).adapt_domain<float>( + return mesh.attributes().adapt_domain<float>( VArray<float>::ForFunc(polys.size(), area_fn), ATTR_DOMAIN_FACE, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc index 040b243a868..72c45de7b0f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -72,7 +72,7 @@ class PlanarFieldInput final : public bke::MeshFieldInput { return max - min < thresholds[i] / 2.0f; }; - return bke::mesh_attributes(mesh).adapt_domain<bool>( + return mesh.attributes().adapt_domain<bool>( VArray<bool>::ForFunc(polys.size(), planar_fn), ATTR_DOMAIN_FACE, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc index cd58a0ad428..9e85eae3a31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc @@ -37,7 +37,7 @@ static VArray<int> construct_neighbor_count_varray(const Mesh &mesh, const eAttr } } - return bke::mesh_attributes(mesh).adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain); } @@ -71,7 +71,7 @@ class FaceNeighborCountFieldInput final : public bke::MeshFieldInput { static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain domain) { const Span<MPoly> polys = mesh.polys(); - return bke::mesh_attributes(mesh).adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForFunc(polys.size(), [polys](const int i) -> float { return polys[i].totloop; }), ATTR_DOMAIN_FACE, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index 53cb3d0a19f..9d7735e707d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -47,7 +47,7 @@ class IslandFieldInput final : public bke::MeshFieldInput { output[i] = ordered_roots.index_of_or_add(root); } - return bke::mesh_attributes(mesh).adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); } @@ -87,8 +87,7 @@ class IslandCountFieldInput final : public bke::MeshFieldInput { island_list.add(root); } - return VArray<int>::ForSingle(island_list.size(), - bke::mesh_attributes(mesh).domain_size(domain)); + return VArray<int>::ForSingle(island_list.size(), mesh.attributes().domain_size(domain)); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc index 122c7b352c7..da09d3650e3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc @@ -88,7 +88,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - params.used_named_attribute(name, eNamedAttrUsage::Read); + params.used_named_attribute(name, NamedAttributeUsage::Read); switch (data_type) { case CD_PROP_FLOAT: diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc index e13edc8f979..a54daabde3b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc @@ -125,7 +125,7 @@ class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput { } } }); - return bke::mesh_attributes(mesh).adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(next_index)), ATTR_DOMAIN_POINT, domain); } @@ -189,7 +189,7 @@ class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput { } } }); - return bke::mesh_attributes(mesh).adapt_domain<float>( + return mesh.attributes().adapt_domain<float>( VArray<float>::ForContainer(std::move(cost)), ATTR_DOMAIN_POINT, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 2a80d7d855a..ec2f1b00e6c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -45,8 +45,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); bke::SpanAttributeWriter<float3> point_positions = point_attributes.lookup_or_add_for_write_only_span<float3>("position", 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 023d7a32a61..9fdf7fe7d31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -177,7 +177,7 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet static void node_geo_exec(GeoNodeExecParams params) { - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry"); + Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Geometry"); GeometrySet geometry_set_result; join_component_type<MeshComponent>(geometry_sets, geometry_set_result); diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 9822e0ea0d6..628688f3b47 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -32,7 +32,7 @@ static void select_mesh_by_material(const Mesh &mesh, slots.append(i); } } - const AttributeAccessor attributes = bke::mesh_attributes(mesh); + const AttributeAccessor attributes = mesh.attributes(); const VArray<int> material_indices = attributes.lookup_or_default<int>( "material_index", ATTR_DOMAIN_FACE, 0); if (material != nullptr && material_indices.is_single() && @@ -81,7 +81,7 @@ class MaterialSelectionFieldInput final : public bke::GeometryFieldInput { Array<bool> selection(mesh->totpoly); select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection); - return bke::mesh_attributes(*mesh).adapt_domain<bool>( + return mesh->attributes().adapt_domain<bool>( VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain); return nullptr; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 93ecc96337e..edf14f664c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -255,7 +255,7 @@ int ConeConfig::calculate_total_corners() return corner_total; } -static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config) +static void calculate_cone_verts(const MutableSpan<MVert> &verts, const ConeConfig &config) { Array<float2> circle(config.circle_segments); const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments)); @@ -480,7 +480,7 @@ static void calculate_selection_outputs(Mesh *mesh, const ConeConfig &config, ConeAttributeOutputs &attribute_outputs) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); /* Populate "Top" selection output. */ if (attribute_outputs.top_id) { @@ -536,7 +536,7 @@ static void calculate_selection_outputs(Mesh *mesh, */ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); @@ -694,7 +694,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, MutableSpan<MPoly> polys = mesh->polys_for_write(); MutableSpan<MLoop> loops = mesh->loops_for_write(); - calculate_cone_vertices(verts, config); + calculate_cone_verts(verts, config); calculate_cone_edges(edges, config); calculate_cone_faces(loops, polys, config); calculate_cone_uvs(mesh, config); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index d8a4db43b27..6f0b8283b72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -18,7 +18,7 @@ namespace blender::nodes { static void calculate_uvs( Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index b2f629806cd..4fd6399f4eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -179,15 +179,15 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> vertices = mesh->verts_for_write(); + MutableSpan<MVert> verts = mesh->verts_for_write(); MutableSpan<MEdge> edges = mesh->edges_for_write(); threading::parallel_invoke( 1024 < count, [&]() { - threading::parallel_for(vertices.index_range(), 4096, [&](IndexRange range) { + threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { for (const int i : range) { - copy_v3_v3(vertices[i].co, start + delta * i); + copy_v3_v3(verts[i].co, start + delta * i); } }); }, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 017132b1a43..d39e72b7f0a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -254,7 +254,7 @@ BLI_NOINLINE static void calculate_sphere_corners(MutableSpan<MLoop> loops, BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index d5c7fec4ce7..a1d6695b33b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -2,6 +2,7 @@ #include "BLI_task.hh" +#include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" @@ -65,7 +66,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, geometry_set.remove_geometry_during_modify(); return; } - const int domain_size = bke::mesh_attributes(*mesh).domain_size(domain); + const int domain_size = mesh->attributes().domain_size(domain); if (domain_size == 0) { geometry_set.remove_geometry_during_modify(); return; @@ -83,7 +84,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - MutableAttributeAccessor dst_attributes = bke::pointcloud_attributes_for_write(*pointcloud); + MutableAttributeAccessor dst_attributes = pointcloud->attributes_for_write(); GSpanAttributeWriter position = dst_attributes.lookup_or_add_for_write_only_span( "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); @@ -102,7 +103,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); attributes.remove("position"); - const AttributeAccessor src_attributes = bke::mesh_attributes(*mesh); + const AttributeAccessor src_attributes = mesh->attributes(); for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; diff --git a/source/blender/nodes/geometry/nodes/node_geo_points.cc b/source/blender/nodes/geometry/nodes/node_geo_points.cc index e0ba1f1c810..4a294076834 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BKE_pointcloud.h" +#include "DNA_pointcloud_types.h" #include "BLI_task.hh" @@ -70,7 +71,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radius_field = params.extract_input<Field<float>>("Radius"); PointCloud *points = BKE_pointcloud_new_nomain(count); - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(*points); + MutableAttributeAccessor attributes = points->attributes_for_write(); AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>( "position", ATTR_DOMAIN_POINT); AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>( diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 1f6ffca0303..4ac3bf712f7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -47,8 +47,8 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0); geometry_set.replace_mesh(mesh); - const AttributeAccessor src_attributes = bke::pointcloud_attributes(*points); - MutableAttributeAccessor dst_attributes = bke::mesh_attributes_for_write(*mesh); + const AttributeAccessor src_attributes = points->attributes(); + MutableAttributeAccessor dst_attributes = mesh->attributes_for_write(); for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 5c2ec74b59e..f657b128c51 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -312,7 +312,7 @@ class RaycastFunction : public fn::MultiFunction { } const Mesh &mesh = *target_.get_mesh_for_read(); target_context_.emplace(bke::MeshFieldContext{mesh, domain_}); - const int domain_size = bke::mesh_attributes(mesh).domain_size(domain_); + const int domain_size = mesh.attributes().domain_size(domain_); target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); target_evaluator_->add(std::move(src_field)); target_evaluator_->evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc index ee279ba58f9..1b398f63691 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc @@ -55,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params) }); if (attribute_exists && !cannot_delete) { - params.used_named_attribute(name, eNamedAttrUsage::Remove); + params.used_named_attribute(name, NamedAttributeUsage::Remove); } if (!attribute_exists) { 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 8fd05e70ed3..2ebbf88b8ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc @@ -282,11 +282,11 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM return islands; } -static void get_face_vertices(const Span<MEdge> /*edges*/, - const Span<MPoly> polys, - const Span<MLoop> loops, - int face_index, - VectorSet<int> &r_vertex_indices) +static void get_face_verts(const Span<MEdge> /*edges*/, + const Span<MPoly> polys, + const Span<MLoop> loops, + int face_index, + VectorSet<int> &r_vertex_indices) { const MPoly &poly = polys[face_index]; const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); @@ -315,7 +315,7 @@ static void scale_faces_on_axis(Mesh &mesh, const AxisScaleFields &fields) AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); - scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices); + scale_vertex_islands_on_axis(mesh, island, params, get_face_verts); } static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator, @@ -337,7 +337,7 @@ static void scale_faces_uniformly(Mesh &mesh, const UniformScaleFields &fields) UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); - scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices); + scale_vertex_islands_uniformly(mesh, island, params, get_face_verts); } static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection) @@ -371,11 +371,11 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM return islands; } -static void get_edge_vertices(const Span<MEdge> edges, - const Span<MPoly> /*polygons*/, - const Span<MLoop> /*loops*/, - int edge_index, - VectorSet<int> &r_vertex_indices) +static void get_edge_verts(const Span<MEdge> edges, + const Span<MPoly> /*polys*/, + const Span<MLoop> /*loops*/, + int edge_index, + VectorSet<int> &r_vertex_indices) { const MEdge &edge = edges[edge_index]; r_vertex_indices.add(edge.v1); @@ -389,7 +389,7 @@ static void scale_edges_uniformly(Mesh &mesh, const UniformScaleFields &fields) UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); - scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices); + scale_vertex_islands_uniformly(mesh, island, params, get_edge_verts); } static void scale_edges_on_axis(Mesh &mesh, const AxisScaleFields &fields) @@ -399,7 +399,7 @@ static void scale_edges_on_axis(Mesh &mesh, const AxisScaleFields &fields) AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); - scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices); + scale_vertex_islands_on_axis(mesh, island, params, get_edge_verts); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 3aee25b0693..8d00d82664b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -50,7 +50,7 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); } - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>( "material_index", ATTR_DOMAIN_FACE); material_indices.span.fill_indices(selection, new_material_index); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index f1ac6e7f14c..28d07b31218 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -25,7 +25,7 @@ static void set_radius_in_component(PointCloud &pointcloud, if (pointcloud.totpoint == 0) { return; } - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius", ATTR_DOMAIN_POINT); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index fa4d3eb6ac9..0df51e49827 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -22,7 +22,7 @@ static void set_smooth(Mesh &mesh, return; } - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); AttributeWriter<bool> smooth = attributes.lookup_or_add_for_write<bool>("shade_smooth", ATTR_DOMAIN_FACE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index c2d6f57ce8a..2a590f5bf4a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -149,7 +149,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - params.used_named_attribute(name, eNamedAttrUsage::Write); + params.used_named_attribute(name, NamedAttributeUsage::Write); const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node()); const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc index bb33430a02f..09c01b8c627 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -13,12 +13,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { - Vector<std::string> strings = params.extract_multi_input<std::string>("Strings"); + Vector<fn::ValueOrField<std::string>> strings = + params.extract_input<Vector<fn::ValueOrField<std::string>>>("Strings"); const std::string delim = params.extract_input<std::string>("Delimiter"); std::string output; for (const int i : strings.index_range()) { - output += strings[i]; + output += strings[i].as_value(); if (i < (strings.size() - 1)) { output += delim; } 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 13bfe78fbe5..afc492c40e4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -235,12 +235,12 @@ static void get_closest_mesh_looptris(const Mesh &mesh, free_bvhtree_from_mesh(&tree_data); } -static void get_closest_mesh_polygons(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_poly_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) +static void get_closest_mesh_polys(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_poly_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) { BLI_assert(mesh.totpoly > 0); @@ -270,7 +270,7 @@ static void get_closest_mesh_corners(const Mesh &mesh, BLI_assert(mesh.totloop > 0); Array<int> poly_indices(positions.size()); - get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {}); + get_closest_mesh_polys(mesh, positions, mask, poly_indices, {}, {}); for (const int i : mask) { const float3 position = positions[i]; @@ -438,7 +438,7 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { { const Mesh &mesh = *source_.get_mesh_for_read(); source_context_.emplace(bke::MeshFieldContext{mesh, domain_}); - const int domain_size = bke::mesh_attributes(mesh).domain_size(domain_); + const int domain_size = mesh.attributes().domain_size(domain_); source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); source_evaluator_->add(src_field_); source_evaluator_->evaluate(); @@ -540,7 +540,7 @@ class NearestTransferFunction : public fn::MultiFunction { break; } case ATTR_DOMAIN_FACE: { - get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + get_closest_mesh_polys(*mesh, positions, mask, mesh_indices, mesh_distances, {}); break; } case ATTR_DOMAIN_CORNER: { @@ -583,7 +583,7 @@ class NearestTransferFunction : public fn::MultiFunction { { if (use_mesh_) { const Mesh &mesh = *source_.get_mesh_for_read(); - const int domain_size = bke::mesh_attributes(mesh).domain_size(domain_); + const int domain_size = mesh.attributes().domain_size(domain_); mesh_context_.emplace(bke::MeshFieldContext(mesh, domain_)); mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); mesh_evaluator_->add(src_field_); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 0a36f58ba09..4130cad3bda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -47,7 +47,7 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform) static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) { - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( "position", ATTR_DOMAIN_POINT); for (const int i : position.span.index_range()) { @@ -58,7 +58,7 @@ static void translate_pointcloud(PointCloud &pointcloud, const float3 translatio static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) { - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( "position", ATTR_DOMAIN_POINT); for (const int i : position.span.index_range()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc index 4953a0aa8d0..ccb489f6e29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc @@ -87,7 +87,7 @@ static VArray<float3> construct_uv_gvarray(const Mesh &mesh, GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); - return bke::mesh_attributes(mesh).adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc index 513b9534c55..801bc3f4642 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -126,7 +126,7 @@ static VArray<float3> construct_uv_gvarray(const Mesh &mesh, GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); - return bke::mesh_attributes(mesh).adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain); } diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc deleted file mode 100644 index 89bfa5834e8..00000000000 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ /dev/null @@ -1,520 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "NOD_geometry_nodes_eval_log.hh" - -#include "BKE_curves.hh" -#include "BKE_geometry_set_instances.hh" - -#include "DNA_modifier_types.h" -#include "DNA_space_types.h" - -#include "FN_field_cpp_type.hh" - -#include "BLT_translation.h" - -#include <chrono> - -namespace blender::nodes::geometry_nodes_eval_log { - -using fn::FieldCPPType; -using fn::FieldInput; -using fn::GField; -using fn::ValueOrFieldCPPType; - -ModifierLog::ModifierLog(GeoLogger &logger) - : input_geometry_log_(std::move(logger.input_geometry_log_)), - output_geometry_log_(std::move(logger.output_geometry_log_)) -{ - root_tree_logs_ = allocator_.construct<TreeLog>(); - - LogByTreeContext log_by_tree_context; - - /* Combine all the local loggers that have been used by separate threads. */ - for (LocalGeoLogger &local_logger : logger) { - /* Take ownership of the allocator. */ - logger_allocators_.append(std::move(local_logger.allocator_)); - - for (ValueOfSockets &value_of_sockets : local_logger.values_) { - ValueLog *value_log = value_of_sockets.value.get(); - - /* Take centralized ownership of the logged value. It might be referenced by multiple - * sockets. */ - logged_values_.append(std::move(value_of_sockets.value)); - - for (const DSocket &socket : value_of_sockets.sockets) { - SocketLog &socket_log = this->lookup_or_add_socket_log(log_by_tree_context, socket); - socket_log.value_ = value_log; - } - } - - for (NodeWithWarning &node_with_warning : local_logger.node_warnings_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, - node_with_warning.node); - node_log.warnings_.append(node_with_warning.warning); - } - - for (NodeWithExecutionTime &node_with_exec_time : local_logger.node_exec_times_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, - node_with_exec_time.node); - node_log.exec_time_ = node_with_exec_time.exec_time; - } - - for (NodeWithDebugMessage &debug_message : local_logger.node_debug_messages_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, debug_message.node); - node_log.debug_messages_.append(debug_message.message); - } - - for (NodeWithUsedNamedAttribute &node_with_attribute_name : - local_logger.used_named_attributes_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, - node_with_attribute_name.node); - node_log.used_named_attributes_.append(std::move(node_with_attribute_name.attribute)); - } - } -} - -TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, - const DTreeContext &tree_context) -{ - TreeLog *tree_log = log_by_tree_context.lookup_default(&tree_context, nullptr); - if (tree_log != nullptr) { - return *tree_log; - } - - const DTreeContext *parent_context = tree_context.parent_context(); - if (parent_context == nullptr) { - return *root_tree_logs_.get(); - } - TreeLog &parent_log = this->lookup_or_add_tree_log(log_by_tree_context, *parent_context); - destruct_ptr<TreeLog> owned_tree_log = allocator_.construct<TreeLog>(); - tree_log = owned_tree_log.get(); - log_by_tree_context.add_new(&tree_context, tree_log); - parent_log.child_logs_.add_new(tree_context.parent_node()->name, std::move(owned_tree_log)); - return *tree_log; -} - -NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node) -{ - TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context()); - NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name, [&]() { - destruct_ptr<NodeLog> node_log = allocator_.construct<NodeLog>(); - node_log->input_logs_.resize(node->input_sockets().size()); - node_log->output_logs_.resize(node->output_sockets().size()); - return node_log; - }); - return node_log; -} - -SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, - DSocket socket) -{ - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, socket.node()); - MutableSpan<SocketLog> socket_logs = socket->is_input() ? node_log.input_logs_ : - node_log.output_logs_; - SocketLog &socket_log = socket_logs[socket->index()]; - return socket_log; -} - -void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const -{ - if (root_tree_logs_) { - root_tree_logs_->foreach_node_log(fn); - } -} - -const GeometryValueLog *ModifierLog::input_geometry_log() const -{ - return input_geometry_log_.get(); -} -const GeometryValueLog *ModifierLog::output_geometry_log() const -{ - return output_geometry_log_.get(); -} - -const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const -{ - const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name); - if (node_log == nullptr) { - return nullptr; - } - return node_log->get(); -} - -const NodeLog *TreeLog::lookup_node_log(const bNode &node) const -{ - return this->lookup_node_log(node.name); -} - -const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const -{ - const destruct_ptr<TreeLog> *tree_log = child_logs_.lookup_ptr_as(node_name); - if (tree_log == nullptr) { - return nullptr; - } - return tree_log->get(); -} - -void TreeLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const -{ - for (auto node_log : node_logs_.items()) { - fn(*node_log.value); - } - - for (auto child : child_logs_.items()) { - child.value->foreach_node_log(fn); - } -} - -const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const -{ - BLI_assert(index >= 0); - Span<SocketLog> socket_logs = (in_out == SOCK_IN) ? input_logs_ : output_logs_; - if (index >= socket_logs.size()) { - return nullptr; - } - return &socket_logs[index]; -} - -const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket &socket) const -{ - ListBase sockets = socket.in_out == SOCK_IN ? node.inputs : node.outputs; - int index = BLI_findindex(&sockets, &socket); - return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); -} - -GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type()) -{ - const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs(); - - /* Put the deduplicated field inputs into a vector so that they can be sorted below. */ - Vector<std::reference_wrapper<const FieldInput>> field_inputs; - if (field_input_nodes) { - field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(), - field_input_nodes->deduplicated_nodes.end()); - } - - std::sort( - field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { - const int index_a = (int)a.category(); - const int index_b = (int)b.category(); - if (index_a == index_b) { - return a.socket_inspection_name().size() < b.socket_inspection_name().size(); - } - return index_a < index_b; - }); - - for (const FieldInput &field_input : field_inputs) { - input_tooltips_.append(field_input.socket_inspection_name()); - } - - if (log_full_field) { - field_ = std::move(field); - } -} - -GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) -{ - static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, - GEO_COMPONENT_TYPE_INSTANCES, - GEO_COMPONENT_TYPE_MESH, - GEO_COMPONENT_TYPE_POINT_CLOUD, - GEO_COMPONENT_TYPE_VOLUME}; - - /* Keep track handled attribute names to make sure that we do not return the same name twice. - * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge - * attributes with the same name but different domains or data types on separate components. */ - Set<StringRef> names; - - geometry_set.attribute_foreach( - all_component_types, - true, - [&](const bke::AttributeIDRef &attribute_id, - const bke::AttributeMetaData &meta_data, - const GeometryComponent &UNUSED(component)) { - if (attribute_id.is_named() && names.add(attribute_id.name())) { - this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); - } - }); - - for (const GeometryComponent *component : geometry_set.get_components_for_read()) { - component_types_.append(component->type()); - switch (component->type()) { - case GEO_COMPONENT_TYPE_MESH: { - const MeshComponent &mesh_component = *(const MeshComponent *)component; - MeshInfo &info = this->mesh_info.emplace(); - info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); - break; - } - case GEO_COMPONENT_TYPE_CURVE: { - const CurveComponent &curve_component = *(const CurveComponent *)component; - CurveInfo &info = this->curve_info.emplace(); - info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); - break; - } - case GEO_COMPONENT_TYPE_POINT_CLOUD: { - const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; - PointCloudInfo &info = this->pointcloud_info.emplace(); - info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); - break; - } - case GEO_COMPONENT_TYPE_INSTANCES: { - const InstancesComponent &instances_component = *(const InstancesComponent *)component; - InstancesInfo &info = this->instances_info.emplace(); - info.instances_num = instances_component.instances_num(); - break; - } - case GEO_COMPONENT_TYPE_EDIT: { - const GeometryComponentEditData &edit_component = *( - const GeometryComponentEditData *)component; - if (const bke::CurvesEditHints *curve_edit_hints = - edit_component.curves_edit_hints_.get()) { - EditDataInfo &info = this->edit_data_info.emplace(); - info.has_deform_matrices = curve_edit_hints->deform_mats.has_value(); - info.has_deformed_positions = curve_edit_hints->positions.has_value(); - } - break; - } - case GEO_COMPONENT_TYPE_VOLUME: { - break; - } - } - } - if (log_full_geometry) { - full_geometry_ = std::make_unique<GeometrySet>(geometry_set); - full_geometry_->ensure_owns_direct_data(); - } -} - -Vector<const GeometryAttributeInfo *> NodeLog::lookup_available_attributes() const -{ - Vector<const GeometryAttributeInfo *> attributes; - Set<StringRef> names; - for (const SocketLog &socket_log : input_logs_) { - const ValueLog *value_log = socket_log.value(); - if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>( - value_log)) { - for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) { - if (names.add(attribute.name)) { - attributes.append(&attribute); - } - } - } - } - return attributes; -} - -const ModifierLog *ModifierLog::find_root_by_node_editor_context(const SpaceNode &snode) -{ - if (snode.id == nullptr) { - return nullptr; - } - if (GS(snode.id->name) != ID_OB) { - return nullptr; - } - Object *object = (Object *)snode.id; - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - NodesModifierData *nmd = (NodesModifierData *)md; - if (nmd->node_group == snode.nodetree) { - return (ModifierLog *)nmd->runtime_eval_log; - } - } - } - return nullptr; -} - -const TreeLog *ModifierLog::find_tree_by_node_editor_context(const SpaceNode &snode) -{ - const ModifierLog *eval_log = ModifierLog::find_root_by_node_editor_context(snode); - if (eval_log == nullptr) { - return nullptr; - } - Vector<bNodeTreePath *> tree_path_vec = snode.treepath; - if (tree_path_vec.is_empty()) { - return nullptr; - } - TreeLog *current = eval_log->root_tree_logs_.get(); - for (bNodeTreePath *path : tree_path_vec.as_span().drop_front(1)) { - destruct_ptr<TreeLog> *tree_log = current->child_logs_.lookup_ptr_as(path->node_name); - if (tree_log == nullptr) { - return nullptr; - } - current = tree_log->get(); - } - return current; -} - -const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode, - const bNode &node) -{ - const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode); - if (tree_log == nullptr) { - return nullptr; - } - return tree_log->lookup_node_log(node); -} - -const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode, - const StringRef node_name) -{ - const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode); - if (tree_log == nullptr) { - return nullptr; - } - return tree_log->lookup_node_log(node_name); -} - -const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode, - const bNode &node, - const bNodeSocket &socket) -{ - const NodeLog *node_log = ModifierLog::find_node_by_node_editor_context(snode, node); - if (node_log == nullptr) { - return nullptr; - } - return node_log->lookup_socket_log(node, socket); -} - -const NodeLog *ModifierLog::find_node_by_spreadsheet_editor_context( - const SpaceSpreadsheet &sspreadsheet) -{ - Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path; - if (context_path.size() <= 2) { - return nullptr; - } - if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return nullptr; - } - if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { - return nullptr; - } - for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { - if (context->type != SPREADSHEET_CONTEXT_NODE) { - return nullptr; - } - } - Span<SpreadsheetContextNode *> node_contexts = - context_path.as_span().drop_front(2).cast<SpreadsheetContextNode *>(); - - Object *object = ((SpreadsheetContextObject *)context_path[0])->object; - StringRefNull modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name; - if (object == nullptr) { - return nullptr; - } - - const ModifierLog *eval_log = nullptr; - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - if (md->name == modifier_name) { - NodesModifierData *nmd = (NodesModifierData *)md; - eval_log = (const ModifierLog *)nmd->runtime_eval_log; - break; - } - } - } - if (eval_log == nullptr) { - return nullptr; - } - - const TreeLog *tree_log = &eval_log->root_tree(); - for (SpreadsheetContextNode *context : node_contexts.drop_back(1)) { - tree_log = tree_log->lookup_child_log(context->node_name); - if (tree_log == nullptr) { - return nullptr; - } - } - const NodeLog *node_log = tree_log->lookup_node_log(node_contexts.last()->node_name); - return node_log; -} - -void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value) -{ - const CPPType &type = *value.type(); - Span<DSocket> copied_sockets = allocator_->construct_array_copy(sockets); - if (type.is<GeometrySet>()) { - bool log_full_geometry = false; - for (const DSocket &socket : sockets) { - if (main_logger_->log_full_sockets_.contains(socket)) { - log_full_geometry = true; - break; - } - } - - const GeometrySet &geometry_set = *value.get<GeometrySet>(); - destruct_ptr<GeometryValueLog> value_log = allocator_->construct<GeometryValueLog>( - geometry_set, log_full_geometry); - values_.append({copied_sockets, std::move(value_log)}); - } - else if (const ValueOrFieldCPPType *value_or_field_type = - dynamic_cast<const ValueOrFieldCPPType *>(&type)) { - const void *value_or_field = value.get(); - if (value_or_field_type->is_field(value_or_field)) { - GField field = *value_or_field_type->get_field_ptr(value_or_field); - bool log_full_field = false; - if (!field.node().depends_on_input()) { - /* Always log constant fields so that their value can be shown in socket inspection. - * In the future we can also evaluate the field here and only store the value. */ - log_full_field = true; - } - if (!log_full_field) { - for (const DSocket &socket : sockets) { - if (main_logger_->log_full_sockets_.contains(socket)) { - log_full_field = true; - break; - } - } - } - destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>( - std::move(field), log_full_field); - values_.append({copied_sockets, std::move(value_log)}); - } - else { - const CPPType &base_type = value_or_field_type->base_type(); - const void *value = value_or_field_type->get_value_ptr(value_or_field); - void *buffer = allocator_->allocate(base_type.size(), base_type.alignment()); - base_type.copy_construct(value, buffer); - destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>( - GMutablePointer{base_type, buffer}); - values_.append({copied_sockets, std::move(value_log)}); - } - } - else { - void *buffer = allocator_->allocate(type.size(), type.alignment()); - type.copy_construct(value.get(), buffer); - destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>( - GMutablePointer{type, buffer}); - values_.append({copied_sockets, std::move(value_log)}); - } -} - -void LocalGeoLogger::log_multi_value_socket(DSocket socket, Span<GPointer> values) -{ - /* Doesn't have to be logged currently. */ - UNUSED_VARS(socket, values); -} - -void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::string message) -{ - node_warnings_.append({node, {type, std::move(message)}}); -} - -void LocalGeoLogger::log_execution_time(DNode node, std::chrono::microseconds exec_time) -{ - node_exec_times_.append({node, exec_time}); -} - -void LocalGeoLogger::log_used_named_attribute(DNode node, - std::string attribute_name, - eNamedAttrUsage usage) -{ - used_named_attributes_.append({node, {std::move(attribute_name), usage}}); -} - -void LocalGeoLogger::log_debug_message(DNode node, std::string message) -{ - node_debug_messages_.append({node, std::move(message)}); -} - -} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc new file mode 100644 index 00000000000..e4d476e6374 --- /dev/null +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -0,0 +1,1327 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * This file mainly converts a #bNodeTree into a lazy-function graph. This generally works by + * creating a lazy-function for every node, which is then put into the lazy-function graph. Then + * the nodes in the new graph are linked based on links in the original #bNodeTree. Some additional + * nodes are inserted for things like type conversions and multi-input sockets. + * + * Currently, lazy-functions are even created for nodes that don't strictly require it, like + * reroutes or muted nodes. In the future we could avoid that at the cost of additional code + * complexity. So far, this does not seem to be a performance issue. + */ + +#include "NOD_geometry_exec.hh" +#include "NOD_geometry_nodes_lazy_function.hh" +#include "NOD_multi_function.hh" +#include "NOD_node_declaration.hh" + +#include "BLI_map.hh" + +#include "DNA_ID.h" + +#include "BKE_compute_contexts.hh" +#include "BKE_geometry_set.hh" +#include "BKE_type_conversions.hh" + +#include "FN_field_cpp_type.hh" +#include "FN_lazy_function_graph_executor.hh" + +namespace blender::nodes { + +using fn::ValueOrField; +using fn::ValueOrFieldCPPType; +using namespace fn::multi_function_types; + +static const CPPType *get_socket_cpp_type(const bNodeSocketType &typeinfo) +{ + const CPPType *type = typeinfo.geometry_nodes_cpp_type; + if (type == nullptr) { + return nullptr; + } + BLI_assert(type->has_special_member_functions()); + return type; +} + +static const CPPType *get_socket_cpp_type(const bNodeSocket &socket) +{ + return get_socket_cpp_type(*socket.typeinfo); +} + +static const CPPType *get_vector_type(const CPPType &type) +{ + /* This could be generalized in the future. For now we only support a small set of vectors. */ + if (type.is<GeometrySet>()) { + return &CPPType::get<Vector<GeometrySet>>(); + } + if (type.is<ValueOrField<std::string>>()) { + return &CPPType::get<Vector<ValueOrField<std::string>>>(); + } + return nullptr; +} + +/** + * Checks which sockets of the node are available and creates corresponding inputs/outputs on the + * lazy-function. + */ +static void lazy_function_interface_from_node(const bNode &node, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs, + Vector<lf::Input> &r_inputs, + Vector<lf::Output> &r_outputs) +{ + const bool is_muted = node.is_muted(); + const bool supports_laziness = node.typeinfo->geometry_node_execute_supports_laziness || + node.is_group(); + const lf::ValueUsage input_usage = supports_laziness ? lf::ValueUsage::Maybe : + lf::ValueUsage::Used; + for (const bNodeSocket *socket : node.input_sockets()) { + if (!socket->is_available()) { + continue; + } + const CPPType *type = get_socket_cpp_type(*socket); + if (type == nullptr) { + continue; + } + if (socket->is_multi_input() && !is_muted) { + type = get_vector_type(*type); + } + r_inputs.append({socket->identifier, *type, input_usage}); + r_used_inputs.append(socket); + } + for (const bNodeSocket *socket : node.output_sockets()) { + if (!socket->is_available()) { + continue; + } + const CPPType *type = get_socket_cpp_type(*socket); + if (type == nullptr) { + continue; + } + r_outputs.append({socket->identifier, *type}); + r_used_outputs.append(socket); + } +} + +/** + * Used for most normal geometry nodes like Subdivision Surface and Set Position. + */ +class LazyFunctionForGeometryNode : public LazyFunction { + private: + const bNode &node_; + + public: + LazyFunctionForGeometryNode(const bNode &node, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + : node_(node) + { + BLI_assert(node.typeinfo->geometry_node_execute != nullptr); + debug_name_ = node.name; + lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + + GeoNodeExecParams geo_params{node_, params, context}; + + geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now(); + node_.typeinfo->geometry_node_execute(geo_params); + geo_eval_log::TimePoint end_time = geo_eval_log::Clock::now(); + + if (geo_eval_log::GeoModifierLog *modifier_log = user_data->modifier_data->eval_log) { + geo_eval_log::GeoTreeLogger &tree_logger = modifier_log->get_local_tree_logger( + *user_data->compute_context); + tree_logger.node_execution_times.append({node_.name, start_time, end_time}); + } + } +}; + +/** + * Used to gather all inputs of a multi-input socket. A separate node is necessary, because + * multi-inputs are not supported in lazy-function graphs. + */ +class LazyFunctionForMultiInput : public LazyFunction { + private: + const CPPType *base_type_; + + public: + LazyFunctionForMultiInput(const bNodeSocket &socket) + { + debug_name_ = "Multi Input"; + base_type_ = get_socket_cpp_type(socket); + BLI_assert(base_type_ != nullptr); + BLI_assert(socket.is_multi_input()); + for (const bNodeLink *link : socket.directly_linked_links()) { + if (!link->is_muted()) { + inputs_.append({"Input", *base_type_}); + } + } + const CPPType *vector_type = get_vector_type(*base_type_); + BLI_assert(vector_type != nullptr); + outputs_.append({"Output", *vector_type}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + /* Currently we only have multi-inputs for geometry and string sockets. This could be + * generalized in the future. */ + base_type_->to_static_type_tag<GeometrySet, ValueOrField<std::string>>([&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (std::is_void_v<T>) { + /* This type is not support in this node for now. */ + BLI_assert_unreachable(); + } + else { + void *output_ptr = params.get_output_data_ptr(0); + Vector<T> &values = *new (output_ptr) Vector<T>(); + for (const int i : inputs_.index_range()) { + values.append(params.extract_input<T>(i)); + } + params.output_set(0); + } + }); + } +}; + +/** + * Simple lazy-function that just forwards the input. + */ +class LazyFunctionForRerouteNode : public LazyFunction { + public: + LazyFunctionForRerouteNode(const CPPType &type) + { + debug_name_ = "Reroute"; + inputs_.append({"Input", type}); + outputs_.append({"Output", type}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + void *input_value = params.try_get_input_data_ptr(0); + void *output_value = params.get_output_data_ptr(0); + BLI_assert(input_value != nullptr); + BLI_assert(output_value != nullptr); + const CPPType &type = *inputs_[0].type; + type.move_construct(input_value, output_value); + params.output_set(0); + } +}; + +/** + * Executes a multi-function. If all inputs are single values, the results will also be single + * values. If any input is a field, the outputs will also be fields. + */ +static void execute_multi_function_on_value_or_field( + const MultiFunction &fn, + const std::shared_ptr<MultiFunction> &owned_fn, + const Span<const ValueOrFieldCPPType *> input_types, + const Span<const ValueOrFieldCPPType *> output_types, + const Span<const void *> input_values, + const Span<void *> output_values) +{ + BLI_assert(fn.param_amount() == input_types.size() + output_types.size()); + BLI_assert(input_types.size() == input_values.size()); + BLI_assert(output_types.size() == output_values.size()); + + /* Check if any input is a field. */ + bool any_input_is_field = false; + for (const int i : input_types.index_range()) { + const ValueOrFieldCPPType &type = *input_types[i]; + const void *value_or_field = input_values[i]; + if (type.is_field(value_or_field)) { + any_input_is_field = true; + break; + } + } + + if (any_input_is_field) { + /* Convert all inputs into fields, so that they can be used as input in the new field. */ + Vector<GField> input_fields; + for (const int i : input_types.index_range()) { + const ValueOrFieldCPPType &type = *input_types[i]; + const void *value_or_field = input_values[i]; + input_fields.append(type.as_field(value_or_field)); + } + + /* Construct the new field node. */ + std::shared_ptr<fn::FieldOperation> operation; + if (owned_fn) { + operation = std::make_shared<fn::FieldOperation>(owned_fn, std::move(input_fields)); + } + else { + operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields)); + } + + /* Store the new fields in the output. */ + for (const int i : output_types.index_range()) { + const ValueOrFieldCPPType &type = *output_types[i]; + void *value_or_field = output_values[i]; + type.construct_from_field(value_or_field, GField{operation, i}); + } + } + else { + /* In this case, the multi-function is evaluated directly. */ + MFParamsBuilder params{fn, 1}; + MFContextBuilder context; + + for (const int i : input_types.index_range()) { + const ValueOrFieldCPPType &type = *input_types[i]; + const CPPType &base_type = type.base_type(); + const void *value_or_field = input_values[i]; + const void *value = type.get_value_ptr(value_or_field); + params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, value)); + } + for (const int i : output_types.index_range()) { + const ValueOrFieldCPPType &type = *output_types[i]; + const CPPType &base_type = type.base_type(); + void *value_or_field = output_values[i]; + type.default_construct(value_or_field); + void *value = type.get_value_ptr(value_or_field); + base_type.destruct(value); + params.add_uninitialized_single_output(GMutableSpan{base_type, value, 1}); + } + fn.call(IndexRange(1), params, context); + } +} + +/** + * Behavior of muted nodes: + * - Some inputs are forwarded to outputs without changes. + * - Some inputs are converted to a different type which becomes the output. + * - Some outputs are value initialized because they don't have a corresponding input. + */ +class LazyFunctionForMutedNode : public LazyFunction { + private: + Array<int> input_by_output_index_; + + public: + LazyFunctionForMutedNode(const bNode &node, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + { + debug_name_ = "Muted"; + lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); + for (lf::Input &fn_input : inputs_) { + fn_input.usage = lf::ValueUsage::Maybe; + } + + for (lf::Input &fn_input : inputs_) { + fn_input.usage = lf::ValueUsage::Unused; + } + + input_by_output_index_.reinitialize(outputs_.size()); + input_by_output_index_.fill(-1); + for (const bNodeLink *internal_link : node.internal_links_span()) { + const int input_i = r_used_inputs.first_index_of_try(internal_link->fromsock); + const int output_i = r_used_outputs.first_index_of_try(internal_link->tosock); + if (ELEM(-1, input_i, output_i)) { + continue; + } + input_by_output_index_[output_i] = input_i; + inputs_[input_i].usage = lf::ValueUsage::Maybe; + } + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + for (const int output_i : outputs_.index_range()) { + if (params.output_was_set(output_i)) { + continue; + } + const CPPType &output_type = *outputs_[output_i].type; + void *output_value = params.get_output_data_ptr(output_i); + const int input_i = input_by_output_index_[output_i]; + if (input_i == -1) { + /* The output does not have a corresponding input. */ + output_type.value_initialize(output_value); + params.output_set(output_i); + continue; + } + const void *input_value = params.try_get_input_data_ptr_or_request(input_i); + if (input_value == nullptr) { + continue; + } + const CPPType &input_type = *inputs_[input_i].type; + if (input_type == output_type) { + /* Forward the value as is. */ + input_type.copy_construct(input_value, output_value); + params.output_set(output_i); + continue; + } + /* Perform a type conversion and then format the value. */ + const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); + const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&input_type); + const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&output_type); + if (from_field_type != nullptr && to_field_type != nullptr) { + const CPPType &from_base_type = from_field_type->base_type(); + const CPPType &to_base_type = to_field_type->base_type(); + if (conversions.is_convertible(from_base_type, to_base_type)) { + const MultiFunction &multi_fn = *conversions.get_conversion_multi_function( + MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + execute_multi_function_on_value_or_field( + multi_fn, {}, {from_field_type}, {to_field_type}, {input_value}, {output_value}); + } + params.output_set(output_i); + continue; + } + /* Use a value initialization if the conversion does not work. */ + output_type.value_initialize(output_value); + params.output_set(output_i); + } + } +}; + +/** + * Type conversions are generally implemented as multi-functions. This node checks if the input is + * a field or single value and outputs a field or single value respectively. + */ +class LazyFunctionForMultiFunctionConversion : public LazyFunction { + private: + const MultiFunction &fn_; + const ValueOrFieldCPPType &from_type_; + const ValueOrFieldCPPType &to_type_; + const Vector<const bNodeSocket *> target_sockets_; + + public: + LazyFunctionForMultiFunctionConversion(const MultiFunction &fn, + const ValueOrFieldCPPType &from, + const ValueOrFieldCPPType &to, + Vector<const bNodeSocket *> &&target_sockets) + : fn_(fn), from_type_(from), to_type_(to), target_sockets_(std::move(target_sockets)) + { + debug_name_ = "Convert"; + inputs_.append({"From", from}); + outputs_.append({"To", to}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + const void *from_value = params.try_get_input_data_ptr(0); + void *to_value = params.get_output_data_ptr(0); + BLI_assert(from_value != nullptr); + BLI_assert(to_value != nullptr); + + execute_multi_function_on_value_or_field( + fn_, {}, {&from_type_}, {&to_type_}, {from_value}, {to_value}); + + params.output_set(0); + } +}; + +/** + * This lazy-function wraps nodes that are implemented as multi-function (mostly math nodes). + */ +class LazyFunctionForMultiFunctionNode : public LazyFunction { + private: + const bNode &node_; + const NodeMultiFunctions::Item fn_item_; + Vector<const ValueOrFieldCPPType *> input_types_; + Vector<const ValueOrFieldCPPType *> output_types_; + Vector<const bNodeSocket *> output_sockets_; + + public: + LazyFunctionForMultiFunctionNode(const bNode &node, + NodeMultiFunctions::Item fn_item, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + : node_(node), fn_item_(std::move(fn_item)) + { + BLI_assert(fn_item_.fn != nullptr); + debug_name_ = node.name; + lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); + for (const lf::Input &fn_input : inputs_) { + input_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_input.type)); + } + for (const lf::Output &fn_output : outputs_) { + output_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_output.type)); + } + output_sockets_ = r_used_outputs; + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + Vector<const void *> input_values(inputs_.size()); + Vector<void *> output_values(outputs_.size()); + for (const int i : inputs_.index_range()) { + input_values[i] = params.try_get_input_data_ptr(i); + } + for (const int i : outputs_.index_range()) { + output_values[i] = params.get_output_data_ptr(i); + } + execute_multi_function_on_value_or_field( + *fn_item_.fn, fn_item_.owned_fn, input_types_, output_types_, input_values, output_values); + for (const int i : outputs_.index_range()) { + params.output_set(i); + } + } +}; + +/** + * Some sockets have non-trivial implicit inputs (e.g. the Position input of the Set Position + * node). Those are implemented as a separate node that outputs the value. + */ +class LazyFunctionForImplicitInput : public LazyFunction { + private: + /** + * The function that generates the implicit input. The passed in memory is uninitialized. + */ + std::function<void(void *)> init_fn_; + + public: + LazyFunctionForImplicitInput(const CPPType &type, std::function<void(void *)> init_fn) + : init_fn_(std::move(init_fn)) + { + debug_name_ = "Input"; + outputs_.append({"Output", type}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + void *value = params.get_output_data_ptr(0); + init_fn_(value); + params.output_set(0); + } +}; + +/** + * The viewer node does not have outputs. Instead it is executed because the executor knows that it + * has side effects. The side effect is that the inputs to the viewer are logged. + */ +class LazyFunctionForViewerNode : public LazyFunction { + private: + const bNode &bnode_; + /** The field is only logged when it is linked. */ + bool use_field_input_ = true; + + public: + LazyFunctionForViewerNode(const bNode &bnode, Vector<const bNodeSocket *> &r_used_inputs) + : bnode_(bnode) + { + debug_name_ = "Viewer"; + Vector<const bNodeSocket *> dummy_used_outputs; + lazy_function_interface_from_node(bnode, r_used_inputs, dummy_used_outputs, inputs_, outputs_); + if (!r_used_inputs[1]->is_directly_linked()) { + use_field_input_ = false; + r_used_inputs.pop_last(); + inputs_.pop_last(); + } + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + + GeometrySet geometry = params.extract_input<GeometrySet>(0); + + GField field; + if (use_field_input_) { + const void *value_or_field = params.try_get_input_data_ptr(1); + BLI_assert(value_or_field != nullptr); + const ValueOrFieldCPPType &value_or_field_type = static_cast<const ValueOrFieldCPPType &>( + *inputs_[1].type); + field = value_or_field_type.as_field(value_or_field); + } + + geo_eval_log::GeoTreeLogger &tree_logger = + user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); + tree_logger.log_viewer_node(bnode_, geometry, field); + } +}; + +/** + * This lazy-function wraps a group node. Internally it just executes the lazy-function graph of + * the referenced group. + */ +class LazyFunctionForGroupNode : public LazyFunction { + private: + const bNode &group_node_; + std::optional<GeometryNodesLazyFunctionLogger> lf_logger_; + std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_; + std::optional<lf::GraphExecutor> graph_executor_; + + public: + LazyFunctionForGroupNode(const bNode &group_node, + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + : group_node_(group_node) + { + debug_name_ = group_node.name; + lazy_function_interface_from_node( + group_node, r_used_inputs, r_used_outputs, inputs_, outputs_); + + bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(group_node_.id); + BLI_assert(group_btree != nullptr); + + Vector<const lf::OutputSocket *> graph_inputs; + for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) { + if (socket != nullptr) { + graph_inputs.append(socket); + } + } + Vector<const lf::InputSocket *> graph_outputs; + if (const bNode *group_output_bnode = group_btree->group_output_node()) { + for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { + const lf::Socket *socket = lf_graph_info.mapping.dummy_socket_map.lookup_default(bsocket, + nullptr); + if (socket != nullptr) { + graph_outputs.append(&socket->as_input()); + } + } + } + + lf_logger_.emplace(lf_graph_info); + lf_side_effect_provider_.emplace(lf_graph_info); + graph_executor_.emplace(lf_graph_info.graph, + std::move(graph_inputs), + std::move(graph_outputs), + &*lf_logger_, + &*lf_side_effect_provider_); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + + /* The compute context changes when entering a node group. */ + bke::NodeGroupComputeContext compute_context{user_data->compute_context, group_node_.name}; + GeoNodesLFUserData group_user_data = *user_data; + group_user_data.compute_context = &compute_context; + + lf::Context group_context = context; + group_context.user_data = &group_user_data; + + graph_executor_->execute(params, group_context); + } + + void *init_storage(LinearAllocator<> &allocator) const + { + return graph_executor_->init_storage(allocator); + } + + void destruct_storage(void *storage) const + { + graph_executor_->destruct_storage(storage); + } +}; + +static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator, + const bNodeSocket &bsocket) +{ + const bNodeSocketType &typeinfo = *bsocket.typeinfo; + const CPPType *type = get_socket_cpp_type(typeinfo); + if (type == nullptr) { + return {}; + } + void *buffer = allocator.allocate(type->size(), type->alignment()); + typeinfo.get_geometry_nodes_cpp_value(bsocket, buffer); + return {type, buffer}; +} + +/** + * Utility class to build a lazy-function graph based on a geometry nodes tree. + * This is mainly a separate class because it makes it easier to have variables that can be + * accessed by many functions. + */ +struct GeometryNodesLazyFunctionGraphBuilder { + private: + const bNodeTree &btree_; + GeometryNodesLazyFunctionGraphInfo *lf_graph_info_; + lf::Graph *lf_graph_; + GeometryNodeLazyFunctionGraphMapping *mapping_; + MultiValueMap<const bNodeSocket *, lf::InputSocket *> input_socket_map_; + Map<const bNodeSocket *, lf::OutputSocket *> output_socket_map_; + Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes_; + const bke::DataTypeConversions *conversions_; + + /** + * All group input nodes are combined into one dummy node in the lazy-function graph. + * If some input has an invalid type, it is ignored in the new graph. In this case null and -1 is + * used in the vectors below. + */ + Vector<const CPPType *> group_input_types_; + Vector<int> group_input_indices_; + lf::DummyNode *group_input_lf_node_; + + /** + * The output types or null if an output is invalid. Each group output node gets a separate + * corresponding dummy node in the new graph. + */ + Vector<const CPPType *> group_output_types_; + Vector<int> group_output_indices_; + + public: + GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree, + GeometryNodesLazyFunctionGraphInfo &lf_graph_info) + : btree_(btree), lf_graph_info_(&lf_graph_info) + { + } + + void build() + { + btree_.ensure_topology_cache(); + + lf_graph_ = &lf_graph_info_->graph; + mapping_ = &lf_graph_info_->mapping; + conversions_ = &bke::get_implicit_type_conversions(); + + this->prepare_node_multi_functions(); + this->prepare_group_inputs(); + this->prepare_group_outputs(); + this->build_group_input_node(); + this->handle_nodes(); + this->handle_links(); + this->add_default_inputs(); + + lf_graph_->update_node_indices(); + } + + private: + void prepare_node_multi_functions() + { + lf_graph_info_->node_multi_functions = std::make_unique<NodeMultiFunctions>(btree_); + } + + void prepare_group_inputs() + { + LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.inputs) { + const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo); + if (type != nullptr) { + const int index = group_input_types_.append_and_get_index(type); + group_input_indices_.append(index); + } + else { + group_input_indices_.append(-1); + } + } + } + + void prepare_group_outputs() + { + LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.outputs) { + const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo); + if (type != nullptr) { + const int index = group_output_types_.append_and_get_index(type); + group_output_indices_.append(index); + } + else { + group_output_indices_.append(-1); + } + } + } + + void build_group_input_node() + { + /* Create a dummy node for the group inputs. */ + group_input_lf_node_ = &lf_graph_->add_dummy({}, group_input_types_); + for (const int group_input_index : group_input_indices_) { + if (group_input_index == -1) { + mapping_->group_input_sockets.append(nullptr); + } + else { + mapping_->group_input_sockets.append(&group_input_lf_node_->output(group_input_index)); + } + } + } + + void handle_nodes() + { + /* Insert all nodes into the lazy function graph. */ + for (const bNode *bnode : btree_.all_nodes()) { + const bNodeType *node_type = bnode->typeinfo; + if (node_type == nullptr) { + continue; + } + if (bnode->is_muted()) { + this->handle_muted_node(*bnode); + continue; + } + switch (node_type->type) { + case NODE_FRAME: { + /* Ignored. */ + break; + } + case NODE_REROUTE: { + this->handle_reroute_node(*bnode); + break; + } + case NODE_GROUP_INPUT: { + this->handle_group_input_node(*bnode); + break; + } + case NODE_GROUP_OUTPUT: { + this->handle_group_output_node(*bnode); + break; + } + case NODE_CUSTOM_GROUP: + case NODE_GROUP: { + this->handle_group_node(*bnode); + break; + } + case GEO_NODE_VIEWER: { + this->handle_viewer_node(*bnode); + break; + } + default: { + if (node_type->geometry_node_execute) { + this->handle_geometry_node(*bnode); + break; + } + const NodeMultiFunctions::Item &fn_item = lf_graph_info_->node_multi_functions->try_get( + *bnode); + if (fn_item.fn != nullptr) { + this->handle_multi_function_node(*bnode, fn_item); + } + /* Nodes that don't match any of the criteria above are just ignored. */ + break; + } + } + } + } + + void handle_muted_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForMutedNode>( + bnode, used_inputs, used_outputs); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_reroute_node(const bNode &bnode) + { + const bNodeSocket &input_bsocket = bnode.input_socket(0); + const bNodeSocket &output_bsocket = bnode.output_socket(0); + const CPPType *type = get_socket_cpp_type(input_bsocket); + if (type == nullptr) { + return; + } + + auto lazy_function = std::make_unique<LazyFunctionForRerouteNode>(*type); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + lf::InputSocket &lf_input = lf_node.input(0); + lf::OutputSocket &lf_output = lf_node.output(0); + input_socket_map_.add(&input_bsocket, &lf_input); + output_socket_map_.add_new(&output_bsocket, &lf_output); + mapping_->bsockets_by_lf_socket_map.add(&lf_input, &input_bsocket); + mapping_->bsockets_by_lf_socket_map.add(&lf_output, &output_bsocket); + } + + void handle_group_input_node(const bNode &bnode) + { + for (const int btree_index : group_input_indices_.index_range()) { + const int lf_index = group_input_indices_[btree_index]; + if (lf_index == -1) { + continue; + } + const bNodeSocket &bsocket = bnode.output_socket(btree_index); + lf::OutputSocket &lf_socket = group_input_lf_node_->output(lf_index); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_group_output_node(const bNode &bnode) + { + lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy(group_output_types_, {}); + for (const int btree_index : group_output_indices_.index_range()) { + const int lf_index = group_output_indices_[btree_index]; + if (lf_index == -1) { + continue; + } + const bNodeSocket &bsocket = bnode.input_socket(btree_index); + lf::InputSocket &lf_socket = group_output_lf_node.input(lf_index); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->dummy_socket_map.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_group_node(const bNode &bnode) + { + const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id); + if (group_btree == nullptr) { + return; + } + const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = + ensure_geometry_nodes_lazy_function_graph(*group_btree); + if (group_lf_graph_info == nullptr) { + return; + } + + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForGroupNode>( + bnode, *group_lf_graph_info, used_inputs, used_outputs); + lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + BLI_assert(!bsocket.is_multi_input()); + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + mapping_->group_node_map.add(&bnode, &lf_node); + } + + void handle_geometry_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>( + bnode, used_inputs, used_outputs); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + lf::InputSocket &lf_socket = lf_node.input(i); + + if (bsocket.is_multi_input()) { + auto multi_input_lazy_function = std::make_unique<LazyFunctionForMultiInput>(bsocket); + lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function); + lf_graph_info_->functions.append(std::move(multi_input_lazy_function)); + lf_graph_->add_link(lf_multi_input_node.output(0), lf_socket); + multi_input_socket_nodes_.add_new(&bsocket, &lf_multi_input_node); + for (lf::InputSocket *lf_multi_input_socket : lf_multi_input_node.inputs()) { + mapping_->bsockets_by_lf_socket_map.add(lf_multi_input_socket, &bsocket); + } + } + else { + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item) + { + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForMultiFunctionNode>( + bnode, fn_item, used_inputs, used_outputs); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + BLI_assert(!bsocket.is_multi_input()); + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_viewer_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_inputs; + auto lazy_function = std::make_unique<LazyFunctionForViewerNode>(bnode, used_inputs); + lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + + mapping_->viewer_node_map.add(&bnode, &lf_node); + } + + void handle_links() + { + for (const auto item : output_socket_map_.items()) { + this->insert_links_from_socket(*item.key, *item.value); + } + } + + void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket) + { + const Span<const bNodeLink *> links_from_bsocket = from_bsocket.directly_linked_links(); + + struct TypeWithLinks { + const CPPType *type; + Vector<const bNodeLink *> links; + }; + + /* Group available target sockets by type so that they can be handled together. */ + Vector<TypeWithLinks> types_with_links; + for (const bNodeLink *link : links_from_bsocket) { + if (link->is_muted()) { + continue; + } + const bNodeSocket &to_bsocket = *link->tosock; + if (!to_bsocket.is_available()) { + continue; + } + const CPPType *to_type = get_socket_cpp_type(to_bsocket); + if (to_type == nullptr) { + continue; + } + bool inserted = false; + for (TypeWithLinks &types_with_links : types_with_links) { + if (types_with_links.type == to_type) { + types_with_links.links.append(link); + inserted = true; + break; + } + } + if (inserted) { + continue; + } + types_with_links.append({to_type, {link}}); + } + + for (const TypeWithLinks &type_with_links : types_with_links) { + const CPPType &to_type = *type_with_links.type; + const Span<const bNodeLink *> links = type_with_links.links; + + Vector<const bNodeSocket *> target_bsockets; + for (const bNodeLink *link : links) { + target_bsockets.append(link->tosock); + } + + lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary( + from_lf_socket, to_type, std::move(target_bsockets)); + + auto make_input_link_or_set_default = [&](lf::InputSocket &to_lf_socket) { + if (converted_from_lf_socket == nullptr) { + const void *default_value = to_type.default_value(); + to_lf_socket.set_default_value(default_value); + } + else { + lf_graph_->add_link(*converted_from_lf_socket, to_lf_socket); + } + }; + + for (const bNodeLink *link : links) { + const bNodeSocket &to_bsocket = *link->tosock; + if (to_bsocket.is_multi_input()) { + /* TODO: Cache this index on the link. */ + int link_index = 0; + for (const bNodeLink *multi_input_link : to_bsocket.directly_linked_links()) { + if (multi_input_link == link) { + break; + } + if (!multi_input_link->is_muted()) { + link_index++; + } + } + if (to_bsocket.owner_node().is_muted()) { + if (link_index == 0) { + for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) { + make_input_link_or_set_default(*to_lf_socket); + } + } + } + else { + lf::Node *multi_input_lf_node = multi_input_socket_nodes_.lookup_default(&to_bsocket, + nullptr); + if (multi_input_lf_node == nullptr) { + continue; + } + make_input_link_or_set_default(multi_input_lf_node->input(link_index)); + } + } + else { + for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) { + make_input_link_or_set_default(*to_lf_socket); + } + } + } + } + } + + lf::OutputSocket *insert_type_conversion_if_necessary( + lf::OutputSocket &from_socket, + const CPPType &to_type, + Vector<const bNodeSocket *> &&target_sockets) + { + const CPPType &from_type = from_socket.type(); + if (from_type == to_type) { + return &from_socket; + } + const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&from_type); + const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type); + if (from_field_type != nullptr && to_field_type != nullptr) { + const CPPType &from_base_type = from_field_type->base_type(); + const CPPType &to_base_type = to_field_type->base_type(); + if (conversions_->is_convertible(from_base_type, to_base_type)) { + const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function( + MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + auto fn = std::make_unique<LazyFunctionForMultiFunctionConversion>( + multi_fn, *from_field_type, *to_field_type, std::move(target_sockets)); + lf::Node &conversion_node = lf_graph_->add_function(*fn); + lf_graph_info_->functions.append(std::move(fn)); + lf_graph_->add_link(from_socket, conversion_node.input(0)); + return &conversion_node.output(0); + } + } + return nullptr; + } + + void add_default_inputs() + { + for (auto item : input_socket_map_.items()) { + const bNodeSocket &bsocket = *item.key; + const Span<lf::InputSocket *> lf_sockets = item.value; + for (lf::InputSocket *lf_socket : lf_sockets) { + if (lf_socket->origin() != nullptr) { + /* Is linked already. */ + continue; + } + this->add_default_input(bsocket, *lf_socket); + } + } + } + + void add_default_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) + { + if (this->try_add_implicit_input(input_bsocket, input_lf_socket)) { + return; + } + GMutablePointer value = get_socket_default_value(lf_graph_info_->allocator, input_bsocket); + if (value.get() == nullptr) { + /* Not possible to add a default value. */ + return; + } + input_lf_socket.set_default_value(value.get()); + if (!value.type()->is_trivially_destructible()) { + lf_graph_info_->values_to_destruct.append(value); + } + } + + bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) + { + const bNode &bnode = input_bsocket.owner_node(); + const NodeDeclaration *node_declaration = bnode.declaration(); + if (node_declaration == nullptr) { + return false; + } + const SocketDeclaration &socket_declaration = + *node_declaration->inputs()[input_bsocket.index()]; + if (socket_declaration.input_field_type() != InputSocketFieldType::Implicit) { + return false; + } + const CPPType &type = input_lf_socket.type(); + std::function<void(void *)> init_fn = this->get_implicit_input_init_function(bnode, + input_bsocket); + if (!init_fn) { + return false; + } + + auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn)); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + lf_graph_->add_link(lf_node.output(0), input_lf_socket); + return true; + } + + std::function<void(void *)> get_implicit_input_init_function(const bNode &bnode, + const bNodeSocket &bsocket) + { + const bNodeSocketType &socket_type = *bsocket.typeinfo; + if (socket_type.type == SOCK_VECTOR) { + if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { + StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == + GEO_NODE_CURVE_HANDLE_LEFT ? + "handle_left" : + "handle_right"; + return [side](void *r_value) { + new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side)); + }; + } + else if (bnode.type == GEO_NODE_EXTRUDE_MESH) { + return [](void *r_value) { + new (r_value) + ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>())); + }; + } + else { + return [](void *r_value) { + new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position")); + }; + } + } + else if (socket_type.type == SOCK_INT) { + if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { + return [](void *r_value) { + new (r_value) + ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>())); + }; + } + else { + return [](void *r_value) { + new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>())); + }; + } + } + return {}; + } +}; + +const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( + const bNodeTree &btree) +{ + btree.ensure_topology_cache(); + if (btree.has_link_cycle()) { + return nullptr; + } + + std::unique_ptr<GeometryNodesLazyFunctionGraphInfo> &lf_graph_info_ptr = + btree.runtime->geometry_nodes_lazy_function_graph_info; + + if (lf_graph_info_ptr) { + return lf_graph_info_ptr.get(); + } + std::lock_guard lock{btree.runtime->geometry_nodes_lazy_function_graph_info_mutex}; + if (lf_graph_info_ptr) { + return lf_graph_info_ptr.get(); + } + + auto lf_graph_info = std::make_unique<GeometryNodesLazyFunctionGraphInfo>(); + GeometryNodesLazyFunctionGraphBuilder builder{btree, *lf_graph_info}; + builder.build(); + + lf_graph_info_ptr = std::move(lf_graph_info); + return lf_graph_info_ptr.get(); +} + +GeometryNodesLazyFunctionLogger::GeometryNodesLazyFunctionLogger( + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info) + : lf_graph_info_(lf_graph_info) +{ +} + +void GeometryNodesLazyFunctionLogger::log_socket_value( + const fn::lazy_function::Socket &lf_socket, + const GPointer value, + const fn::lazy_function::Context &context) const +{ + const Span<const bNodeSocket *> bsockets = + lf_graph_info_.mapping.bsockets_by_lf_socket_map.lookup(&lf_socket); + if (bsockets.is_empty()) { + return; + } + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + if (user_data->modifier_data->eval_log == nullptr) { + return; + } + geo_eval_log::GeoTreeLogger &tree_logger = + user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); + for (const bNodeSocket *bsocket : bsockets) { + /* Avoid logging to some sockets when the same value will also be logged to a linked socket. + * This reduces the number of logged values without losing information. */ + if (bsocket->is_input() && bsocket->is_directly_linked()) { + continue; + } + const bNode &bnode = bsocket->owner_node(); + if (bnode.is_reroute()) { + continue; + } + tree_logger.log_value(bsocket->owner_node(), *bsocket, value); + } +} + +static std::mutex dump_error_context_mutex; + +void GeometryNodesLazyFunctionLogger::dump_when_outputs_are_missing( + const lf::FunctionNode &node, + Span<const lf::OutputSocket *> missing_sockets, + const lf::Context &context) const +{ + std::lock_guard lock{dump_error_context_mutex}; + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + user_data->compute_context->print_stack(std::cout, node.name()); + std::cout << "Missing outputs:\n"; + for (const lf::OutputSocket *socket : missing_sockets) { + std::cout << " " << socket->name() << "\n"; + } +} + +void GeometryNodesLazyFunctionLogger::dump_when_input_is_set_twice( + const lf::InputSocket &target_socket, + const lf::OutputSocket &from_socket, + const lf::Context &context) const +{ + std::lock_guard lock{dump_error_context_mutex}; + + std::stringstream ss; + ss << from_socket.node().name() << ":" << from_socket.name() << " -> " + << target_socket.node().name() << ":" << target_socket.name(); + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + user_data->compute_context->print_stack(std::cout, ss.str()); +} + +GeometryNodesLazyFunctionSideEffectProvider::GeometryNodesLazyFunctionSideEffectProvider( + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info) + : lf_graph_info_(lf_graph_info) +{ +} + +Vector<const lf::FunctionNode *> GeometryNodesLazyFunctionSideEffectProvider:: + get_nodes_with_side_effects(const lf::Context &context) const +{ + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + const ComputeContextHash &context_hash = user_data->compute_context->hash(); + const GeoNodesModifierData &modifier_data = *user_data->modifier_data; + return modifier_data.side_effect_nodes->lookup(context_hash); +} + +GeometryNodesLazyFunctionGraphInfo::GeometryNodesLazyFunctionGraphInfo() = default; +GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo() +{ + for (GMutablePointer &p : this->values_to_destruct) { + p.destruct(); + } +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc new file mode 100644 index 00000000000..350b199cd60 --- /dev/null +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -0,0 +1,608 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "NOD_geometry_nodes_lazy_function.hh" +#include "NOD_geometry_nodes_log.hh" + +#include "BKE_compute_contexts.hh" +#include "BKE_curves.hh" +#include "BKE_node_runtime.hh" + +#include "FN_field_cpp_type.hh" + +#include "DNA_modifier_types.h" +#include "DNA_space_types.h" + +namespace blender::nodes::geo_eval_log { + +using fn::FieldInput; +using fn::FieldInputs; + +GenericValueLog::~GenericValueLog() +{ + this->value.destruct(); +} + +FieldInfoLog::FieldInfoLog(const GField &field) : type(field.cpp_type()) +{ + const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs(); + + /* Put the deduplicated field inputs into a vector so that they can be sorted below. */ + Vector<std::reference_wrapper<const FieldInput>> field_inputs; + if (field_input_nodes) { + field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(), + field_input_nodes->deduplicated_nodes.end()); + } + + std::sort( + field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { + const int index_a = (int)a.category(); + const int index_b = (int)b.category(); + if (index_a == index_b) { + return a.socket_inspection_name().size() < b.socket_inspection_name().size(); + } + return index_a < index_b; + }); + + for (const FieldInput &field_input : field_inputs) { + this->input_tooltips.append(field_input.socket_inspection_name()); + } +} + +GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set) +{ + static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_VOLUME}; + + /* Keep track handled attribute names to make sure that we do not return the same name twice. + * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge + * attributes with the same name but different domains or data types on separate components. */ + Set<StringRef> names; + + geometry_set.attribute_foreach( + all_component_types, + true, + [&](const bke::AttributeIDRef &attribute_id, + const bke::AttributeMetaData &meta_data, + const GeometryComponent &UNUSED(component)) { + if (attribute_id.is_named() && names.add(attribute_id.name())) { + this->attributes.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); + } + }); + + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + this->component_types.append(component->type()); + switch (component->type()) { + case GEO_COMPONENT_TYPE_MESH: { + const MeshComponent &mesh_component = *(const MeshComponent *)component; + MeshInfo &info = this->mesh_info.emplace(); + info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + const CurveComponent &curve_component = *(const CurveComponent *)component; + CurveInfo &info = this->curve_info.emplace(); + info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; + PointCloudInfo &info = this->pointcloud_info.emplace(); + info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + const InstancesComponent &instances_component = *(const InstancesComponent *)component; + InstancesInfo &info = this->instances_info.emplace(); + info.instances_num = instances_component.instances_num(); + break; + } + case GEO_COMPONENT_TYPE_EDIT: { + const GeometryComponentEditData &edit_component = *( + const GeometryComponentEditData *)component; + if (const bke::CurvesEditHints *curve_edit_hints = + edit_component.curves_edit_hints_.get()) { + EditDataInfo &info = this->edit_data_info.emplace(); + info.has_deform_matrices = curve_edit_hints->deform_mats.has_value(); + info.has_deformed_positions = curve_edit_hints->positions.has_value(); + } + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + break; + } + } + } +} + +/* Avoid generating these in every translation unit. */ +GeoModifierLog::GeoModifierLog() = default; +GeoModifierLog::~GeoModifierLog() = default; + +GeoTreeLogger::GeoTreeLogger() = default; +GeoTreeLogger::~GeoTreeLogger() = default; + +GeoNodeLog::GeoNodeLog() = default; +GeoNodeLog::~GeoNodeLog() = default; + +GeoTreeLog::GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers) + : modifier_log_(modifier_log), tree_loggers_(std::move(tree_loggers)) +{ + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const ComputeContextHash &hash : tree_logger->children_hashes) { + children_hashes_.add(hash); + } + } +} + +GeoTreeLog::~GeoTreeLog() = default; + +void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, const GPointer value) +{ + const CPPType &type = *value.type(); + + auto store_logged_value = [&](destruct_ptr<ValueLog> value_log) { + auto &socket_values = socket.in_out == SOCK_IN ? this->input_socket_values : + this->output_socket_values; + socket_values.append({node.name, socket.identifier, std::move(value_log)}); + }; + + auto log_generic_value = [&](const CPPType &type, const void *value) { + void *buffer = this->allocator->allocate(type.size(), type.alignment()); + type.copy_construct(value, buffer); + store_logged_value(this->allocator->construct<GenericValueLog>(GMutablePointer{type, buffer})); + }; + + if (type.is<GeometrySet>()) { + const GeometrySet &geometry = *value.get<GeometrySet>(); + store_logged_value(this->allocator->construct<GeometryInfoLog>(geometry)); + } + else if (const auto *value_or_field_type = dynamic_cast<const fn::ValueOrFieldCPPType *>( + &type)) { + const void *value_or_field = value.get(); + const CPPType &base_type = value_or_field_type->base_type(); + if (value_or_field_type->is_field(value_or_field)) { + const GField *field = value_or_field_type->get_field_ptr(value_or_field); + if (field->node().depends_on_input()) { + store_logged_value(this->allocator->construct<FieldInfoLog>(*field)); + } + else { + BUFFER_FOR_CPP_TYPE_VALUE(base_type, value); + fn::evaluate_constant_field(*field, value); + log_generic_value(base_type, value); + } + } + else { + const void *value = value_or_field_type->get_value_ptr(value_or_field); + log_generic_value(base_type, value); + } + } + else { + log_generic_value(type, value.get()); + } +} + +void GeoTreeLogger::log_viewer_node(const bNode &viewer_node, + const GeometrySet &geometry, + const GField &field) +{ + destruct_ptr<ViewerNodeLog> log = this->allocator->construct<ViewerNodeLog>(); + log->geometry = geometry; + log->field = field; + log->geometry.ensure_owns_direct_data(); + this->viewer_node_logs.append({viewer_node.name, std::move(log)}); +} + +void GeoTreeLog::ensure_node_warnings() +{ + if (reduced_node_warnings_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::WarningWithNode &warnings : tree_logger->node_warnings) { + this->nodes.lookup_or_add_default(warnings.node_name).warnings.append(warnings.warning); + this->all_warnings.append(warnings.warning); + } + } + for (const ComputeContextHash &child_hash : children_hashes_) { + GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash); + child_log.ensure_node_warnings(); + const std::optional<std::string> &group_node_name = + child_log.tree_loggers_[0]->group_node_name; + if (group_node_name.has_value()) { + this->nodes.lookup_or_add_default(*group_node_name).warnings.extend(child_log.all_warnings); + } + this->all_warnings.extend(child_log.all_warnings); + } + reduced_node_warnings_ = true; +} + +void GeoTreeLog::ensure_node_run_time() +{ + if (reduced_node_run_times_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::NodeExecutionTime &timings : tree_logger->node_execution_times) { + const std::chrono::nanoseconds duration = timings.end - timings.start; + this->nodes.lookup_or_add_default_as(timings.node_name).run_time += duration; + this->run_time_sum += duration; + } + } + for (const ComputeContextHash &child_hash : children_hashes_) { + GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash); + child_log.ensure_node_run_time(); + const std::optional<std::string> &group_node_name = + child_log.tree_loggers_[0]->group_node_name; + if (group_node_name.has_value()) { + this->nodes.lookup_or_add_default(*group_node_name).run_time += child_log.run_time_sum; + } + this->run_time_sum += child_log.run_time_sum; + } + reduced_node_run_times_ = true; +} + +void GeoTreeLog::ensure_socket_values() +{ + if (reduced_socket_values_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->input_socket_values) { + this->nodes.lookup_or_add_as(value_log_data.node_name) + .input_values_.add(value_log_data.socket_identifier, value_log_data.value.get()); + } + for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->output_socket_values) { + this->nodes.lookup_or_add_as(value_log_data.node_name) + .output_values_.add(value_log_data.socket_identifier, value_log_data.value.get()); + } + } + reduced_socket_values_ = true; +} + +void GeoTreeLog::ensure_viewer_node_logs() +{ + if (reduced_viewer_node_logs_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::ViewerNodeLogWithNode &viewer_log : tree_logger->viewer_node_logs) { + this->viewer_node_logs.add(viewer_log.node_name, viewer_log.viewer_log.get()); + } + } + reduced_viewer_node_logs_ = true; +} + +void GeoTreeLog::ensure_existing_attributes() +{ + if (reduced_existing_attributes_) { + return; + } + this->ensure_socket_values(); + + Set<StringRef> names; + + auto handle_value_log = [&](const ValueLog &value_log) { + const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(&value_log); + if (geo_log == nullptr) { + return; + } + for (const GeometryAttributeInfo &attribute : geo_log->attributes) { + if (names.add(attribute.name)) { + this->existing_attributes.append(&attribute); + } + } + }; + + for (const GeoNodeLog &node_log : this->nodes.values()) { + for (const ValueLog *value_log : node_log.input_values_.values()) { + handle_value_log(*value_log); + } + for (const ValueLog *value_log : node_log.output_values_.values()) { + handle_value_log(*value_log); + } + } + reduced_existing_attributes_ = true; +} + +void GeoTreeLog::ensure_used_named_attributes() +{ + if (reduced_used_named_attributes_) { + return; + } + + auto add_attribute = [&](const StringRef node_name, + const StringRef attribute_name, + const NamedAttributeUsage &usage) { + this->nodes.lookup_or_add_as(node_name).used_named_attributes.lookup_or_add_as(attribute_name, + usage) |= usage; + this->used_named_attributes.lookup_or_add_as(attribute_name, usage) |= usage; + }; + + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::AttributeUsageWithNode &item : tree_logger->used_named_attributes) { + add_attribute(item.node_name, item.attribute_name, item.usage); + } + } + for (const ComputeContextHash &child_hash : children_hashes_) { + GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash); + child_log.ensure_used_named_attributes(); + if (const std::optional<std::string> &group_node_name = + child_log.tree_loggers_[0]->group_node_name) { + for (const auto &item : child_log.used_named_attributes.items()) { + add_attribute(*group_node_name, item.key, item.value); + } + } + } + reduced_used_named_attributes_ = true; +} + +void GeoTreeLog::ensure_debug_messages() +{ + if (reduced_debug_messages_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::DebugMessage &debug_message : tree_logger->debug_messages) { + this->nodes.lookup_or_add_as(debug_message.node_name) + .debug_messages.append(debug_message.message); + } + } + reduced_debug_messages_ = true; +} + +ValueLog *GeoTreeLog::find_socket_value_log(const bNodeSocket &query_socket) +{ + /** + * Geometry nodes does not log values for every socket. That would produce a lot of redundant + * data,because often many linked sockets have the same value. To find the logged value for a + * socket one might have to look at linked sockets as well. + */ + + BLI_assert(reduced_socket_values_); + if (query_socket.is_multi_input()) { + /* Not supported currently. */ + return nullptr; + } + + Set<const bNodeSocket *> added_sockets; + Stack<const bNodeSocket *> sockets_to_check; + sockets_to_check.push(&query_socket); + added_sockets.add(&query_socket); + + while (!sockets_to_check.is_empty()) { + const bNodeSocket &socket = *sockets_to_check.pop(); + const bNode &node = socket.owner_node(); + if (GeoNodeLog *node_log = this->nodes.lookup_ptr(node.name)) { + ValueLog *value_log = socket.is_input() ? + node_log->input_values_.lookup_default(socket.identifier, + nullptr) : + node_log->output_values_.lookup_default(socket.identifier, + nullptr); + if (value_log != nullptr) { + return value_log; + } + } + + if (socket.is_input()) { + const Span<const bNodeLink *> links = socket.directly_linked_links(); + for (const bNodeLink *link : links) { + const bNodeSocket &from_socket = *link->fromsock; + if (added_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + else { + if (node.is_reroute()) { + const bNodeSocket &input_socket = node.input_socket(0); + if (added_sockets.add(&input_socket)) { + sockets_to_check.push(&input_socket); + } + const Span<const bNodeLink *> links = input_socket.directly_linked_links(); + for (const bNodeLink *link : links) { + const bNodeSocket &from_socket = *link->fromsock; + if (added_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + else if (node.is_muted()) { + if (const bNodeSocket *input_socket = socket.internal_link_input()) { + if (added_sockets.add(input_socket)) { + sockets_to_check.push(input_socket); + } + const Span<const bNodeLink *> links = input_socket->directly_linked_links(); + for (const bNodeLink *link : links) { + const bNodeSocket &from_socket = *link->fromsock; + if (added_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + } + } + } + + return nullptr; +} + +GeoTreeLogger &GeoModifierLog::get_local_tree_logger(const ComputeContext &compute_context) +{ + LocalData &local_data = data_per_thread_.local(); + Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> &local_tree_loggers = + local_data.tree_logger_by_context; + destruct_ptr<GeoTreeLogger> &tree_logger_ptr = local_tree_loggers.lookup_or_add_default( + compute_context.hash()); + if (tree_logger_ptr) { + return *tree_logger_ptr; + } + tree_logger_ptr = local_data.allocator.construct<GeoTreeLogger>(); + GeoTreeLogger &tree_logger = *tree_logger_ptr; + tree_logger.allocator = &local_data.allocator; + const ComputeContext *parent_compute_context = compute_context.parent(); + if (parent_compute_context != nullptr) { + tree_logger.parent_hash = parent_compute_context->hash(); + GeoTreeLogger &parent_logger = this->get_local_tree_logger(*parent_compute_context); + parent_logger.children_hashes.append(compute_context.hash()); + } + if (const bke::NodeGroupComputeContext *node_group_compute_context = + dynamic_cast<const bke::NodeGroupComputeContext *>(&compute_context)) { + tree_logger.group_node_name.emplace(node_group_compute_context->node_name()); + } + return tree_logger; +} + +GeoTreeLog &GeoModifierLog::get_tree_log(const ComputeContextHash &compute_context_hash) +{ + GeoTreeLog &reduced_tree_log = *tree_logs_.lookup_or_add_cb(compute_context_hash, [&]() { + Vector<GeoTreeLogger *> tree_logs; + for (LocalData &local_data : data_per_thread_) { + destruct_ptr<GeoTreeLogger> *tree_log = local_data.tree_logger_by_context.lookup_ptr( + compute_context_hash); + if (tree_log != nullptr) { + tree_logs.append(tree_log->get()); + } + } + return std::make_unique<GeoTreeLog>(this, std::move(tree_logs)); + }); + return reduced_tree_log; +} + +struct ObjectAndModifier { + const Object *object; + const NodesModifierData *nmd; +}; + +static std::optional<ObjectAndModifier> get_modifier_for_node_editor(const SpaceNode &snode) +{ + if (snode.id == nullptr) { + return std::nullopt; + } + if (GS(snode.id->name) != ID_OB) { + return std::nullopt; + } + const Object *object = reinterpret_cast<Object *>(snode.id); + const NodesModifierData *used_modifier = nullptr; + if (snode.flag & SNODE_PIN) { + LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + /* Would be good to store the name of the pinned modifier in the node editor. */ + if (nmd->node_group == snode.nodetree) { + used_modifier = nmd; + break; + } + } + } + } + else { + LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + if (nmd->node_group == snode.nodetree) { + if (md->flag & eModifierFlag_Active) { + used_modifier = nmd; + break; + } + } + } + } + } + if (used_modifier == nullptr) { + return std::nullopt; + } + return ObjectAndModifier{object, used_modifier}; +} + +GeoTreeLog *GeoModifierLog::get_tree_log_for_node_editor(const SpaceNode &snode) +{ + std::optional<ObjectAndModifier> object_and_modifier = get_modifier_for_node_editor(snode); + if (!object_and_modifier) { + return nullptr; + } + GeoModifierLog *modifier_log = static_cast<GeoModifierLog *>( + object_and_modifier->nmd->runtime_eval_log); + if (modifier_log == nullptr) { + return nullptr; + } + Vector<const bNodeTreePath *> tree_path = snode.treepath; + if (tree_path.is_empty()) { + return nullptr; + } + ComputeContextBuilder compute_context_builder; + compute_context_builder.push<bke::ModifierComputeContext>( + object_and_modifier->nmd->modifier.name); + for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(1)) { + compute_context_builder.push<bke::NodeGroupComputeContext>(path_item->node_name); + } + return &modifier_log->get_tree_log(compute_context_builder.hash()); +} + +const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_spreadsheet( + const SpaceSpreadsheet &sspreadsheet) +{ + Vector<const SpreadsheetContext *> context_path = sspreadsheet.context_path; + if (context_path.size() < 3) { + return nullptr; + } + if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { + return nullptr; + } + if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { + return nullptr; + } + const SpreadsheetContextObject *object_context = + reinterpret_cast<const SpreadsheetContextObject *>(context_path[0]); + const SpreadsheetContextModifier *modifier_context = + reinterpret_cast<const SpreadsheetContextModifier *>(context_path[1]); + if (object_context->object == nullptr) { + return nullptr; + } + NodesModifierData *nmd = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &object_context->object->modifiers) { + if (STREQ(md->name, modifier_context->modifier_name)) { + if (md->type == eModifierType_Nodes) { + nmd = reinterpret_cast<NodesModifierData *>(md); + } + } + } + if (nmd == nullptr) { + return nullptr; + } + if (nmd->runtime_eval_log == nullptr) { + return nullptr; + } + nodes::geo_eval_log::GeoModifierLog *modifier_log = + static_cast<nodes::geo_eval_log::GeoModifierLog *>(nmd->runtime_eval_log); + + ComputeContextBuilder compute_context_builder; + compute_context_builder.push<bke::ModifierComputeContext>(modifier_context->modifier_name); + for (const SpreadsheetContext *context : context_path.as_span().drop_front(2).drop_back(1)) { + if (context->type != SPREADSHEET_CONTEXT_NODE) { + return nullptr; + } + const SpreadsheetContextNode &node_context = *reinterpret_cast<const SpreadsheetContextNode *>( + context); + compute_context_builder.push<bke::NodeGroupComputeContext>(node_context.node_name); + } + const ComputeContextHash context_hash = compute_context_builder.hash(); + nodes::geo_eval_log::GeoTreeLog &tree_log = modifier_log->get_tree_log(context_hash); + tree_log.ensure_viewer_node_logs(); + + const SpreadsheetContext *last_context = context_path.last(); + if (last_context->type != SPREADSHEET_CONTEXT_NODE) { + return nullptr; + } + const SpreadsheetContextNode &last_node_context = + *reinterpret_cast<const SpreadsheetContextNode *>(last_context); + const ViewerNodeLog *viewer_log = tree_log.viewer_node_logs.lookup_default( + last_node_context.node_name, nullptr); + return viewer_log; +} + +} // namespace blender::nodes::geo_eval_log diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 953dce035c2..1833774fe33 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -11,34 +11,27 @@ #include "node_geometry_util.hh" -using blender::nodes::geometry_nodes_eval_log::LocalGeoLogger; - namespace blender::nodes { void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const { - if (provider_->logger == nullptr) { - return; + if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) { + tree_logger->node_warnings.append({node_.name, {type, std::move(message)}}); } - LocalGeoLogger &local_logger = provider_->logger->local(); - local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } void GeoNodeExecParams::used_named_attribute(std::string attribute_name, - const eNamedAttrUsage usage) + const NamedAttributeUsage usage) { - if (provider_->logger == nullptr) { - return; + if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) { + tree_logger->used_named_attributes.append({node_.name, std::move(attribute_name), usage}); } - LocalGeoLogger &local_logger = provider_->logger->local(); - local_logger.log_used_named_attribute(provider_->dnode, std::move(attribute_name), usage); } void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const { - const SocketDeclaration &decl = - *provider_->dnode->input_by_identifier(identifier).runtime->declaration; + const SocketDeclaration &decl = *node_.input_by_identifier(identifier).runtime->declaration; const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl); if (geo_decl == nullptr) { return; @@ -118,7 +111,7 @@ void GeoNodeExecParams::check_output_geometry_set(const GeometrySet &geometry_se const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { - for (const bNodeSocket *socket : provider_->dnode->runtime->inputs) { + for (const bNodeSocket *socket : node_.input_sockets()) { if (socket->is_available() && socket->name == name) { return socket; } @@ -129,19 +122,19 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name std::string GeoNodeExecParams::attribute_producer_name() const { - return provider_->dnode->label_or_name() + TIP_(" node"); + return node_.label_or_name() + TIP_(" node"); } void GeoNodeExecParams::set_default_remaining_outputs() { - provider_->set_default_remaining_outputs(); + params_.set_default_remaining_outputs(); } void GeoNodeExecParams::check_input_access(StringRef identifier, const CPPType *requested_type) const { const bNodeSocket *found_socket = nullptr; - for (const bNodeSocket *socket : provider_->dnode->input_sockets()) { + for (const bNodeSocket *socket : node_.input_sockets()) { if (socket->identifier == identifier) { found_socket = socket; break; @@ -151,7 +144,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, if (found_socket == nullptr) { std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n"; std::cout << "Possible identifiers are: "; - for (const bNodeSocket *socket : provider_->dnode->input_sockets()) { + for (const bNodeSocket *socket : node_.input_sockets()) { if (socket->is_available()) { std::cout << "'" << socket->identifier << "', "; } @@ -164,13 +157,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, << "' is disabled.\n"; BLI_assert_unreachable(); } - else if (!provider_->can_get_input(identifier)) { - std::cout << "The identifier '" << identifier - << "' is valid, but there is no value for it anymore.\n"; - std::cout << "Most likely it has been extracted before.\n"; - BLI_assert_unreachable(); - } - else if (requested_type != nullptr) { + else if (requested_type != nullptr && (found_socket->flag & SOCK_MULTI_INPUT) == 0) { const CPPType &expected_type = *found_socket->typeinfo->geometry_nodes_cpp_type; if (*requested_type != expected_type) { std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '" @@ -183,7 +170,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const { const bNodeSocket *found_socket = nullptr; - for (const bNodeSocket *socket : provider_->dnode->output_sockets()) { + for (const bNodeSocket *socket : node_.output_sockets()) { if (socket->identifier == identifier) { found_socket = socket; break; @@ -193,8 +180,8 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType if (found_socket == nullptr) { std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n"; std::cout << "Possible identifiers are: "; - for (const bNodeSocket *socket : provider_->dnode->output_sockets()) { - if (!(socket->flag & SOCK_UNAVAIL)) { + for (const bNodeSocket *socket : node_.output_sockets()) { + if (socket->is_available()) { std::cout << "'" << socket->identifier << "', "; } } @@ -206,7 +193,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType << "' is disabled.\n"; BLI_assert_unreachable(); } - else if (!provider_->can_set_output(identifier)) { + else if (params_.output_was_set(this->get_output_index(identifier))) { std::cout << "The identifier '" << identifier << "' has been set already.\n"; BLI_assert_unreachable(); } diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc index 1f8397923e9..d731fe8f877 100644 --- a/source/blender/nodes/intern/node_multi_function.cc +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -3,21 +3,21 @@ #include "NOD_multi_function.hh" #include "BKE_node.h" +#include "BKE_node_runtime.hh" namespace blender::nodes { -NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree) +NodeMultiFunctions::NodeMultiFunctions(const bNodeTree &tree) { - for (const bNodeTree *btree : tree.used_btrees()) { - for (const bNode *bnode : btree->all_nodes()) { - if (bnode->typeinfo->build_multi_function == nullptr) { - continue; - } - NodeMultiFunctionBuilder builder{*bnode, *btree}; - bnode->typeinfo->build_multi_function(builder); - if (builder.built_fn_ != nullptr) { - map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)}); - } + tree.ensure_topology_cache(); + for (const bNode *bnode : tree.all_nodes()) { + if (bnode->typeinfo->build_multi_function == nullptr) { + continue; + } + NodeMultiFunctionBuilder builder{*bnode, tree}; + bnode->typeinfo->build_multi_function(builder); + if (builder.built_fn_ != nullptr) { + map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)}); } } } diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c index 3ddab4bebd9..2e6d1698da9 100644 --- a/source/blender/python/bmesh/bmesh_py_api.c +++ b/source/blender/python/bmesh/bmesh_py_api.c @@ -160,7 +160,7 @@ static struct PyModuleDef BPy_BM_module_def = { BPy_BM_doc, /* m_doc */ 0, /* m_size */ BPy_BM_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/bmesh/bmesh_py_geometry.c b/source/blender/python/bmesh/bmesh_py_geometry.c index 5625812fbda..f2af8599807 100644 --- a/source/blender/python/bmesh/bmesh_py_geometry.c +++ b/source/blender/python/bmesh/bmesh_py_geometry.c @@ -66,7 +66,7 @@ static struct PyModuleDef BPy_BM_geometry_module_def = { BPy_BM_utils_doc, /* m_doc */ 0, /* m_size */ BPy_BM_geometry_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/bmesh/bmesh_py_ops.c b/source/blender/python/bmesh/bmesh_py_ops.c index 8b343b00342..37e2b009f55 100644 --- a/source/blender/python/bmesh/bmesh_py_ops.c +++ b/source/blender/python/bmesh/bmesh_py_ops.c @@ -267,7 +267,7 @@ static struct PyModuleDef BPy_BM_ops_module_def = { BPy_BM_ops_doc, /* m_doc */ 0, /* m_size */ BPy_BM_ops_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 972a782d650..d592a583817 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -3740,7 +3740,7 @@ static struct PyModuleDef BPy_BM_types_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c index 1bee1342a07..6630eb4924e 100644 --- a/source/blender/python/bmesh/bmesh_py_utils.c +++ b/source/blender/python/bmesh/bmesh_py_utils.c @@ -822,7 +822,7 @@ static struct PyModuleDef BPy_BM_utils_module_def = { BPy_BM_utils_doc, /* m_doc */ 0, /* m_size */ BPy_BM_utils_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index 197af75e5d7..36ab1e86d92 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -1397,7 +1397,7 @@ static struct PyModuleDef BGL_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c index 0f0a2bf0ed1..19958a99df9 100644 --- a/source/blender/python/generic/bl_math_py_api.c +++ b/source/blender/python/generic/bl_math_py_api.c @@ -133,7 +133,7 @@ static struct PyModuleDef M_bl_math_module_def = { M_bl_math_doc, /* m_doc */ 0, /* m_size */ M_bl_math_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 060b7758ea9..11b71256327 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -465,7 +465,7 @@ static struct PyModuleDef BLF_module_def = { BLF_doc, /* m_doc */ 0, /* m_size */ BLF_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 3f880da2f56..333ab9487d1 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -2119,7 +2119,7 @@ static struct PyModuleDef IDProp_types_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -2168,7 +2168,7 @@ static struct PyModuleDef IDProp_module_def = { IDProp_module_doc, /* m_doc */ 0, /* m_size */ IDProp_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c index e6d90c46866..056af83cc49 100644 --- a/source/blender/python/generic/imbuf_py_api.c +++ b/source/blender/python/generic/imbuf_py_api.c @@ -570,7 +570,7 @@ static struct PyModuleDef IMB_module_def = { IMB_doc, /* m_doc */ 0, /* m_size */ IMB_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -614,7 +614,7 @@ static struct PyModuleDef IMB_types_module_def = { IMB_types_doc, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 02fccedd820..fbc45124147 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -37,6 +37,9 @@ "``IMAGE``\n" \ " :Attributes: vec3 pos, vec2 texCoord\n" \ " :Uniforms: sampler2D image\n" \ + "``IMAGE_COLOR``\n" \ + " :Attributes: vec3 pos, vec2 texCoord\n" \ + " :Uniforms: sampler2D image, vec4 color\n" \ "``SMOOTH_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ @@ -56,6 +59,7 @@ static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { {GPU_SHADER_3D_FLAT_COLOR, "FLAT_COLOR"}, {GPU_SHADER_3D_IMAGE, "IMAGE"}, + {GPU_SHADER_3D_IMAGE_COLOR, "IMAGE_COLOR"}, {GPU_SHADER_3D_SMOOTH_COLOR, "SMOOTH_COLOR"}, {GPU_SHADER_3D_UNIFORM_COLOR, "UNIFORM_COLOR"}, {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "POLYLINE_FLAT_COLOR"}, diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 939473ceaa0..a0129157b95 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -79,8 +79,6 @@ static PyStructSequence_Field app_info_fields[] = { {"version_string", "The Blender version formatted as a string"}, {"version_cycle", "The release status of this build alpha/beta/rc/release"}, {"version_char", "Deprecated, always an empty string"}, - {"binary_path", - "The location of Blender's executable, useful for utilities that open new instances"}, {"background", "Boolean, True when blender is running without a user interface (started with -b)"}, {"factory_startup", "Boolean, True when blender is running with --factory-startup)"}, @@ -151,7 +149,6 @@ static PyObject *make_app_info(void) SetStrItem(STRINGIFY(BLENDER_VERSION_CYCLE)); SetStrItem(""); - SetStrItem(BKE_appdir_program_path()); SetObjItem(PyBool_FromLong(G.background)); SetObjItem(PyBool_FromLong(G.factory_startup)); @@ -345,6 +342,33 @@ static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void return PyC_UnicodeFromByte(G.autoexec_fail); } +PyDoc_STRVAR(bpy_app_binary_path_doc, + "The location of Blender's executable, useful for utilities that open new instances. " + "Read-only unless Blender is built as a Python module - in this case the value is " + "an empty string which script authors may point to a Blender binary."); +static PyObject *bpy_app_binary_path_get(PyObject *UNUSED(self), void *UNUSED(closure)) +{ + return PyC_UnicodeFromByte(BKE_appdir_program_path()); +} + +static int bpy_app_binary_path_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure)) +{ +#ifndef WITH_PYTHON_MODULE + PyErr_SetString(PyExc_AttributeError, + "bpy.app.binary_path is only writable when built as a Python module"); + return -1; +#endif + PyObject *value_coerce = NULL; + const char *filepath = PyC_UnicodeAsByte(value, &value_coerce); + if (filepath == NULL) { + PyErr_Format(PyExc_ValueError, "expected a string or bytes, got %s", Py_TYPE(value)->tp_name); + return -1; + } + BKE_appdir_program_path_init(filepath); + Py_XDECREF(value_coerce); + return 0; +} + static PyGetSetDef bpy_app_getsets[] = { {"debug", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG}, {"debug_ffmpeg", @@ -450,7 +474,14 @@ static PyGetSetDef bpy_app_getsets[] = { (void *)G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET}, {"autoexec_fail_message", bpy_app_autoexec_fail_message_get, NULL, NULL, NULL}, - /* End-of-list marker. */ + /* Support script authors setting the Blender binary path to use, otherwise this value + * is not known when built as a Python module. */ + {"binary_path", + bpy_app_binary_path_get, + bpy_app_binary_path_set, + bpy_app_binary_path_doc, + NULL}, + {NULL, NULL, NULL, NULL, NULL}, }; diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c index 3f884338bbb..918d96d9f44 100644 --- a/source/blender/python/intern/bpy_app_icons.c +++ b/source/blender/python/intern/bpy_app_icons.c @@ -166,7 +166,7 @@ static struct PyModuleDef M_AppIcons_module_def = { NULL, /* m_doc */ 0, /* m_size */ M_AppIcons_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/intern/bpy_app_timers.c b/source/blender/python/intern/bpy_app_timers.c index 5a42ecfdbc8..4adc200357b 100644 --- a/source/blender/python/intern/bpy_app_timers.c +++ b/source/blender/python/intern/bpy_app_timers.c @@ -168,7 +168,7 @@ static struct PyModuleDef M_AppTimers_module_def = { NULL, /* m_doc */ 0, /* m_size */ M_AppTimers_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 23fc0bcaeda..3a095f4b9f3 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -11,6 +11,10 @@ #include <Python.h> #include <frameobject.h> +#ifdef WITH_PYTHON_MODULE +# include "pylifecycle.h" /* For `Py_Version`. */ +#endif + #include "MEM_guardedalloc.h" #include "CLG_log.h" @@ -768,7 +772,7 @@ static struct PyModuleDef bpy_proxy_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ bpy_module_free, /* m_free */ @@ -807,6 +811,50 @@ static void bpy_module_delay_init(PyObject *bpy_proxy) PyDict_Update(PyModule_GetDict(bpy_proxy), PyModule_GetDict(bpy_package_py)); } +/** + * Raise an error and return false if the Python version used to compile Blender + * isn't compatible with the interpreter loading the `bpy` module. + */ +static bool bpy_module_ensure_compatible_version(void) +{ + /* First check the Python version used matches the major version that Blender was built with. + * While this isn't essential, the error message in this case may be cryptic and misleading. + * NOTE: using `Py_LIMITED_API` would remove the need for this, in practice it's + * unlikely Blender will ever used the limited API though. */ +# if PY_VERSION_HEX >= 0x030b0000 /* Python 3.11 & newer. */ + const uint version_runtime = Py_Version; +# else + uint version_runtime; + { + uint version_runtime_major = 0, version_runtime_minor = 0; + const char *version_str = Py_GetVersion(); + if (sscanf(version_str, "%u.%u.", &version_runtime_major, &version_runtime_minor) != 2) { + /* Should never happen, raise an error to ensure this check never fails silently. */ + PyErr_Format(PyExc_ImportError, "Failed to extract the version from \"%s\"", version_str); + return false; + } + version_runtime = (version_runtime_major << 24) | (version_runtime_minor << 16); + } +# endif + + uint version_compile_major = PY_VERSION_HEX >> 24; + uint version_compile_minor = ((PY_VERSION_HEX & 0x00ff0000) >> 16); + uint version_runtime_major = version_runtime >> 24; + uint version_runtime_minor = ((version_runtime & 0x00ff0000) >> 16); + if ((version_compile_major != version_runtime_major) || + (version_compile_minor != version_runtime_minor)) { + PyErr_Format(PyExc_ImportError, + "The version of \"bpy\" was compiled with: " + "(%u.%u) is incompatible with: (%u.%u) used by the interpreter!", + version_compile_major, + version_compile_minor, + version_runtime_major, + version_runtime_minor); + return false; + } + return true; +} + static void dealloc_obj_dealloc(PyObject *self); static PyTypeObject dealloc_obj_Type; @@ -824,6 +872,10 @@ PyMODINIT_FUNC PyInit_bpy(void); PyMODINIT_FUNC PyInit_bpy(void) { + if (!bpy_module_ensure_compatible_version()) { + return NULL; /* The error has been set. */ + } + PyObject *bpy_proxy = PyModule_Create(&bpy_proxy_def); /* Problem: diff --git a/source/blender/python/intern/bpy_path.c b/source/blender/python/intern/bpy_path.c index fbb47817389..f3a1a7cb1df 100644 --- a/source/blender/python/intern/bpy_path.c +++ b/source/blender/python/intern/bpy_path.c @@ -26,7 +26,7 @@ static struct PyModuleDef _bpy_path_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 56de0bfc18e..941e0adfc1d 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -7868,7 +7868,7 @@ static struct PyModuleDef bpy_types_module_def = { bpy_types_module_doc, /* m_doc */ sizeof(struct BPy_TypesModule_State), /* m_size */ bpy_types_module_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 1aa2cec861c..dcb6d7f3bc6 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -724,7 +724,7 @@ static struct PyModuleDef M_Mathutils_module_def = { M_Mathutils_doc, /* m_doc */ 0, /* m_size */ M_Mathutils_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index b923a9dfb14..4bdb1adcdde 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -1294,7 +1294,7 @@ static struct PyModuleDef bvhtree_moduledef = { py_bvhtree_doc, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 1e492574903..28deebcf5ac 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -1790,7 +1790,7 @@ static struct PyModuleDef M_Geometry_module_def = { M_Geometry_doc, /* m_doc */ 0, /* m_size */ M_Geometry_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c index 6523b3f7259..10f42d9b070 100644 --- a/source/blender/python/mathutils/mathutils_interpolate.c +++ b/source/blender/python/mathutils/mathutils_interpolate.c @@ -93,7 +93,7 @@ static struct PyModuleDef M_Interpolate_module_def = { M_Interpolate_doc, /* m_doc */ 0, /* m_size */ M_Interpolate_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c index 66c48697b6b..f5a27c6f90f 100644 --- a/source/blender/python/mathutils/mathutils_kdtree.c +++ b/source/blender/python/mathutils/mathutils_kdtree.c @@ -428,7 +428,7 @@ static struct PyModuleDef kdtree_moduledef = { py_kdtree_doc, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c index e1282e90c48..3a3297f27f7 100644 --- a/source/blender/python/mathutils/mathutils_noise.c +++ b/source/blender/python/mathutils/mathutils_noise.c @@ -1089,7 +1089,7 @@ static struct PyModuleDef M_Noise_module_def = { M_Noise_doc, /* m_doc */ 0, /* m_size */ M_Noise_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/render/intern/engine.cc b/source/blender/render/intern/engine.cc index a440b34af78..0024ebe38f7 100644 --- a/source/blender/render/intern/engine.cc +++ b/source/blender/render/intern/engine.cc @@ -1276,8 +1276,6 @@ void RE_engine_gpu_context_destroy(RenderEngine *engine) return; } - BLI_assert(BLI_thread_is_main()); - const bool drw_state = DRW_opengl_context_release(); WM_opengl_context_activate(engine->gpu_context); diff --git a/source/blender/render/intern/initrender.cc b/source/blender/render/intern/initrender.cc index cc05aa8621e..1ea93cbf6c8 100644 --- a/source/blender/render/intern/initrender.cc +++ b/source/blender/render/intern/initrender.cc @@ -124,7 +124,8 @@ float RE_filter_value(int type, float x) } return 1.0f - x; - case R_FILTER_GAUSS: { + case R_FILTER_GAUSS: + case R_FILTER_FAST_GAUSS: { const float two_gaussfac2 = 2.0f * gaussfac * gaussfac; x *= 3.0f * gaussfac; return 1.0f / sqrtf((float)M_PI * two_gaussfac2) * expf(-x * x / two_gaussfac2); diff --git a/source/blender/render/intern/pipeline.cc b/source/blender/render/intern/pipeline.cc index e96103fcdae..4b52fb62bee 100644 --- a/source/blender/render/intern/pipeline.cc +++ b/source/blender/render/intern/pipeline.cc @@ -1439,7 +1439,7 @@ static bool check_valid_compositing_camera(Scene *scene, Object *camera_override if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) { Scene *sce = node->id ? (Scene *)node->id : scene; if (sce->camera == nullptr) { - sce->camera = BKE_view_layer_camera_find(scene, BKE_view_layer_default_render(sce)); + sce->camera = BKE_view_layer_camera_find(sce, BKE_view_layer_default_render(sce)); } if (sce->camera == nullptr) { /* all render layers nodes need camera */ diff --git a/source/blender/render/intern/render_result.cc b/source/blender/render/intern/render_result.cc index 86ee9ad779a..8b7a07e2b3f 100644 --- a/source/blender/render/intern/render_result.cc +++ b/source/blender/render/intern/render_result.cc @@ -929,7 +929,11 @@ int render_result_exr_file_read_path(RenderResult *rr, return 1; } -static void render_result_exr_file_cache_path(Scene *sce, const char *root, char *r_path) +#define FILE_CACHE_MAX (FILE_MAXFILE + FILE_MAXFILE + MAX_ID_NAME + 100) + +static void render_result_exr_file_cache_path(Scene *sce, + const char *root, + char r_path[FILE_CACHE_MAX]) { char filename_full[FILE_MAX + MAX_ID_NAME + 100], filename[FILE_MAXFILE], dirname[FILE_MAXDIR]; char path_digest[16] = {0}; @@ -948,7 +952,7 @@ static void render_result_exr_file_cache_path(Scene *sce, const char *root, char } BLI_hash_md5_to_hexdigest(path_digest, path_hexdigest); - /* Default to *non-volatile* tmp dir. */ + /* Default to *non-volatile* temp dir. */ if (*root == '\0') { root = BKE_tempdir_base(); } @@ -959,13 +963,17 @@ static void render_result_exr_file_cache_path(Scene *sce, const char *root, char filename, sce->id.name + 2, path_hexdigest); - BLI_make_file_string(dirname, r_path, root, filename_full); + + BLI_join_dirfile(r_path, FILE_CACHE_MAX, root, filename_full); + if (BLI_path_is_rel(r_path)) { + BLI_path_abs(r_path, dirname); + } } void render_result_exr_file_cache_write(Render *re) { RenderResult *rr = re->result; - char str[FILE_MAXFILE + FILE_MAXFILE + MAX_ID_NAME + 100]; + char str[FILE_CACHE_MAX]; char *root = U.render_cachedir; render_result_passes_allocated_ensure(rr); @@ -979,7 +987,7 @@ void render_result_exr_file_cache_write(Render *re) bool render_result_exr_file_cache_read(Render *re) { /* File path to cache. */ - char filepath[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; + char filepath[FILE_CACHE_MAX] = ""; char *root = U.render_cachedir; render_result_exr_file_cache_path(re->scene, root, filepath); diff --git a/source/blender/sequencer/intern/modifier.c b/source/blender/sequencer/intern/modifier.c index b0f2f53396b..b17db8f762e 100644 --- a/source/blender/sequencer/intern/modifier.c +++ b/source/blender/sequencer/intern/modifier.c @@ -598,7 +598,7 @@ static void modifier_color_balance_apply( ColorBalanceInitData init_data; if (!ibuf->rect_float && make_float) { - imb_addrectfloatImBuf(ibuf); + imb_addrectfloatImBuf(ibuf, 4); } init_data.cb = cb; diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index 374e18dd36a..4220efab8bf 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -177,14 +177,12 @@ static bool seq_proxy_get_fname(Scene *scene, BLI_snprintf(name, PROXY_MAXFILE, - "%s/images/%d/%s_proxy%s", + "%s/images/%d/%s_proxy%s.jpg", dir, proxy_size_number, SEQ_render_give_stripelem(scene, seq, timeline_frame)->name, suffix); BLI_path_abs(name, BKE_main_blendfile_path_from_global()); - strcat(name, ".jpg"); - return true; } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index b7dc0e7035d..fd3b6103b94 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -134,7 +134,7 @@ void seq_imbuf_to_sequencer_space(Scene *scene, ImBuf *ibuf, bool make_float) /* We perform conversion to a float buffer so we don't worry about * precision loss. */ - imb_addrectfloatImBuf(ibuf); + imb_addrectfloatImBuf(ibuf, 4); IMB_colormanagement_transform_from_byte_threaded(ibuf->rect_float, (unsigned char *)ibuf->rect, ibuf->x, diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 0393be93bb5..775b62e7d39 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -861,7 +861,7 @@ void WM_operator_properties_select_action(struct wmOperatorType *ot, int default_action, bool hide_gui); /** - * Only #SELECT / #DESELECT. + * Only for select/de-select. */ void WM_operator_properties_select_action_simple(struct wmOperatorType *ot, int default_action, diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index a0200373ac6..bd480526f9f 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -275,7 +275,7 @@ IDTypeInfo IDType_ID_WM = { .foreach_id = window_manager_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - .owner_get = NULL, + .owner_pointer_get = NULL, .blend_write = window_manager_blend_write, .blend_read_data = window_manager_blend_read_data, diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 5b42335fe63..5f7a6078328 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -5929,7 +5929,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) bToolRef *tref = nullptr; if ((region->regiontype == RGN_TYPE_WINDOW) && ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK)) { - const Scene*scene = WM_window_get_active_scene(win); + const Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); WorkSpace *workspace = WM_window_get_active_workspace(win); bToolKey tkey{}; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 13c1579d24b..0e43ed5509a 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1397,29 +1397,27 @@ void wm_homefile_read_post(struct bContext *C, void wm_history_file_read(void) { - char name[FILE_MAX]; - LinkNode *l, *lines; - struct RecentFile *recent; - const char *line; - int num; const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); - if (!cfgdir) { return; } + char name[FILE_MAX]; + LinkNode *l; + int num; + BLI_join_dirfile(name, sizeof(name), cfgdir, BLENDER_HISTORY_FILE); - lines = BLI_file_read_as_lines(name); + LinkNode *lines = BLI_file_read_as_lines(name); wm_history_files_free(); /* read list of recent opened files from recent-files.txt to memory */ for (l = lines, num = 0; l && (num < U.recent_files); l = l->next) { - line = l->link; + const char *line = l->link; /* don't check if files exist, causes slow startup for remote/external drives */ if (line[0]) { - recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile"); + struct RecentFile *recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile"); BLI_addtail(&(G.recent_files), recent); recent->filepath = BLI_strdup(line); num++; @@ -1899,7 +1897,7 @@ static bool wm_file_write(bContext *C, /** \name Auto-Save API * \{ */ -static void wm_autosave_location(char *filepath) +static void wm_autosave_location(char filepath[FILE_MAX]) { const int pid = abs(getpid()); char path[1024]; @@ -1918,23 +1916,21 @@ static void wm_autosave_location(char *filepath) BLI_snprintf(path, sizeof(path), "%d_autosave.blend", pid); } + const char *tempdir_base = BKE_tempdir_base(); + /* NOTE(@campbellbarton): It's strange that this is only used on WIN32. + * From reading commits it seems accessing the temporary directory used to be less reliable. + * If this is still the case on WIN32 - other features such as copy-paste will also fail. + * We could support #BLENDER_USER_AUTOSAVE on all platforms or remove it entirely. */ #ifdef WIN32 - /* XXX Need to investigate how to handle default location of '/tmp/' - * This is a relative directory on Windows, and it may be - * found. Example: - * Blender installed on D:\ drive, D:\ drive has D:\tmp\ - * Now, BLI_exists() will find '/tmp/' exists, but - * BLI_make_file_string will create string that has it most likely on C:\ - * through BLI_windows_get_default_root_dir(). - * If there is no C:\tmp autosave fails. */ - if (!BLI_exists(BKE_tempdir_base())) { + if (!BLI_exists(tempdir_base)) { const char *savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL); - BLI_make_file_string("/", filepath, savedir, path); - return; + if (savedir) { + tempdir_base = savedir; + } } #endif - BLI_join_dirfile(filepath, FILE_MAX, BKE_tempdir_base(), path); + BLI_join_dirfile(filepath, FILE_MAX, tempdir_base, path); } static void wm_autosave_write(Main *bmain, wmWindowManager *wm) diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 8163b39b3dd..7ab2e67e4b6 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -634,13 +634,16 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_sound_exit(); BKE_appdir_exit(); - CLG_exit(); BKE_blender_atexit(); wm_autosave_delete(); BKE_tempdir_session_purge(); + + /* Logging cannot be called after exiting (#CLOG_INFO, #CLOG_WARN etc will crash). + * So postpone exiting until other sub-systems that may use logging have shut down. */ + CLG_exit(); } void WM_exit(bContext *C) diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c index cedb9db62cb..6fc9300926c 100644 --- a/source/blender/windowmanager/intern/wm_operator_utils.c +++ b/source/blender/windowmanager/intern/wm_operator_utils.c @@ -209,7 +209,7 @@ static int op_generic_value_invoke(bContext *C, wmOperator *op, const wmEvent *e return WM_operator_call_notest(C, op); } - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( diff --git a/source/blender/windowmanager/intern/wm_platform_support.c b/source/blender/windowmanager/intern/wm_platform_support.c index becc2d896d0..a0519506d29 100644 --- a/source/blender/windowmanager/intern/wm_platform_support.c +++ b/source/blender/windowmanager/intern/wm_platform_support.c @@ -32,35 +32,35 @@ */ static bool wm_platform_support_check_approval(const char *platform_support_key, bool update) { - const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); - bool result = false; - if (G.factory_startup) { - return result; + return false; + } + const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); + if (!cfgdir) { + return false; } - if (cfgdir) { - char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_PLATFORM_SUPPORT_FILE); - LinkNode *lines = BLI_file_read_as_lines(filepath); - for (LinkNode *line_node = lines; line_node; line_node = line_node->next) { - char *line = line_node->link; - if (STREQ(line, platform_support_key)) { - result = true; - break; - } + bool result = false; + char filepath[FILE_MAX]; + BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_PLATFORM_SUPPORT_FILE); + LinkNode *lines = BLI_file_read_as_lines(filepath); + for (LinkNode *line_node = lines; line_node; line_node = line_node->next) { + char *line = line_node->link; + if (STREQ(line, platform_support_key)) { + result = true; + break; } + } - if (!result && update) { - FILE *fp = BLI_fopen(filepath, "a"); - if (fp) { - fprintf(fp, "%s\n", platform_support_key); - fclose(fp); - } + if (!result && update) { + FILE *fp = BLI_fopen(filepath, "a"); + if (fp) { + fprintf(fp, "%s\n", platform_support_key); + fclose(fp); } - - BLI_file_free_lines(lines); } + + BLI_file_free_lines(lines); return result; } diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 0e9c3a853aa..eee64b97e82 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -26,9 +26,8 @@ if(HAVE_FEENABLEEXCEPT) endif() if(WITH_TBB) - # Force TBB libraries to be in front of MKL (part of OpenImageDenoise), so - # that it is initialized before MKL and static library initialization order - # issues are avoided. + # Force TBB libraries to be in front of MKL (part of `OpenImageDenoise`), so + # that it is initialized before MKL and static library initialization order issues are avoided. # # This isn't fully robust but seems to work. list(INSERT LIB 0 ${TBB_LIBRARIES}) @@ -58,7 +57,7 @@ endif() if(WITH_TBB) blender_include_dirs(${TBB_INCLUDE_DIRS}) if(WIN32) - # For pragma that links tbbmalloc_proxy.lib + # For `pragma` that links `tbbmalloc_proxy.lib`. link_directories(${LIBDIR}/tbb/lib) endif() endif() @@ -108,7 +107,7 @@ if(WITH_OPENCOLORIO) add_definitions(-DWITH_OCIO) endif() -# Setup the exe sources and buildinfo +# Setup the EXE sources and `buildinfo`. set(SRC creator.c creator_args.c @@ -117,7 +116,7 @@ set(SRC creator_intern.h ) -# MSVC 2010 gives linking errors with the manifest +# MSVC 2010 gives linking errors with the manifest. if(WIN32 AND NOT UNIX) add_definitions( -DBLEN_VER_RC_STR="${BLENDER_VERSION}" @@ -173,19 +172,20 @@ if(WITH_BUILDINFO) unset(BUILD_SYSTEM) # -------------------------------------------------------------------------- - # write header for values that change each build - # note, generated file is in build dir's source/creator - # except when used as an include path. + # Write header for values that change each build + # + # NOTE: generated file is in build directory `source/creator` + # except when used as an include path. add_definitions(-DWITH_BUILDINFO_HEADER) - # include the output directory, where the buildinfo.h file is generated + # Include the output directory, where the `buildinfo.h` file is generated. include_directories(${CMAKE_CURRENT_BINARY_DIR}) - # XXX, ${buildinfo_h_fake} is used here, + # XXX: `${buildinfo_h_fake}` is used here, # because we rely on that file being detected as missing - # every build so that the real header "buildinfo.h" is updated. + # every build so that the real header `buildinfo.h` is updated. # # Keep this until we find a better way to resolve! @@ -193,14 +193,18 @@ if(WITH_BUILDINFO) set(buildinfo_h_fake "${CMAKE_CURRENT_BINARY_DIR}/buildinfo.h_fake") if(EXISTS ${buildinfo_h_fake}) - message(FATAL_ERROR "File \"${buildinfo_h_fake}\" found, this should never be created, remove!") + message( + FATAL_ERROR + "File \"${buildinfo_h_fake}\" found, this should never be created, remove!" + ) endif() - # From the cmake documentation "If the output of the custom command is not actually created as a + # From the CMAKE documentation "If the output of the custom command is not actually created as a # file on disk it should be marked with the SYMBOLIC source file property." # - # Not doing this leads to build warnings for the not generated file on windows when using msbuild - SET_SOURCE_FILES_PROPERTIES(${buildinfo_h_fake} PROPERTIES SYMBOLIC TRUE) + # Not doing this leads to build warnings for the not generated file on + # MS-Windows when using `msbuild`. + set_source_files_properties(${buildinfo_h_fake} PROPERTIES SYMBOLIC TRUE) # a custom target that is always built add_custom_target( @@ -208,19 +212,21 @@ if(WITH_BUILDINFO) DEPENDS ${buildinfo_h_fake} ) - # creates buildinfo.h using cmake script + # Creates `buildinfo.h` using CMAKE script. add_custom_command( OUTPUT ${buildinfo_h_fake} # ensure we always run ${buildinfo_h_real} - COMMAND ${CMAKE_COMMAND} - -DSOURCE_DIR=${CMAKE_SOURCE_DIR} - # overrides only used when non-empty strings - -DBUILD_DATE=${BUILDINFO_OVERRIDE_DATE} - -DBUILD_TIME=${BUILDINFO_OVERRIDE_TIME} - -P ${CMAKE_SOURCE_DIR}/build_files/cmake/buildinfo.cmake) - - # buildinfo.h is a generated file + COMMAND + ${CMAKE_COMMAND} + -DSOURCE_DIR=${CMAKE_SOURCE_DIR} + # Overrides only used when non-empty strings. + -DBUILD_DATE=${BUILDINFO_OVERRIDE_DATE} + -DBUILD_TIME=${BUILDINFO_OVERRIDE_TIME} + -P ${CMAKE_SOURCE_DIR}/build_files/cmake/buildinfo.cmake + ) + + # `buildinfo.h` is a generated file. set_source_files_properties( ${buildinfo_h_real} PROPERTIES GENERATED TRUE @@ -229,7 +235,7 @@ if(WITH_BUILDINFO) unset(buildinfo_h_real) unset(buildinfo_h_fake) - # add deps below, after adding blender + # Add dependencies below, after adding Blender # -------------- done with header values. list(APPEND SRC @@ -247,26 +253,35 @@ add_cc_flags_custom_test(blender) if(WITH_PYTHON_MODULE) add_definitions(-DWITH_PYTHON_MODULE) - # creates ./bin/bpy.so which can be imported as a python module. + # Creates `./bpy/__init__.so` which can be imported as a Python module. # - # note that 'SHARED' works on Linux and Windows, - # but not OSX which _must_ be 'MODULE' + # Note that 'SHARED' works on Linux and Windows, but not MACOS which _must_ be 'MODULE'. add_library(blender MODULE ${SRC}) + + + get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(GENERATOR_IS_MULTI_CONFIG) + set(BPY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>/bpy) + else() + set(BPY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/bpy) + endif() + set_target_properties( blender PROPERTIES PREFIX "" - OUTPUT_NAME bpy - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin # only needed on windows + OUTPUT_NAME __init__ + LIBRARY_OUTPUT_DIRECTORY ${BPY_OUTPUT_DIRECTORY} + RUNTIME_OUTPUT_DIRECTORY ${BPY_OUTPUT_DIRECTORY} ) + unset(BPY_OUTPUT_DIRECTORY) if(APPLE) set_target_properties(blender PROPERTIES MACOSX_BUNDLE TRUE) endif() if(WIN32) - # python modules use this + # Python modules use this. set_target_properties( blender PROPERTIES @@ -288,31 +303,33 @@ else() endif() if(WITH_BUILDINFO) - # explicitly say that the executable depends on the buildinfo + # Explicitly say that the executable depends on the `buildinfo`. add_dependencies(blender buildinfo) endif() set(BLENDER_TEXT_FILES ${CMAKE_SOURCE_DIR}/release/text/copyright.txt - # generate this file - # ${CMAKE_SOURCE_DIR}/release/text/readme.html + # Generate this file: + # `${CMAKE_SOURCE_DIR}/release/text/readme.html` ) # ----------------------------------------------------------------------------- -# Platform specific target destinations for version dir, libs, bpy, text files. +# Platform specific target destinations +# +# Setup version directory, libraries, `bpy` & text files. if(UNIX AND NOT APPLE) if(WITH_PYTHON_MODULE) if(WITH_INSTALL_PORTABLE) - set(TARGETDIR_BPY .) - set(TARGETDIR_VER ${BLENDER_VERSION}) - set(TARGETDIR_LIB lib) + set(TARGETDIR_BPY bpy) + set(TARGETDIR_VER bpy/${BLENDER_VERSION}) + set(TARGETDIR_LIB bpy/lib) else() - set(TARGETDIR_BPY ${PYTHON_SITE_PACKAGES}) - set(TARGETDIR_VER ${PYTHON_SITE_PACKAGES}/${BLENDER_VERSION}) - set(TARGETDIR_LIB ${PYTHON_SITE_PACKAGES}/lib) + set(TARGETDIR_BPY ${PYTHON_SITE_PACKAGES}/bpy) + set(TARGETDIR_VER ${PYTHON_SITE_PACKAGES}/bpy/${BLENDER_VERSION}) + set(TARGETDIR_LIB ${PYTHON_SITE_PACKAGES}/bpy/lib) endif() else() if(WITH_INSTALL_PORTABLE) @@ -326,21 +343,28 @@ if(UNIX AND NOT APPLE) endif() elseif(WIN32) - set(TARGETDIR_VER ${BLENDER_VERSION}) - set(TARGETDIR_TEXT .) - set(TARGETDIR_LIB .) - + if(WITH_PYTHON_MODULE) + set(TARGETDIR_BPY $<TARGET_FILE_DIR:blender>) + set(TARGETDIR_VER $<TARGET_FILE_DIR:blender>/${BLENDER_VERSION}) + # Important the DLL's are next to `__init__.pyd` otherwise it won't load. + set(TARGETDIR_LIB $<TARGET_FILE_DIR:blender>) + else() + set(TARGETDIR_VER ${BLENDER_VERSION}) + set(TARGETDIR_TEXT .) + set(TARGETDIR_LIB .) + endif() elseif(APPLE) if(WITH_PYTHON_MODULE) if(WITH_INSTALL_PORTABLE) - set(TARGETDIR_VER $<TARGET_FILE_DIR:blender>/../Resources/${BLENDER_VERSION}) - set(TARGETDIR_LIB lib) + set(TARGETDIR_BPY bpy) + set(TARGETDIR_VER bpy/${BLENDER_VERSION}) + set(TARGETDIR_LIB bpy/lib) else() # Paths defined in terms of site-packages since the site-packages # directory can be a symlink (brew for example). - set(TARGETDIR_BPY ${PYTHON_LIBPATH}/site-packages) - set(TARGETDIR_VER ${TARGETDIR_BPY}/../Resources/${BLENDER_VERSION}) - set(TARGETDIR_LIB ${TARGETDIR_BPY}/lib) + set(TARGETDIR_BPY ${PYTHON_SITE_PACKAGES}/bpy) + set(TARGETDIR_VER ${PYTHON_SITE_PACKAGES}/bpy/${BLENDER_VERSION}) + set(TARGETDIR_LIB ${PYTHON_SITE_PACKAGES}/bpy/lib) endif() else() set(TARGETDIR_VER Blender.app/Contents/Resources/${BLENDER_VERSION}) @@ -348,7 +372,7 @@ elseif(APPLE) set(TARGETDIR_TEXT Blender.app/Contents/Resources/text) endif() - # Skip relinking on cpack / install + # Skip re-linking on CPACK / install. set_target_properties(blender PROPERTIES BUILD_WITH_INSTALL_RPATH true) endif() @@ -371,14 +395,14 @@ if(WITH_PYTHON) "${BLENDER_VERSION_CYCLE}" STREQUAL "rc") set(ADDON_EXCLUDE_CONDITIONAL "addons_contrib/*") else() - set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # dummy, won't do anything + set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # Dummy, won't do anything. endif() # do not install freestyle dir if disabled if(NOT WITH_FREESTYLE) set(FREESTYLE_EXCLUDE_CONDITIONAL "freestyle/*") else() - set(FREESTYLE_EXCLUDE_CONDITIONAL "_freestyle/*") # dummy, won't do anything + set(FREESTYLE_EXCLUDE_CONDITIONAL "_freestyle/*") # Dummy, won't do anything. endif() install( @@ -398,8 +422,7 @@ endif() # fonts install( - DIRECTORY - ${CMAKE_SOURCE_DIR}/release/datafiles/fonts + DIRECTORY ${CMAKE_SOURCE_DIR}/release/datafiles/fonts DESTINATION ${TARGETDIR_VER}/datafiles ) @@ -413,14 +436,14 @@ if(WITH_INTERNATIONAL) msgfmt_simple(${_po_file} _all_mo_files) endforeach() - # Create a custom target which will compile all po to mo + # Create a custom target which will compile all `*.po` to `*.mo`. add_custom_target( locales DEPENDS ${_all_mo_files} ) add_dependencies(blender locales) - # Generate INSTALL rules + # Generate INSTALL rules. install( FILES ${_locale_dir}/languages DESTINATION ${_locale_target_dir} @@ -445,7 +468,7 @@ if(WITH_INTERNATIONAL) unset(_locale_dir) endif() -# color management +# Color management. if(WITH_OPENCOLORIO) install( DIRECTORY ${CMAKE_SOURCE_DIR}/release/datafiles/colormanagement @@ -453,13 +476,14 @@ if(WITH_OPENCOLORIO) ) endif() -# helpful tip when using make +# Helpful tip when using make. if("${CMAKE_GENERATOR}" MATCHES ".*Makefiles.*") - # message after building. + # Message to display after building. add_custom_command( TARGET blender POST_BUILD MAIN_DEPENDENCY blender - COMMAND ${CMAKE_COMMAND} -E - echo 'now run: \"make install\" to copy runtime files and scripts to ${TARGETDIR_VER}' + COMMAND + ${CMAKE_COMMAND} -E + echo 'now run: \"make install\" to copy runtime files and scripts to ${TARGETDIR_VER}' ) endif() @@ -473,9 +497,10 @@ if(UNIX AND NOT APPLE) if(WITH_DOC_MANPAGE) add_custom_target( blender_man_page ALL - COMMAND ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py - --blender ${EXECUTABLE_OUTPUT_PATH}/blender - --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1 + COMMAND + ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py + --blender ${EXECUTABLE_OUTPUT_PATH}/blender + --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1 ) add_dependencies(blender_man_page blender) endif() @@ -488,7 +513,7 @@ if(UNIX AND NOT APPLE) ) endif() - # there are a few differences between portable and system install + # There are a few differences between portable and system install. if(WITH_PYTHON_MODULE) if(WITH_INSTALL_PORTABLE) install( @@ -561,14 +586,14 @@ if(UNIX AND NOT APPLE) DESTINATION bin ) if(WITH_DOC_MANPAGE) - # manpage only with 'blender' binary + # Manual page (only with `blender` binary). install( FILES ${CMAKE_CURRENT_BINARY_DIR}/blender.1 DESTINATION share/man/man1 ) endif() - # misc files + # Misc files. install( FILES ${CMAKE_SOURCE_DIR}/release/freedesktop/blender.desktop DESTINATION share/applications @@ -597,11 +622,9 @@ if(UNIX AND NOT APPLE) DESTINATION ${TARGETDIR_VER}/python/bin ) - # on some platforms (like openSUSE) Python is linked - # to be used from lib64 folder. - # determine this from Python's libraries path - # - # ugh, its possible 'lib64' is just a symlink to 'lib' which causes incorrect use of 'lib64' + # On some platforms (like openSUSE) Python is linked to be used from `lib64` directory. + # determine this from Python's libraries path. + # Ugh, its possible `lib64` is just a symlink to 'lib' which causes incorrect use of `lib64`. get_filename_component(_pypath_real ${PYTHON_LIBPATH} REALPATH) if(${_pypath_real} MATCHES "lib64$") set(_target_LIB "lib64") @@ -610,7 +633,7 @@ if(UNIX AND NOT APPLE) endif() unset(_pypath_real) - # Copy the systems python into the install directory + # Copy the systems python into the install directory: # install(CODE "message(\"copying a subset of the systems python...\")") install( DIRECTORY ${PYTHON_LIBPATH}/python${PYTHON_VERSION} @@ -628,8 +651,8 @@ if(UNIX AND NOT APPLE) PATTERN "wininst*.exe" EXCLUDE # from distutils, avoid malware false positive ) - # Needed for distutils/pip - # get the last part of the include dir, will be 'python{version}{abiflag}', + # Needed for `distutils/pip`. + # Get the last part of the include dir, will be `python{version}{abiflag}`. get_filename_component(_py_inc_suffix ${PYTHON_INCLUDE_DIR} NAME) install( FILES ${PYTHON_INCLUDE_DIR}/pyconfig.h @@ -639,7 +662,7 @@ if(UNIX AND NOT APPLE) if(WITH_PYTHON_INSTALL_NUMPY) # Install to the same directory as the source, so debian-like - # distros are happy with their policy. + # distributions are happy with their policy. set(_suffix "site-packages") if(${PYTHON_NUMPY_PATH} MATCHES "dist-packages") set(_suffix "dist-packages") @@ -676,7 +699,7 @@ if(UNIX AND NOT APPLE) if(WITH_PYTHON_INSTALL_ZSTANDARD) # Install to the same directory as the source, so debian-like - # distros are happy with their policy. + # distributions are happy with their policy. set(_suffix "site-packages") if(${PYTHON_ZSTANDARD_PATH} MATCHES "dist-packages") set(_suffix "dist-packages") @@ -692,7 +715,7 @@ if(UNIX AND NOT APPLE) unset(_suffix) endif() - # Copy requests, we need to generalize site-packages + # Copy requests, we need to generalize site-packages. if(WITH_PYTHON_INSTALL_REQUESTS) set(_suffix "site-packages") if(${PYTHON_REQUESTS_PATH} MATCHES "dist-packages") @@ -708,9 +731,8 @@ if(UNIX AND NOT APPLE) ) # On some platforms requests does have extra dependencies. # - # Either 'chardet' or 'charset_normalizer" is used, depending on the - # version of Python. The code below silently skips the one that's not - # available, so we can just list both here. + # Either `chardet` or `charset_normalizer` is used, depending on the version of Python. + # The code below silently skips the one that's not available, so we can list both here. set(_requests_deps "certifi" "chardet" "charset_normalizer" "idna" "urllib3") foreach(_requests_dep ${_requests_deps}) if(EXISTS ${PYTHON_REQUESTS_PATH}/${_requests_dep}) @@ -765,11 +787,16 @@ elseif(WIN32) ) endif() if(MSVC_ASAN) - # The asan dll's can be found in the same folder as the compiler, this is the easiest way to find these. + # The ASAN DLL's can be found in the same folder as the compiler, + # this is the easiest way to find these. string(REPLACE "cl.exe" "clang_rt.asan_dynamic-x86_64.dll" ASAN_DLL ${CMAKE_C_COMPILER}) string(REPLACE "cl.exe" "clang_rt.asan_dbg_dynamic-x86_64.dll" ASAN_DEBUG_DLL ${CMAKE_C_COMPILER}) if(NOT EXISTS "${ASAN_DLL}") - message(FATAL_ERROR "Asan is enabled, but the ASAN runtime is not detected, this is an optional component during the MSVC install, please install it") + message( + FATAL_ERROR + "ASAN is enabled, but the ASAN runtime is not detected, " + "this is an optional component during the MSVC install, please install it" + ) endif() install( FILES ${ASAN_DLL} @@ -804,10 +831,14 @@ elseif(WIN32) if(WITH_WINDOWS_PDB) if(WITH_WINDOWS_STRIPPED_PDB) - # Icky hack for older cmake from https://stackoverflow.com/a/21198501 - # $<CONFIG> will work in newer cmake but the version currently (3.12) - # on the buildbot does not support this endavour. - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/blender_public.pdb DESTINATION . RENAME blender.pdb) + # Icky hack for older CMAKE from https://stackoverflow.com/a/21198501 + # `$<CONFIG>` will work in newer CMAKE but the version currently (3.12) + # on the build-bot does not support this endeavor. + install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/blender_public.pdb + DESTINATION . + RENAME blender.pdb + ) else() install(FILES $<TARGET_PDB_FILE:blender> DESTINATION . RENAME blender.pdb) endif() @@ -829,24 +860,29 @@ elseif(WIN32) if(WITH_PYTHON) string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION}) - if(NOT CMAKE_COMPILER_IS_GNUCC) - install( - FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll - ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3.dll - DESTINATION ${TARGETDIR_LIB} - CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel - ) + if(NOT WITH_PYTHON_MODULE) + if(NOT CMAKE_COMPILER_IS_GNUCC) + install( + FILES + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3.dll + DESTINATION ${TARGETDIR_LIB} + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + ) - install( - FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll - ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3_d.dll - DESTINATION ${TARGETDIR_LIB} - CONFIGURATIONS Debug - ) + install( + FILES + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3_d.dll + DESTINATION ${TARGETDIR_LIB} + CONFIGURATIONS Debug + ) + endif() endif() if(WITH_PYTHON_INSTALL) - # note, as far as python is concerned 'RelWithDebInfo' is not debug since its without debug flags. + # NOTE: as far as python is concerned `RelWithDebInfo` + # is not debug since its without debug flags. install(DIRECTORY DESTINATION ${TARGETDIR_VER}/python) install(DIRECTORY DESTINATION ${TARGETDIR_VER}/python/lib) @@ -856,7 +892,7 @@ elseif(WIN32) DESTINATION ${BLENDER_VERSION}/python/ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel PATTERN ".svn" EXCLUDE - PATTERN "*_d.*" EXCLUDE # * debug libraries * + PATTERN "*_d.*" EXCLUDE # * debug libraries * PATTERN "__pycache__" EXCLUDE # * any cache * PATTERN "*.pyc" EXCLUDE # * any cache * PATTERN "*.pyo" EXCLUDE # * any cache * @@ -887,27 +923,31 @@ elseif(WIN32) ) install( - FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll - ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python.exe + FILES + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python.exe DESTINATION ${BLENDER_VERSION}/python/bin CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( - FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll - ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python_d.exe + FILES + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python_d.exe DESTINATION ${BLENDER_VERSION}/python/bin CONFIGURATIONS Debug ) if(WINDOWS_PYTHON_DEBUG) install( - FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.pdb + FILES + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.pdb DESTINATION ${TARGETDIR_LIB} CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( - FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}_d.pdb + FILES + ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}_d.pdb DESTINATION ${TARGETDIR_LIB} CONFIGURATIONS Debug ) @@ -918,9 +958,8 @@ elseif(WIN32) endif() if(WITH_CODEC_FFMPEG) - # Filenames change slightly between ffmpeg versions - # check both 5.0 and fallback to 4.4 to ease the transition - # between versions. + # Filenames change slightly between FFMPEG versions check both 5.0 and fallback to 4.4 + # to ease the transition between versions. if(EXISTS "${LIBDIR}/ffmpeg/lib/avcodec-59.dll") install( FILES @@ -1009,16 +1048,19 @@ elseif(WIN32) ) endif() - install( - FILES - ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu.cmd - ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd - ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd - ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd - ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd - ${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json - DESTINATION ${TARGETDIR_LIB} - ) + + if(NOT WITH_PYTHON_MODULE) + install( + FILES + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json + DESTINATION ${TARGETDIR_LIB} + ) + endif() if(WITH_BLENDER_THUMBNAILER) install( @@ -1035,12 +1077,12 @@ elseif(WIN32) endif() elseif(APPLE) if(NOT WITH_PYTHON_MODULE) - # Uppercase name for app bundle + # Uppercase name for app bundle. set_target_properties(blender PROPERTIES OUTPUT_NAME Blender) endif() - # handy install macro to exclude files, we use \$ escape for the "to" - # argument when calling so ${BUILD_TYPE} does not get expanded + # Handy install macro to exclude files, we use \$ escape for the "to" + # argument when calling so `${BUILD_TYPE}` does not get expanded. macro(install_dir from to) install( DIRECTORY ${from} @@ -1068,10 +1110,12 @@ elseif(APPLE) set(OSX_APP_SOURCEDIR ${CMAKE_SOURCE_DIR}/release/darwin/Blender.app) - # setup Info.plist - execute_process(COMMAND date "+%Y-%m-%d" - OUTPUT_VARIABLE BLENDER_DATE - OUTPUT_STRIP_TRAILING_WHITESPACE) + # Setup `Info.plist`. + execute_process( + COMMAND date "+%Y-%m-%d" + OUTPUT_VARIABLE BLENDER_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) set_target_properties(blender PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${OSX_APP_SOURCEDIR}/Contents/Info.plist @@ -1079,19 +1123,22 @@ elseif(APPLE) MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}" ) - # Gather the date in finder-style - execute_process(COMMAND date "+%m/%d/%Y/%H:%M" - OUTPUT_VARIABLE SETFILE_DATE - OUTPUT_STRIP_TRAILING_WHITESPACE) + # Gather the date in finder-style. + execute_process( + COMMAND date "+%m/%d/%Y/%H:%M" + OUTPUT_VARIABLE SETFILE_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) - # Give the bundle actual creation/modification date + # Give the bundle actual creation/modification date. # - # Note that the directory might not yet exist, which happens when CMake is first run. + # Note that the directory might not yet exist, which happens when CMAKE is first run. if(NOT EXISTS ${EXECUTABLE_OUTPUT_PATH}/Blender.app) file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/Blender.app) endif() - execute_process(COMMAND SetFile -d ${SETFILE_DATE} -m ${SETFILE_DATE} - ${EXECUTABLE_OUTPUT_PATH}/Blender.app) + execute_process( + COMMAND SetFile -d ${SETFILE_DATE} -m ${SETFILE_DATE} ${EXECUTABLE_OUTPUT_PATH}/Blender.app + ) install( TARGETS blender @@ -1122,11 +1169,11 @@ elseif(APPLE) ) endif() - # python + # Python. if(WITH_PYTHON AND NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK) - # Copy the python libs into the install directory + # Copy the python libraries into the install directory. install_dir( - ${PYTHON_LIBPATH} + ${PYTHON_LIBPATH}/python${PYTHON_VERSION} ${TARGETDIR_VER}/python/lib ) @@ -1136,8 +1183,8 @@ elseif(APPLE) DESTINATION ${TARGETDIR_VER}/python/bin ) - # Needed for distutils/pip - # get the last part of the include dir, will be 'python{version}{abiflag}', + # Needed for `distutils/pip`. + # Get the last part of the include dir, will be `python{version}{abiflag}`. get_filename_component(_py_inc_suffix ${PYTHON_INCLUDE_DIR} NAME) install( FILES ${PYTHON_INCLUDE_DIR}/pyconfig.h @@ -1181,13 +1228,12 @@ if(DEFINED TARGETDIR_TEXT) ) install( - DIRECTORY - ${CMAKE_SOURCE_DIR}/release/license + DIRECTORY ${CMAKE_SOURCE_DIR}/release/license DESTINATION "${TARGETDIR_TEXT}" ) endif() -# install more files specified elsewhere +# Install more files specified elsewhere. delayed_do_install(${TARGETDIR_VER}) unset(BLENDER_TEXT_FILES) @@ -1214,16 +1260,18 @@ unset(_icon_names) unset(_icon_files) unset(_f) + # ----------------------------------------------------------------------------- # Studio Lights + install( - DIRECTORY - ${CMAKE_SOURCE_DIR}/release/datafiles/studiolights + DIRECTORY ${CMAKE_SOURCE_DIR}/release/datafiles/studiolights DESTINATION ${TARGETDIR_VER}/datafiles ) + # ----------------------------------------------------------------------------- -# Setup link libs +# Setup link libraries add_dependencies(blender makesdna) target_link_libraries(blender ${LIB}) @@ -1236,22 +1284,23 @@ if(DEFINED PLATFORM_SYMBOLS_MAP) set_target_properties(blender PROPERTIES LINK_DEPENDS ${PLATFORM_SYMBOLS_MAP}) endif() + # ----------------------------------------------------------------------------- # USD registry. -# USD requires a set of JSON files that define the standard schemas. These -# files are required at runtime. + +# USD requires a set of JSON files that define the standard schemas. +# These files are required at runtime. if(WITH_USD) add_definitions(-DWITH_USD) - install(DIRECTORY - ${USD_LIBRARY_DIR}/usd + install( + DIRECTORY ${USD_LIBRARY_DIR}/usd DESTINATION "${TARGETDIR_VER}/datafiles" ) endif() -# vcpkg substitutes our libs with theirs, which will cause issues when you -# you run these builds on other systems due to missing dlls. So we opt out -# the use of vcpkg +# `vcpkg` substitutes our libraries with theirs, which will cause issues when you you run +# these builds on other systems due to missing DLL's. So we opt out the use of `vcpkg`. if(WIN32) set_target_properties(blender PROPERTIES VS_GLOBAL_VcpkgEnabled "false") set_target_properties(blender PROPERTIES @@ -1261,12 +1310,18 @@ if(WIN32) if(WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB) # This is slightly messy, but single target generators like ninja will not have the # `CMAKE_CFG_INTDIR` variable and multi-target generators like `msbuild` will not have - # `CMAKE_BUILD_TYPE`. This can be simplified by target_link_options and the `$<CONFIG>` + # `CMAKE_BUILD_TYPE`. This can be simplified by `target_link_options` and the `$<CONFIG>` # generator expression in newer CMAKE (2.13+) but until that time this fill have suffice. if(CMAKE_BUILD_TYPE) - set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb") + set_property( + TARGET blender APPEND_STRING PROPERTY LINK_FLAGS + " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb" + ) else() - set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb") + set_property( + TARGET blender APPEND_STRING PROPERTY LINK_FLAGS + " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb" + ) endif() endif() endif() diff --git a/source/creator/creator.c b/source/creator/creator.c index e7a803d383f..2d8b1e16098 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -233,6 +233,12 @@ void gmp_blender_init_allocator() /** \name Main Function * \{ */ +/* When building as a Python module, don't use special argument handling + * so the module loading logic can control the `argv` & `argc`. */ +#if defined(WIN32) && !defined(WITH_PYTHON_MODULE) +# define USE_WIN32_UNICODE_ARGS +#endif + /** * Blender's main function responsibilities are: * - setup subsystems. @@ -241,7 +247,7 @@ void gmp_blender_init_allocator() * or exit immediately when running in background-mode. */ int main(int argc, -#ifdef WIN32 +#ifdef USE_WIN32_UNICODE_ARGS const char **UNUSED(argv_c) #else const char **argv @@ -254,7 +260,7 @@ int main(int argc, bArgs *ba; #endif -#ifdef WIN32 +#ifdef USE_WIN32_UNICODE_ARGS char **argv; int argv_num; #endif @@ -280,11 +286,11 @@ int main(int argc, _putenv_s("OMP_WAIT_POLICY", "PASSIVE"); # endif +# ifdef USE_WIN32_UNICODE_ARGS /* Win32 Unicode Arguments. */ - /* NOTE: cannot use `guardedalloc` allocation here, as it's not yet initialized - * (it depends on the arguments passed in, which is what we're getting here!) - */ { + /* NOTE: Can't use `guardedalloc` allocation here, as it's not yet initialized + * (it depends on the arguments passed in, which is what we're getting here!) */ wchar_t **argv_16 = CommandLineToArgvW(GetCommandLineW(), &argc); argv = malloc(argc * sizeof(char *)); for (argv_num = 0; argv_num < argc; argv_num++) { @@ -296,7 +302,8 @@ int main(int argc, app_init_data.argv = argv; app_init_data.argv_num = argv_num; } -#endif /* WIN32 */ +# endif /* USE_WIN32_UNICODE_ARGS */ +#endif /* WIN32 */ /* NOTE: Special exception for guarded allocator type switch: * we need to perform switch from lock-free to fully |