diff options
452 files changed, 15986 insertions, 7092 deletions
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index a3d694b4bc3..fb79eee62be 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -113,7 +113,7 @@ include(cmake/expat.cmake) include(cmake/yamlcpp.cmake) include(cmake/opencolorio.cmake) -if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")) +if(BLENDER_PLATFORM_ARM) include(cmake/sse2neon.cmake) endif() diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake index 8b36af7dc41..5170a3a123e 100644 --- a/build_files/build_environment/cmake/boost.cmake +++ b/build_files/build_environment/cmake/boost.cmake @@ -18,6 +18,12 @@ set(BOOST_ADDRESS_MODEL 64) +if(BLENDER_PLATFORM_ARM) + set(BOOST_ARCHITECTURE arm) +else() + set(BOOST_ARCHITECTURE x86) +endif() + if(WIN32) set(BOOST_TOOLSET toolset=msvc-14.1) set(BOOST_COMPILER_STRING -vc141) @@ -29,7 +35,6 @@ if(WIN32) if(BUILD_MODE STREQUAL Release) set(BOOST_HARVEST_CMD ${BOOST_HARVEST_CMD} && ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/boost/include/boost-${BOOST_VERSION_NODOTS_SHORT}/ ${HARVEST_TARGET}/boost/include/) endif() - elseif(APPLE) set(BOOST_CONFIGURE_COMMAND ./bootstrap.sh) set(BOOST_BUILD_COMMAND ./b2) @@ -93,7 +98,7 @@ ExternalProject_Add(external_boost UPDATE_COMMAND "" PATCH_COMMAND ${BOOST_PATCH_COMMAND} CONFIGURE_COMMAND ${BOOST_CONFIGURE_COMMAND} - BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=x86 address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install + BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=${BOOST_ARCHITECTURE} address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install BUILD_IN_SOURCE 1 INSTALL_COMMAND "${BOOST_HARVEST_CMD}" ) diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake index 4830630def0..cd693d766dc 100644 --- a/build_files/build_environment/cmake/embree.cmake +++ b/build_files/build_environment/cmake/embree.cmake @@ -47,7 +47,7 @@ else() set(EMBREE_BUILD_DIR) endif() -if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")) +if(BLENDER_PLATFORM_ARM) ExternalProject_Add(external_embree GIT_REPOSITORY ${EMBREE_ARM_GIT} GIT_TAG "blender-arm" diff --git a/build_files/build_environment/cmake/gmp.cmake b/build_files/build_environment/cmake/gmp.cmake index 323630a63aa..6ca81678a32 100644 --- a/build_files/build_environment/cmake/gmp.cmake +++ b/build_files/build_environment/cmake/gmp.cmake @@ -25,19 +25,12 @@ else() set(GMP_OPTIONS --enable-static --disable-shared ) endif() -if(APPLE) - if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") - set(GMP_OPTIONS - ${GMP_OPTIONS} - --disable-assembly - ) - else() - set(GMP_OPTIONS - ${GMP_OPTIONS} - --with-pic - ) - endif() -elseif(UNIX) +if(APPLE AND NOT BLENDER_PLATFORM_ARM) + set(GMP_OPTIONS + ${GMP_OPTIONS} + --with-pic + ) +elseif(UNIX AND NOT APPLE) set(GMP_OPTIONS ${GMP_OPTIONS} --with-pic @@ -45,6 +38,13 @@ elseif(UNIX) ) endif() +if(BLENDER_PLATFORM_ARM) + set(GMP_OPTIONS + ${GMP_OPTIONS} + --disable-assembly + ) +endif() + ExternalProject_Add(external_gmp URL file://${PACKAGE_DIR}/${GMP_FILE} DOWNLOAD_DIR ${DOWNLOAD_DIR} diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index 23d0dcbab7b..fc7e652a028 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -109,9 +109,9 @@ harvest(llvm/lib llvm/lib "libclang*.a") if(APPLE) harvest(openmp/lib openmp/lib "*") harvest(openmp/include openmp/include "*.h") - if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") - harvest(sse2neon sse2neon "*.h") - endif() +endif() +if(BLENDER_PLATFORM_ARM) + harvest(sse2neon sse2neon "*.h") endif() harvest(ogg/lib ffmpeg/lib "*.a") harvest(openal/include openal/include "*.h") diff --git a/build_files/build_environment/cmake/llvm.cmake b/build_files/build_environment/cmake/llvm.cmake index f067267a416..cbb986410aa 100644 --- a/build_files/build_environment/cmake/llvm.cmake +++ b/build_files/build_environment/cmake/llvm.cmake @@ -16,7 +16,7 @@ # # ***** END GPL LICENSE BLOCK ***** -if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") +if(BLENDER_PLATFORM_ARM) set(LLVM_TARGETS AArch64$<SEMICOLON>ARM) else() set(LLVM_TARGETS X86) diff --git a/build_files/build_environment/cmake/opencolorio.cmake b/build_files/build_environment/cmake/opencolorio.cmake index bd03be5ebff..28c93973cf5 100644 --- a/build_files/build_environment/cmake/opencolorio.cmake +++ b/build_files/build_environment/cmake/opencolorio.cmake @@ -36,7 +36,7 @@ set(OPENCOLORIO_EXTRA_ARGS -Dyaml-cpp_ROOT=${LIBDIR}/yamlcpp ) -if(APPLE AND NOT("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64")) +if(BLENDER_PLATFORM_ARM) set(OPENCOLORIO_EXTRA_ARGS ${OPENCOLORIO_EXTRA_ARGS} -DOCIO_USE_SSE=OFF diff --git a/build_files/build_environment/cmake/options.cmake b/build_files/build_environment/cmake/options.cmake index 486b3d1a802..8930bd70fab 100644 --- a/build_files/build_environment/cmake/options.cmake +++ b/build_files/build_environment/cmake/options.cmake @@ -137,6 +137,10 @@ else() endif() set(OSX_SYSROOT ${XCODE_DEV_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk) + if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") + set(BLENDER_PLATFORM_ARM ON) + endif() + set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}") set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++ -arch ${CMAKE_OSX_ARCHITECTURES}") set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}") @@ -151,6 +155,10 @@ else() -DCMAKE_OSX_SYSROOT:PATH=${OSX_SYSROOT} ) else() + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") + set(BLENDER_PLATFORM_ARM ON) + endif() + set(PLATFORM_CFLAGS "-fPIC") set(PLATFORM_CXXFLAGS "-std=c++11 -fPIC") set(PLATFORM_LDFLAGS) diff --git a/build_files/build_environment/cmake/png.cmake b/build_files/build_environment/cmake/png.cmake index d9248b61c98..458d3a1fd98 100644 --- a/build_files/build_environment/cmake/png.cmake +++ b/build_files/build_environment/cmake/png.cmake @@ -22,8 +22,8 @@ set(PNG_EXTRA_ARGS -DPNG_STATIC=ON ) -if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")) - set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=on -DCMAKE_SYSTEM_PROCESSOR="aarch64") +if(BLENDER_PLATFORM_ARM) + set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR="aarch64") endif() ExternalProject_Add(external_png diff --git a/build_files/build_environment/cmake/sse2neon.cmake b/build_files/build_environment/cmake/sse2neon.cmake index dca2d94f913..d7987fdfd2f 100644 --- a/build_files/build_environment/cmake/sse2neon.cmake +++ b/build_files/build_environment/cmake/sse2neon.cmake @@ -16,15 +16,13 @@ # # ***** END GPL LICENSE BLOCK ***** -if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")) - ExternalProject_Add(external_sse2neon - GIT_REPOSITORY ${SSE2NEON_GIT} - GIT_TAG ${SSE2NEON_GIT_HASH} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - PREFIX ${BUILD_DIR}/sse2neon - CONFIGURE_COMMAND echo sse2neon - Nothing to configure - BUILD_COMMAND echo sse2neon - nothing to build - INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon - INSTALL_DIR ${LIBDIR}/sse2neon - ) -endif() +ExternalProject_Add(external_sse2neon + GIT_REPOSITORY ${SSE2NEON_GIT} + GIT_TAG ${SSE2NEON_GIT_HASH} + DOWNLOAD_DIR ${DOWNLOAD_DIR} + PREFIX ${BUILD_DIR}/sse2neon + CONFIGURE_COMMAND echo sse2neon - Nothing to configure + BUILD_COMMAND echo sse2neon - nothing to build + INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon + INSTALL_DIR ${LIBDIR}/sse2neon +) diff --git a/build_files/build_environment/cmake/ssl.cmake b/build_files/build_environment/cmake/ssl.cmake index 4426cc876c6..615b88167ec 100644 --- a/build_files/build_environment/cmake/ssl.cmake +++ b/build_files/build_environment/cmake/ssl.cmake @@ -22,7 +22,9 @@ set(SSL_PATCH_CMD echo .) if(APPLE) set(SSL_OS_COMPILER "blender-darwin-${CMAKE_OSX_ARCHITECTURES}") else() - if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + if(BLENDER_PLATFORM_ARM) + set(SSL_OS_COMPILER "blender-linux-aarch64") + elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") set(SSL_EXTRA_ARGS enable-ec_nistp_64_gcc_128) set(SSL_OS_COMPILER "blender-linux-x86_64") else() diff --git a/build_files/build_environment/cmake/ssl.conf b/build_files/build_environment/cmake/ssl.conf index 8a9c9dcab4c..fa59bcf2825 100644 --- a/build_files/build_environment/cmake/ssl.conf +++ b/build_files/build_environment/cmake/ssl.conf @@ -8,6 +8,11 @@ my %targets = ( inherit_from => [ "linux-x86_64" ], cflags => add("-fPIC"), }, + "blender-linux-aarch64" => { + inherit_from => [ "linux-aarch64" ], + cxxflags => add("-fPIC"), + cflags => add("-fPIC"), + }, "blender-darwin-x86_64" => { inherit_from => [ "darwin64-x86_64-cc" ], cflags => add("-fPIC"), diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake index b006898e6fd..44307cd2afb 100644 --- a/build_files/build_environment/cmake/tbb.cmake +++ b/build_files/build_environment/cmake/tbb.cmake @@ -21,6 +21,7 @@ if(WIN32) -DTBB_BUILD_TBBMALLOC=On -DTBB_BUILD_TBBMALLOC_PROXY=On -DTBB_BUILD_STATIC=Off + -DTBB_BUILD_TESTS=Off ) set(TBB_LIBRARY tbb) set(TBB_STATIC_LIBRARY Off) @@ -30,6 +31,7 @@ else() -DTBB_BUILD_TBBMALLOC=On -DTBB_BUILD_TBBMALLOC_PROXY=Off -DTBB_BUILD_STATIC=On + -DTBB_BUILD_TESTS=Off ) set(TBB_LIBRARY tbb_static) set(TBB_STATIC_LIBRARY On) @@ -42,7 +44,7 @@ ExternalProject_Add(external_tbb URL_HASH ${TBB_HASH_TYPE}=${TBB_HASH} PREFIX ${BUILD_DIR}/tbb PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/cmakelists_tbb.txt ${BUILD_DIR}/tbb/src/external_tbb/CMakeLists.txt && - ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/src/tbb/version_string.ver && + ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/build/version_string.ver.in && ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/tbb/src/external_tbb < ${PATCH_DIR}/tbb.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/tbb ${DEFAULT_CMAKE_FLAGS} ${TBB_EXTRA_ARGS} INSTALL_DIR ${LIBDIR}/tbb diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 97da5d54d48..83e438614b6 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -152,7 +152,7 @@ set(OPENCOLORIO_HASH 1a2e3478b6cd9a1549f24e1b2205e3f0) set(OPENCOLORIO_HASH_TYPE MD5) set(OPENCOLORIO_FILE OpenColorIO-${OPENCOLORIO_VERSION}.tar.gz) -if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")) +if(BLENDER_PLATFORM_ARM) # Newer version required by ISPC with arm support. set(LLVM_VERSION 11.0.1) set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz) @@ -398,11 +398,20 @@ set(LZMA_HASH 5117f930900b341493827d63aa910ff5e011e0b994197c3b71c08a20228a42df) set(LZMA_HASH_TYPE SHA256) set(LZMA_FILE xz-${LZMA_VERSION}.tar.bz2) -set(SSL_VERSION 1.1.1g) -set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz) -set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46) -set(SSL_HASH_TYPE SHA256) -set(SSL_FILE openssl-${SSL_VERSION}.tar.gz) +if(BLENDER_PLATFORM_ARM) + # Need at least 1.1.1i for aarch64 support (https://github.com/openssl/openssl/pull/13218) + set(SSL_VERSION 1.1.1i) + set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz) + set(SSL_HASH e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242) + set(SSL_HASH_TYPE SHA256) + set(SSL_FILE openssl-${SSL_VERSION}.tar.gz) +else() + set(SSL_VERSION 1.1.1g) + set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz) + set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46) + set(SSL_HASH_TYPE SHA256) + set(SSL_FILE openssl-${SSL_VERSION}.tar.gz) +endif() set(SQLITE_VERSION 3.31.1) set(SQLITE_URI https://www.sqlite.org/2018/sqlite-src-3240000.zip) @@ -453,7 +462,7 @@ set(XR_OPENXR_SDK_HASH 0df6b2fd6045423451a77ff6bc3e1a75) set(XR_OPENXR_SDK_HASH_TYPE MD5) set(XR_OPENXR_SDK_FILE OpenXR-SDK-${XR_OPENXR_SDK_VERSION}.tar.gz) -if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")) +if(BLENDER_PLATFORM_ARM) # Unreleased version with macOS arm support. set(ISPC_URI https://github.com/ispc/ispc/archive/f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip) set(ISPC_HASH d382fea18d01dbd0cd05d9e1ede36d7d) diff --git a/build_files/build_environment/cmake/x264.cmake b/build_files/build_environment/cmake/x264.cmake index a32f119d184..08d698cc3ad 100644 --- a/build_files/build_environment/cmake/x264.cmake +++ b/build_files/build_environment/cmake/x264.cmake @@ -20,24 +20,16 @@ if(WIN32) set(X264_EXTRA_ARGS --enable-win32thread --cross-prefix=${MINGW_HOST}- --host=${MINGW_HOST}) endif() - -if(APPLE) - if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") - set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm") - set(X264_CONFIGURE_ENV echo .) - else() - set(X264_CONFIGURE_ENV - export AS=${LIBDIR}/nasm/bin/nasm - ) - endif() -else() - set(X264_CONFIGURE_ENV echo .) +if(BLENDER_PLATFORM_ARM) + set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm") endif() -if(UNIX AND NOT APPLE) +if((APPLE AND NOT BLENDER_PLATFORM_ARM) OR (UNIX AND NOT APPLE)) set(X264_CONFIGURE_ENV export AS=${LIBDIR}/nasm/bin/nasm ) +else() + set(X264_CONFIGURE_ENV echo .) endif() ExternalProject_Add(external_x264 diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 7cd21b2885c..7d8c2ca3181 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -1797,6 +1797,10 @@ compile_OCIO() { cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=OFF" cmake_d="$cmake_d -D OCIO_BUILD_GPU_TESTS=OFF" + if [ $(uname -m) == "aarch64" ]; then + cmake_d="$cmake_d -D OCIO_USE_SSE=OFF" + fi + if file /bin/cp | grep -q '32-bit'; then cflags="-fPIC -m32 -march=i686" else @@ -2059,7 +2063,10 @@ compile_OIIO() { cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst" cmake_d="$cmake_d -D STOP_ON_WARNING=OFF" cmake_d="$cmake_d -D LINKSTATIC=OFF" - cmake_d="$cmake_d -D USE_SIMD=sse2" + + if [ $(uname -m) != "aarch64" ]; then + cmake_d="$cmake_d -D USE_SIMD=sse2" + fi cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION" @@ -2079,7 +2086,7 @@ compile_OIIO() { cmake_d="$cmake_d -D USE_OPENCV=OFF" cmake_d="$cmake_d -D BUILD_TESTING=OFF" cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF" - cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=ON" + cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF" cmake_d="$cmake_d -D TXT2MAN=" #cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON" #cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON" @@ -2209,10 +2216,15 @@ compile_LLVM() { mkdir build cd build + LLVM_TARGETS="X86" + if [ $(uname -m) == "aarch64" ]; then + LLVM_TARGETS="AArch64" + fi + cmake_d="-D CMAKE_BUILD_TYPE=Release" cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst" cmake_d="$cmake_d -D LLVM_ENABLE_FFI=ON" - cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=X86" + cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=$LLVM_TARGETS" cmake_d="$cmake_d -D LLVM_ENABLE_TERMINFO=OFF" if [ -d $_FFI_INCLUDE_DIR ]; then @@ -2329,13 +2341,16 @@ compile_OSL() { cmake_d="$cmake_d -D STOP_ON_WARNING=OFF" cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF" cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF" - cmake_d="$cmake_d -D USE_SIMD=sse2" cmake_d="$cmake_d -D USE_LLVM_BITCODE=OFF" cmake_d="$cmake_d -D USE_PARTIO=OFF" cmake_d="$cmake_d -D OSL_BUILD_MATERIALX=OFF" cmake_d="$cmake_d -D USE_QT=OFF" cmake_d="$cmake_d -D USE_PYTHON=OFF" + if [ $(uname -m) != "aarch64" ]; then + cmake_d="$cmake_d -D USE_SIMD=sse2" + fi + cmake_d="$cmake_d -D CMAKE_CXX_STANDARD=14" #~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION" diff --git a/build_files/build_environment/patches/cmakelists_tbb.txt b/build_files/build_environment/patches/cmakelists_tbb.txt index 4032e5d6f83..e05f27afdd6 100644 --- a/build_files/build_environment/patches/cmakelists_tbb.txt +++ b/build_files/build_environment/patches/cmakelists_tbb.txt @@ -1,5 +1,32 @@ -cmake_minimum_required (VERSION 2.8) -project(tbb CXX) +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + +if (POLICY CMP0048) + # cmake warns if loaded from a min-3.0-required parent dir, so silence the warning: + cmake_policy(SET CMP0048 NEW) +endif() + +project (tbb CXX) + +include(CheckCXXCompilerFlag) +include(CheckCXXSourceRuns) + +if(POLICY CMP0058) + cmake_policy(SET CMP0058 NEW) +endif() + +if(POLICY CMP0068) + cmake_policy(SET CMP0068 NEW) +endif() + +if (POLICY CMP0078) + # swig standard target names + cmake_policy(SET CMP0078 NEW) +endif () + +if (POLICY CMP0086) + # UseSWIG honors SWIG_MODULE_NAME via -module flag + cmake_policy(SET CMP0086 NEW) +endif () if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Release' as none was specified.") @@ -8,12 +35,36 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) "MinSizeRel" "RelWithDebInfo") endif() -include_directories(include src src/rml/include ) +if(NOT TBB_INSTALL_RUNTIME_DIR) + set(TBB_INSTALL_RUNTIME_DIR bin) +endif() +if(NOT TBB_INSTALL_LIBRARY_DIR) + set(TBB_INSTALL_LIBRARY_DIR lib) +endif() +if(NOT TBB_INSTALL_ARCHIVE_DIR) + set(TBB_INSTALL_ARCHIVE_DIR lib) +endif() +if(NOT TBB_INSTALL_INCLUDE_DIR) + set(TBB_INSTALL_INCLUDE_DIR include) +endif() +if(NOT TBB_CMAKE_PACKAGE_INSTALL_DIR) + set(TBB_CMAKE_PACKAGE_INSTALL_DIR lib/cmake/tbb) +endif() + +include_directories(include src src/rml/include ${CMAKE_CURRENT_BINARY_DIR}) option(TBB_BUILD_SHARED "Build TBB shared library" ON) option(TBB_BUILD_STATIC "Build TBB static library" ON) option(TBB_BUILD_TBBMALLOC "Build TBB malloc library" ON) option(TBB_BUILD_TBBMALLOC_PROXY "Build TBB malloc proxy library" ON) +option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" ON) +option(TBB_NO_DATE "Do not save the configure date in the version string" OFF) +option(TBB_BUILD_PYTHON "Build TBB Python bindings" OFF) +option(TBB_SET_SOVERSION "Set the SOVERSION (shared library build version suffix)?" OFF) + +# When this repository is part of a larger build system of a parent project +# we may not want TBB to set up default installation targets +option(TBB_INSTALL_TARGETS "Include build targets for 'make install'" ON) if(APPLE) set(CMAKE_MACOSX_RPATH ON) @@ -39,66 +90,143 @@ set(tbbmalloc_proxy_src src/tbbmalloc/proxy.cpp src/tbbmalloc/tbb_function_replacement.cpp) -if (NOT APPLE) - add_definitions(-DDO_ITT_NOTIFY) -else() +add_library (tbb_interface INTERFACE) +add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1) + +if (CMAKE_SYSTEM_PROCESSOR MATCHES "(i386|x86_64)") + if (NOT APPLE AND NOT MINGW) + target_compile_definitions(tbb_interface INTERFACE DO_ITT_NOTIFY) + endif() +endif() + +if (APPLE) # Disable annoying "has no symbols" warnings - set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>") - set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>") - set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>") - set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>") + set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>") + set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>") + set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>") + set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>") endif() -if (UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DUSE_PTHREAD") - if(NOT CMAKE_CXX_FLAGS MATCHES "-mno-rtm") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mrtm") - endif() - if (APPLE) - set(ARCH_PREFIX "mac") - else() - set(ARCH_PREFIX "lin") - endif() - if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(ARCH_PREFIX "${ARCH_PREFIX}64") +macro(CHECK_CXX_COMPILER_AND_LINKER_FLAGS _RESULT _CXX_FLAGS _LINKER_FLAGS) + set(CMAKE_REQUIRED_FLAGS ${_CXX_FLAGS}) + set(CMAKE_REQUIRED_LIBRARIES ${_LINKER_FLAGS}) + set(CMAKE_REQUIRED_QUIET TRUE) + check_cxx_source_runs("#include <iostream>\nint main(int argc, char **argv) { std::cout << \"test\"; return 0; }" ${_RESULT}) + set(CMAKE_REQUIRED_FLAGS "") + set(CMAKE_REQUIRED_LIBRARIES "") +endmacro() + +# Prefer libc++ in conjunction with Clang +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if (CMAKE_CXX_FLAGS MATCHES "-stdlib=libc\\+\\+") + message(STATUS "TBB: using libc++.") else() - set(ARCH_PREFIX "${ARCH_PREFIX}32") + CHECK_CXX_COMPILER_AND_LINKER_FLAGS(HAS_LIBCPP "-stdlib=libc++" "-stdlib=libc++") + if (HAS_LIBCPP) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -D_LIBCPP_VERSION") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++") + message(STATUS "TBB: using libc++.") + else() + message(STATUS "TBB: NOT using libc++.") + endif() endif() - set(ENABLE_RTTI "-frtti -fexceptions ") - set(DISABLE_RTTI "-fno-rtti -fno-exceptions ") +endif() + +set (CMAKE_CXX_STANDARD 11) + +if (UNIX) + target_compile_definitions(tbb_interface INTERFACE USE_PTHREAD) + + check_cxx_compiler_flag ("-mrtm -Werror" SUPPORTS_MRTM) + if (SUPPORTS_MRTM) + target_compile_options(tbb_interface INTERFACE "-mrtm") + endif () + elseif(WIN32) - cmake_minimum_required (VERSION 3.1) - enable_language(ASM_MASM) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS- /Zc:wchar_t /Zc:forScope /DUSE_WINTHREAD") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_CRT_SECURE_NO_DEPRECATE /D_WIN32_WINNT=0x0600 /volatile:iso") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4800 /wd4146 /wd4244 /wd4018") - - if (CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm - src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm) - list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm) - set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1") - set(ARCH_PREFIX "win64") - else() - list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm - src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm) - list(APPEND tbbmalloc_src src/tbb/ia32-masm/atomic_support.asm) - set(ARCH_PREFIX "win32") - endif() + target_compile_definitions(tbb_interface INTERFACE USE_WINTHREAD _WIN32_WINNT=0x0600) + if (MSVC) + enable_language(ASM_MASM) + target_compile_options(tbb_interface INTERFACE /GS- /Zc:wchar_t /Zc:forScope) + check_cxx_compiler_flag ("/volatile:iso" SUPPORTS_VOLATILE_FLAG) + if (SUPPORTS_VOLATILE_FLAG) + target_compile_options(tbb_interface INTERFACE /volatile:iso) + endif () + target_compile_options(tbb_interface INTERFACE $<$<COMPILE_LANGUAGE:CXX>:/wd4267 /wd4800 /wd4146 /wd4244 /wd4577 /wd4018>) + if (NOT CMAKE_SIZEOF_VOID_P) + message(FATAL_ERROR "'CMAKE_SIZEOF_VOID_P' is undefined. Please delete your build directory and rerun CMake again!") + endif() + + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm + src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm) + list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm) + set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1 ${CMAKE_ASM_MASM_FLAGS}") + else() + list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm + src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm) + # Enable SAFESEH feature for assembly (x86 builds only). + set(CMAKE_ASM_MASM_FLAGS "/safeseh ${CMAKE_ASM_MASM_FLAGS}") + endif() + elseif (MINGW) + target_compile_options(tbb_interface INTERFACE "-mthreads") + endif () +endif() + +if (MSVC) set(ENABLE_RTTI "/EHsc /GR ") set(DISABLE_RTTI "/EHs- /GR- ") +elseif (UNIX) + set(ENABLE_RTTI "-frtti -fexceptions ") + set(DISABLE_RTTI "-fno-rtti -fno-exceptions ") +endif () + +##-------- +# - Added TBB_USE_GLIBCXX_VERSION macro to specify the version of GNU +# libstdc++ when it cannot be properly recognized, e.g. when used +# with Clang on Linux* OS. Inspired by a contribution from David A. +if (NOT TBB_USE_GLIBCXX_VERSION AND UNIX AND NOT APPLE) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # using Clang + string(REPLACE "." "0" TBB_USE_GLIBCXX_VERSION ${CMAKE_CXX_COMPILER_VERSION}) + endif() endif() +if (TBB_USE_GLIBCXX_VERSION) + target_compile_definitions(tbb_interface INTERFACE TBB_USE_GLIBCXX_VERSION=${TBB_USE_GLIBCXX_VERSION}) +endif() + +##------- + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("-flifetime-dse=1" SUPPORTS_FLIFETIME) - if (SUPPORTS_FLIFETIME) - add_definitions(-flifetime-dse=1) - endif() + check_cxx_compiler_flag ("-flifetime-dse=1" SUPPORTS_FLIFETIME) + if (SUPPORTS_FLIFETIME) + target_compile_options(tbb_interface INTERFACE -flifetime-dse=1) + endif() endif() # Linker export definitions -if (WIN32) +if (APPLE) + set (ARCH_PREFIX "mac") +elseif(WIN32) + set (ARCH_PREFIX "win") +else() + set (ARCH_PREFIX "lin") +endif() + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(ARCH_PREFIX "${ARCH_PREFIX}64") +else() + set(ARCH_PREFIX "${ARCH_PREFIX}32") +endif() + +if (MINGW) + set (ARCH_PREFIX "${ARCH_PREFIX}-gcc") + # there's no win32-gcc-tbb-export.def, use lin32-tbb-export.def + execute_process (COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/lin32-tbb-export.def ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/win32-gcc-tbb-export.def) +endif() + +if (MSVC) add_custom_command(OUTPUT tbb.def COMMAND ${CMAKE_CXX_COMPILER} /TC /EP ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include > tbb.def MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def @@ -110,18 +238,15 @@ if (WIN32) MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def COMMENT "Preprocessing tbbmalloc.def" ) - list(APPEND tbb_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/tbb_resource.rc) - list(APPEND tbbmalloc_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc) - list(APPEND tbbmalloc_proxy_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc) else() add_custom_command(OUTPUT tbb.def - COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def + COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def COMMENT "Preprocessing tbb.def" ) add_custom_command(OUTPUT tbbmalloc.def - COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def + COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def COMMENT "Preprocessing tbbmalloc.def" ) @@ -132,34 +257,80 @@ add_custom_target(tbb_def_files DEPENDS tbb.def tbbmalloc.def) # TBB library if (TBB_BUILD_STATIC) add_library(tbb_static STATIC ${tbb_src}) - set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1") - set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1") + target_link_libraries(tbb_static PRIVATE tbb_interface) + target_include_directories(tbb_static INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>") set_property(TARGET tbb_static APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI}) - install(TARGETS tbb_static ARCHIVE DESTINATION lib) + if (TBB_INSTALL_TARGETS) + install(TARGETS tbb_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}) + endif() + + target_compile_definitions(tbb_static + PRIVATE + -D__TBB_BUILD=1 + -D__TBB_DYNAMIC_LOAD_ENABLED=0 + -D__TBB_SOURCE_DIRECTLY_INCLUDED=1) + + if (MSVC) + target_compile_definitions(tbb_static + PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1 + PRIVATE -D_CRT_SECURE_NO_WARNINGS) + endif() + + if (UNIX AND NOT APPLE) + target_link_libraries(tbb_static PUBLIC pthread dl) + endif() endif() if (TBB_BUILD_SHARED) add_library(tbb SHARED ${tbb_src}) - set_property(TARGET tbb APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1") + target_link_libraries(tbb PRIVATE tbb_interface) + target_include_directories(tbb INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>") set_property(TARGET tbb APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI}) + if (TBB_SET_SOVERSION) + set_property(TARGET tbb PROPERTY SOVERSION 2) + endif () + + target_compile_definitions(tbb + PRIVATE -D__TBB_BUILD=1) + + if (MSVC) + target_compile_definitions(tbb + PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1 + PRIVATE -D_CRT_SECURE_NO_WARNINGS) + endif() + add_dependencies(tbb tbb_def_files) + if (APPLE) - set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbb.def") - elseif(UNIX) - set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbb.def") - elseif(WIN32) - set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbb.def") - + set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"") + elseif (MSVC) + set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"") + else () + set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"") endif() - install(TARGETS tbb DESTINATION lib) - if(WIN32) - set_target_properties(tbb PROPERTIES OUTPUT_NAME "tbb$<$<CONFIG:Debug>:_debug>") + + if (TBB_INSTALL_TARGETS) + install(TARGETS tbb EXPORT TBB + LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR} + ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR} + RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR}) + if (MSVC) + install(FILES $<TARGET_PDB_FILE:tbb> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL) + endif() + endif() + + if (UNIX AND NOT APPLE) + target_link_libraries(tbb PUBLIC pthread dl) endif() endif() -if(CMAKE_COMPILER_IS_GNUCC) + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # Quench a warning on GCC set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/governor.cpp COMPILE_FLAGS "-Wno-missing-field-initializers ") +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # Quench a warning on Clang + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/itt_notify.cpp COMPILE_FLAGS "-Wno-varargs ") elseif(MSVC) # Quench a warning on MSVC set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/scheduler.cpp COMPILE_FLAGS "/wd4458 ") @@ -169,24 +340,50 @@ if(TBB_BUILD_TBBMALLOC) # TBB malloc library if (TBB_BUILD_STATIC) add_library(tbbmalloc_static STATIC ${tbbmalloc_static_src}) + target_link_libraries(tbbmalloc_static PRIVATE tbb_interface) set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1") + set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0") + set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1") set_property(TARGET tbbmalloc_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI}) - install(TARGETS tbbmalloc_static ARCHIVE DESTINATION lib) + if (MSVC) + target_compile_definitions(tbbmalloc_static PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1) + endif() + if (TBB_INSTALL_TARGETS) + install(TARGETS tbbmalloc_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}) + endif() endif() if (TBB_BUILD_SHARED) add_library(tbbmalloc SHARED ${tbbmalloc_src}) + target_link_libraries(tbbmalloc PRIVATE tbb_interface) set_property(TARGET tbbmalloc APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1") set_property(TARGET tbbmalloc APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI}) + if (TBB_SET_SOVERSION) + set_property(TARGET tbbmalloc PROPERTY SOVERSION 2) + endif () add_dependencies(tbbmalloc tbb_def_files) if (APPLE) - set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def") - elseif(UNIX) - set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def") - elseif(WIN32) - set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def") + set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"") + elseif (MSVC) + set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"") + else () + set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"") + endif() + if (MSVC) + target_compile_definitions(tbbmalloc PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1) + endif() + if (TBB_INSTALL_TARGETS) + install(TARGETS tbbmalloc EXPORT TBB + LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR} + ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR} + RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR}) + if (MSVC) + install(FILES $<TARGET_PDB_FILE:tbbmalloc> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL) + endif() + endif() + if (UNIX AND NOT APPLE) + target_link_libraries(tbbmalloc PUBLIC pthread dl) endif() - install(TARGETS tbbmalloc DESTINATION lib) endif() endif() @@ -194,19 +391,298 @@ if(TBB_BUILD_TBBMALLOC_PROXY) # TBB malloc proxy library if (TBB_BUILD_STATIC) add_library(tbbmalloc_proxy_static STATIC ${tbbmalloc_proxy_src}) + target_link_libraries(tbbmalloc_proxy_static PRIVATE tbb_interface) set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1") + set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0") + set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1") set_property(TARGET tbbmalloc_proxy_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI}) - link_libraries(tbbmalloc_proxy_static tbbmalloc) - install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION lib) + if (TBB_INSTALL_TARGETS) + install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}) + endif() endif() if (TBB_BUILD_SHARED) add_library(tbbmalloc_proxy SHARED ${tbbmalloc_proxy_src}) + target_link_libraries(tbbmalloc_proxy PRIVATE tbb_interface) set_property(TARGET tbbmalloc_proxy APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1") set_property(TARGET tbbmalloc_proxy APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI}) - target_link_libraries(tbbmalloc_proxy tbbmalloc) - install(TARGETS tbbmalloc_proxy DESTINATION lib) + if (TBB_SET_SOVERSION) + set_property(TARGET tbbmalloc_proxy PROPERTY SOVERSION 2) + endif () + target_link_libraries(tbbmalloc_proxy PUBLIC tbbmalloc) + if (TBB_INSTALL_TARGETS) + install(TARGETS tbbmalloc_proxy EXPORT TBB + LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR} + ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR} + RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR}) + if (MSVC) + install(FILES $<TARGET_PDB_FILE:tbbmalloc_proxy> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL) + endif() + endif() + if (UNIX AND NOT APPLE) + target_link_libraries(tbbmalloc_proxy PUBLIC pthread dl) + endif() endif() endif() -install(DIRECTORY include/tbb DESTINATION include) +if (TBB_INSTALL_TARGETS) + install(DIRECTORY include/tbb DESTINATION ${TBB_INSTALL_INCLUDE_DIR}) + if (TBB_BUILD_SHARED) + install(EXPORT TBB DESTINATION ${TBB_CMAKE_PACKAGE_INSTALL_DIR} NAMESPACE TBB:: FILE TBBConfig.cmake) + endif() +endif() + +# version file +if (TBB_INSTALL_TARGETS) + set (_VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/include/tbb/tbb_stddef.h) + file (STRINGS ${_VERSION_FILE} _VERSION_MAJOR_STRING REGEX ".*define[ ]+TBB_VERSION_MAJOR[ ]+[0-9]+.*") + file (STRINGS ${_VERSION_FILE} _VERSION_MINOR_STRING REGEX ".*define[ ]+TBB_VERSION_MINOR[ ]+[0-9]+.*") + string (REGEX REPLACE ".*TBB_VERSION_MAJOR[ ]+([0-9]+)" "\\1" TBB_MAJOR_VERSION ${_VERSION_MAJOR_STRING}) + string (REGEX REPLACE ".*TBB_VERSION_MINOR[ ]+([0-9]+)" "\\1" TBB_MINOR_VERSION ${_VERSION_MINOR_STRING}) + set (TBB_VERSION_STRING "${TBB_MAJOR_VERSION}.${TBB_MINOR_VERSION}") + include (CMakePackageConfigHelpers) + write_basic_package_version_file (TBBConfigVersion.cmake VERSION "${TBB_VERSION_STRING}" COMPATIBILITY AnyNewerVersion) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/TBBConfigVersion.cmake DESTINATION "${TBB_CMAKE_PACKAGE_INSTALL_DIR}") +endif() + +# version_string.ver +if (UNIX AND NOT TBB_NO_DATE) + execute_process (COMMAND date "+%a, %d %b %Y %H:%M:%S %z" + OUTPUT_VARIABLE _configure_date + OUTPUT_STRIP_TRAILING_WHITESPACE) +elseif (WIN32 AND NOT TBB_NO_DATE) + execute_process (COMMAND cmd " /C date /T" + OUTPUT_VARIABLE _configure_date + OUTPUT_STRIP_TRAILING_WHITESPACE) +else () + set (_configure_date "Unknown") +endif() +set (TBB_CONFIG_DATE "${_configure_date}" CACHE STRING "First time that TBB was configured") +set (_configure_date "${TBB_CONFIG_DATE}") +include_directories (${CMAKE_BINARY_DIR}) +configure_file (build/version_string.ver.in version_string.ver @ONLY) + +if (TBB_BUILD_TESTS) + enable_language (C) + enable_testing () + + find_library (LIBRT_LIBRARIES rt) + find_library (LIDL_LIBRARIES dl) + find_package (Threads) + if (NOT APPLE) + find_package (OpenMP) + endif() + + macro (tbb_add_test testname) + set (full_testname tbb_test_${testname}) + add_executable (${full_testname} src/test/test_${testname}.cpp) + target_link_libraries(${full_testname} PRIVATE tbb_interface) + if (TBB_BUILD_SHARED) + target_link_libraries (${full_testname} PRIVATE tbb tbbmalloc) + target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb) + else () + target_link_libraries (${full_testname} PRIVATE tbb_static tbbmalloc_static) + target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb_static) + endif () + if (LIBRT_LIBRARIES) + target_link_libraries (${full_testname} PRIVATE ${LIBRT_LIBRARIES}) + endif () + if (LIDL_LIBRARIES) + target_link_libraries (${full_testname} PRIVATE ${LIDL_LIBRARIES}) + endif () + if (Threads_FOUND) + target_link_libraries (${full_testname} PRIVATE ${CMAKE_THREAD_LIBS_INIT}) + endif () + if (OPENMP_FOUND AND "${testname}" MATCHES "openmp") + set_target_properties (${full_testname} PROPERTIES COMPILE_FLAGS "${OpenMP_CXX_FLAGS}") + set_target_properties (${full_testname} PROPERTIES LINK_FLAGS "${OpenMP_CXX_FLAGS}") + endif() + if (MINGW) + target_link_libraries (${full_testname} PRIVATE psapi) + endif () + add_test (NAME ${full_testname} COMMAND ${full_testname}) + endmacro () + + tbb_add_test (aggregator) + tbb_add_test (aligned_space) + tbb_add_test (assembly) + if (NOT WIN32) + tbb_add_test (async_msg) # msvc64/debug timeouts + endif() + tbb_add_test (async_node) + # tbb_add_test (atomic) # msvc64/debug timeouts: Compile-time initialization fails for static tbb::atomic variables + tbb_add_test (blocked_range2d) + tbb_add_test (blocked_range3d) + tbb_add_test (blocked_range) + tbb_add_test (broadcast_node) + tbb_add_test (buffer_node) + tbb_add_test (cache_aligned_allocator) + if (NOT WIN32) + tbb_add_test (cache_aligned_allocator_STL) + endif() + tbb_add_test (cilk_dynamic_load) + tbb_add_test (cilk_interop) + tbb_add_test (combinable) + tbb_add_test (composite_node) + tbb_add_test (concurrent_hash_map) + tbb_add_test (concurrent_lru_cache) + # tbb_add_test (concurrent_monitor) # too long + # tbb_add_test (concurrent_priority_queue) + if (NOT WIN32) + tbb_add_test (concurrent_queue) # msvc64/debug timeouts + endif() + # tbb_add_test (concurrent_queue_whitebox) + tbb_add_test (concurrent_unordered_map) + # tbb_add_test (concurrent_unordered_set) + tbb_add_test (concurrent_vector) + tbb_add_test (continue_node) + tbb_add_test (critical_section) + tbb_add_test (dynamic_link) + # tbb_add_test (eh_algorithms) + tbb_add_test (eh_flow_graph) + # tbb_add_test (eh_tasks) + tbb_add_test (enumerable_thread_specific) + tbb_add_test (examples_common_utility) + # tbb_add_test (fast_random) + tbb_add_test (flow_graph) + tbb_add_test (flow_graph_whitebox) + # tbb_add_test (fp) # mingw: harness_fp.h:66, assertion !checkConsistency || (ctl.mxcsr & SSE_RND_MODE_MASK) >> 3 == (ctl.x87cw & FE_RND_MODE_MASK): failed + # tbb_add_test (function_node) # mingw:random timeout + # tbb_add_test (global_control) + # tbb_add_test (global_control_whitebox) + tbb_add_test (halt) + tbb_add_test (handle_perror) + # tbb_add_test (hw_concurrency) + tbb_add_test (indexer_node) + tbb_add_test (inits_loop) + tbb_add_test (intrusive_list) + tbb_add_test (ittnotify) + # tbb_add_test (join_node) #msvc/64: fatal error C1128: number of sections exceeded object file format limit: compile with /bigob + tbb_add_test (lambda) + tbb_add_test (limiter_node) + # tbb_add_test (malloc_atexit) + # tbb_add_test (malloc_compliance) #mingw: Limits should be decreased for the test to work + tbb_add_test (malloc_init_shutdown) + # tbb_add_test (malloc_lib_unload) + # tbb_add_test (malloc_overload) + tbb_add_test (malloc_pools) + tbb_add_test (malloc_regression) + # tbb_add_test (malloc_used_by_lib) + # tbb_add_test (malloc_whitebox) + tbb_add_test (model_plugin) + # tbb_add_test (multifunction_node) # too long + tbb_add_test (mutex) + tbb_add_test (mutex_native_threads) + # tbb_add_test (opencl_node) + if (OPENMP_FOUND) + tbb_add_test (openmp) + endif () + tbb_add_test (overwrite_node) + # tbb_add_test (parallel_do) + # This seems to fail on CI platforms (AppVeyor/Travis), perhaps because the VM exposes just 1 core? + tbb_add_test (parallel_for) + tbb_add_test (parallel_for_each) + tbb_add_test (parallel_for_vectorization) + tbb_add_test (parallel_invoke) + tbb_add_test (parallel_pipeline) + tbb_add_test (parallel_reduce) + tbb_add_test (parallel_scan) + tbb_add_test (parallel_sort) + tbb_add_test (parallel_while) + # tbb_add_test (partitioner_whitebox) # too long + tbb_add_test (pipeline) + # tbb_add_test (pipeline_with_tbf) # takes forever on appveyor + tbb_add_test (priority_queue_node) + tbb_add_test (queue_node) + tbb_add_test (reader_writer_lock) + # tbb_add_test (runtime_loader) # LINK : fatal error LNK1104: cannot open file 'tbbproxy.lib' [C:\projects\tbb\test_runtime_loader.vcxproj] + tbb_add_test (rwm_upgrade_downgrade) + # tbb_add_test (ScalableAllocator) + if (NOT WIN32) + tbb_add_test (ScalableAllocator_STL) + endif() + tbb_add_test (semaphore) + # tbb_add_test (sequencer_node) # msvc: timeout + tbb_add_test (source_node) + tbb_add_test (split_node) + tbb_add_test (static_assert) + tbb_add_test (std_thread) + tbb_add_test (tagged_msg) + # tbb_add_test (task_arena) # LINK : fatal error LNK1104: cannot open file '__TBB_LIB_NAME.lib' [C:\projects\tbb\test_task_arena.vcxproj] + # tbb_add_test (task_assertions) + tbb_add_test (task_auto_init) + tbb_add_test (task) + # tbb_add_test (task_enqueue) # too long + tbb_add_test (task_group) + # tbb_add_test (task_leaks) + # tbb_add_test (task_priority) + # tbb_add_test (task_scheduler_init) # msvc: test_task_scheduler_init.cpp:68, assertion !test_mandatory_parallelism || Harness::CanReachConcurrencyLevel(threads): failed + tbb_add_test (task_scheduler_observer) + tbb_add_test (task_steal_limit) + tbb_add_test (tbb_condition_variable) + tbb_add_test (tbb_fork) + # tbb_add_test (tbb_header) + tbb_add_test (tbb_thread) + # tbb_add_test (tbb_version) + tbb_add_test (tick_count) + tbb_add_test (tuple) + tbb_add_test (write_once_node) + tbb_add_test (yield) +endif () + +if (TBB_BUILD_PYTHON) + find_package(PythonInterp) + find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT) + find_package(SWIG 3) + if (PythonLibs_FOUND AND SWIG_FOUND AND TBB_BUILD_SHARED) + include (${SWIG_USE_FILE}) + set_source_files_properties (python/tbb/api.i PROPERTIES CPLUSPLUS ON) + set (CMAKE_SWIG_FLAGS "-threads") + + # swig_add_module is deprecated + if (CMAKE_VERSION VERSION_LESS 3.8) + swig_add_module (api python python/tbb/api.i) + else () + swig_add_library (api LANGUAGE python SOURCES python/tbb/api.i) + endif () + + # UseSWIG generates now standard target names + if (CMAKE_VERSION VERSION_LESS 3.13) + set (module_target ${SWIG_MODULE_api_REAL_NAME}) + else () + set (module_target api) + endif () + + target_include_directories(${module_target} PRIVATE ${PYTHON_INCLUDE_DIRS}) + target_link_libraries(${module_target} PRIVATE tbb) + if(WIN32) + target_link_libraries(${module_target} ${PYTHON_LIBRARIES}) + elseif(APPLE) + set_target_properties(${module_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") + endif() + + if (WIN32) + set (PYTHON_SITE_PACKAGES Lib/site-packages) + else () + set (PYTHON_SITE_PACKAGES lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages) + endif () + if (TBB_INSTALL_TARGETS) + install(FILES python/TBB.py + DESTINATION ${PYTHON_SITE_PACKAGES}) + install(FILES python/tbb/__init__.py python/tbb/pool.py python/tbb/test.py python/tbb/__main__.py ${CMAKE_CURRENT_BINARY_DIR}/api.py + DESTINATION ${PYTHON_SITE_PACKAGES}/tbb) + install(TARGETS ${module_target} + DESTINATION ${PYTHON_SITE_PACKAGES}/tbb) + endif() + + if(UNIX AND NOT APPLE) + add_library(irml SHARED python/rml/ipc_server.cpp python/rml/ipc_utils.cpp src/tbb/cache_aligned_allocator.cpp src/tbb/dynamic_link.cpp src/tbb/tbb_misc_ex.cpp src/tbb/tbb_misc.cpp) + target_compile_definitions(irml PRIVATE DO_ITT_NOTIFY=0 USE_PTHREAD=1) + target_link_libraries(irml PRIVATE tbb) + set_target_properties(irml PROPERTIES VERSION 1) + if (TBB_INSTALL_TARGETS) + install(TARGETS irml DESTINATION ${TBB_INSTALL_LIBRARY_DIR}) + endif() + endif () + endif () +endif () diff --git a/build_files/build_environment/patches/tbb.diff b/build_files/build_environment/patches/tbb.diff index c05c35bca95..07f70aa7007 100644 --- a/build_files/build_environment/patches/tbb.diff +++ b/build_files/build_environment/patches/tbb.diff @@ -10,4 +10,15 @@ index 7a8d06a0..886699d8 100644 + #if (__cplusplus >= 201402L && (!defined(_MSC_VER) || _MSC_VER >= 1920)) #define __TBB_DEPRECATED [[deprecated]] #define __TBB_DEPRECATED_MSG(msg) [[deprecated(msg)]] - #elif _MSC_VER
\ No newline at end of file + #elif _MSC_VER +--- a/src/tbb/tools_api/ittnotify_config.h ++++ b/src/tbb/tools_api/ittnotify_config.h +@@ -162,7 +162,7 @@ + # define ITT_ARCH ITT_ARCH_IA32E + # elif defined _M_IA64 || defined __ia64__ + # define ITT_ARCH ITT_ARCH_IA64 +-# elif defined _M_ARM || defined __arm__ ++# elif defined _M_ARM || defined __arm__ || defined __aarch64__ + # define ITT_ARCH ITT_ARCH_ARM + # elif defined __powerpc64__ + # define ITT_ARCH ITT_ARCH_PPC64 diff --git a/build_files/build_environment/patches/theora.diff b/build_files/build_environment/patches/theora.diff index 3abadb66be9..4436577816e 100644 --- a/build_files/build_environment/patches/theora.diff +++ b/build_files/build_environment/patches/theora.diff @@ -4,7 +4,7 @@ # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ -+ | aarch64 \ ++ | aarch64 \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ @@ -12,7 +12,7 @@ # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ -+ | aarch64-* \ ++ | aarch64-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ diff --git a/build_files/build_environment/patches/usd.diff b/build_files/build_environment/patches/usd.diff index 42527a4f443..dc4982ad114 100644 --- a/build_files/build_environment/patches/usd.diff +++ b/build_files/build_environment/patches/usd.diff @@ -53,3 +53,147 @@ diff -ru USD-20.11/pxr/base/tf/pxrLZ4/lz4.cpp external_usd/pxr/base/tf/pxrLZ4/lz /*-****************************** * Compression functions + +From 442d087962f762deeb8b6e49a0955753fcf9aeb9 Mon Sep 17 00:00:00 2001 +From: Tsahi Zidenberg <tsahee@amazon.com> +Date: Sun, 15 Nov 2020 15:18:24 +0000 +Subject: [PATCH 1/2] stackTrace: support aarch64/linux + +stacktrace calls syscall directly via assembler. Create compatible +aarch64 code. +--- + pxr/base/arch/stackTrace.cpp | 30 ++++++++++++++++++++++++------ + 1 file changed, 24 insertions(+), 6 deletions(-) + +diff --git a/pxr/base/arch/stackTrace.cpp b/pxr/base/arch/stackTrace.cpp +index dcc1dfd46..c11aabeb1 100644 +--- a/pxr/base/arch/stackTrace.cpp ++++ b/pxr/base/arch/stackTrace.cpp +@@ -583,7 +583,6 @@ nonLockingLinux__execve (const char *file, + char *const argv[], + char *const envp[]) + { +-#if defined(ARCH_BITS_64) + /* + * We make a direct system call here, because we can't find an + * execve which corresponds with the non-locking fork we call +@@ -594,7 +593,27 @@ nonLockingLinux__execve (const char *file, + * hangs in a threaded app. (We use the non-locking fork to get + * around problems with forking when we have had memory + * corruption.) whew. +- * ++ */ ++ ++ unsigned long result; ++ ++#if defined (__aarch64__) ++ { ++ register long __file_result asm ("x0") = (long)file; ++ register char* const* __argv asm ("x1") = argv; ++ register char* const* __envp asm ("x2") = envp; ++ register long __num_execve asm ("x8") = 221; ++ __asm__ __volatile__ ( ++ "svc 0" ++ : "=r" (__file_result) ++ : "r"(__num_execve), "r" (__file_result), "r" (__argv), "r" (__envp) ++ : "memory" ++ ); ++ result = __file_result; ++ } ++#elif defined(ARCH_CPU_INTEL) && defined(ARCH_BITS_64) ++ ++ /* + * %rdi, %rsi, %rdx, %rcx, %r8, %r9 are args 0-5 + * syscall clobbers %rcx and %r11 + * +@@ -603,7 +622,6 @@ nonLockingLinux__execve (const char *file, + * constraints to gcc. + */ + +- unsigned long result; + __asm__ __volatile__ ( + "mov %0, %%rdi \n\t" + "mov %%rcx, %%rsi \n\t" +@@ -614,6 +632,9 @@ nonLockingLinux__execve (const char *file, + : "0" (file), "c" (argv), "d" (envp) + : "memory", "cc", "r11" + ); ++#else ++#error Unknown architecture ++#endif + + if (result >= 0xfffffffffffff000) { + errno = -result; +@@ -621,9 +642,6 @@ nonLockingLinux__execve (const char *file, + } + + return result; +-#else +-#error Unknown architecture +-#endif + } + + #endif + +From a1dffe02519bb3c6ccbbe8c6c58304da5db98995 Mon Sep 17 00:00:00 2001 +From: Tsahi Zidenberg <tsahee@amazon.com> +Date: Sun, 15 Nov 2020 15:22:52 +0000 +Subject: [PATCH 2/2] timing: support aarch64/linux + +The aarch64 arch-timer is directly accessible to userspace via two +registers: +CNTVCT_EL0 - holds the current counter value +CNTFRQ_EL0 - holds the counter frequency (in Hz) +--- + pxr/base/arch/timing.cpp | 6 ++++++ + pxr/base/arch/timing.h | 6 +++++- + 2 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/pxr/base/arch/timing.cpp b/pxr/base/arch/timing.cpp +index 27ad58fed..9022950c1 100644 +--- a/pxr/base/arch/timing.cpp ++++ b/pxr/base/arch/timing.cpp +@@ -59,6 +59,11 @@ ARCH_HIDDEN + void + Arch_InitTickTimer() + { ++#ifdef __aarch64__ ++ uint64_t counter_hz; ++ __asm __volatile("mrs %0, CNTFRQ_EL0" : "=&r" (counter_hz)); ++ Arch_NanosecondsPerTick = double(1e9) / double(counter_hz); ++#else + // NOTE: Normally ifstream would be cleaner, but it causes crashes when + // used in conjunction with DSOs and the Intel Compiler. + FILE *in; +@@ -135,6 +140,7 @@ Arch_InitTickTimer() + } + + Arch_NanosecondsPerTick = double(1e9) / double(cpuHz); ++#endif + } + #elif defined(ARCH_OS_WINDOWS) + +diff --git a/pxr/base/arch/timing.h b/pxr/base/arch/timing.h +index 67ec0d15f..6dc3e85a0 100644 +--- a/pxr/base/arch/timing.h ++++ b/pxr/base/arch/timing.h +@@ -36,7 +36,7 @@ + /// \addtogroup group_arch_SystemFunctions + ///@{ + +-#if defined(ARCH_OS_LINUX) ++#if defined(ARCH_OS_LINUX) && defined(ARCH_CPU_INTEL) + #include <x86intrin.h> + #elif defined(ARCH_OS_DARWIN) + #include <mach/mach_time.h> +@@ -69,6 +69,10 @@ ArchGetTickTime() + #elif defined(ARCH_CPU_INTEL) + // On Intel we'll use the rdtsc instruction. + return __rdtsc(); ++#elif defined (__aarch64__) ++ uint64_t result; ++ __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (result)); ++ return result; + #else + #error Unknown architecture. + #endif diff --git a/build_files/buildbot/buildbot_utils.py b/build_files/buildbot/buildbot_utils.py index 057c2155fb6..7e9858d9268 100644 --- a/build_files/buildbot/buildbot_utils.py +++ b/build_files/buildbot/buildbot_utils.py @@ -85,8 +85,8 @@ class VersionInfo: version_number = int(self._parse_header_file(blender_h, 'BLENDER_VERSION')) version_number_patch = int(self._parse_header_file(blender_h, 'BLENDER_VERSION_PATCH')) version_numbers = (version_number // 100, version_number % 100, version_number_patch) - self.short_version = "%d.%02d" % (version_numbers[0], version_numbers[1]) - self.version = "%d.%02d.%d" % version_numbers + self.short_version = "%d.%d" % (version_numbers[0], version_numbers[1]) + self.version = "%d.%d.%d" % version_numbers self.version_cycle = self._parse_header_file(blender_h, 'BLENDER_VERSION_CYCLE') self.hash = self._parse_header_file(buildinfo_h, 'BUILD_HASH')[1:-1] diff --git a/build_files/cmake/Modules/FindEmbree.cmake b/build_files/cmake/Modules/FindEmbree.cmake index 7f7f2ae0fb3..e6105a885f3 100644 --- a/build_files/cmake/Modules/FindEmbree.cmake +++ b/build_files/cmake/Modules/FindEmbree.cmake @@ -34,7 +34,7 @@ FIND_PATH(EMBREE_INCLUDE_DIR include ) -IF(NOT (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))) +IF(NOT (("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") OR (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")))) SET(_embree_SIMD_COMPONENTS embree_sse42 embree_avx diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 91881441c95..813ac013cdf 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -923,10 +923,6 @@ function(get_blender_version) math(EXPR _out_version_major "${_out_version} / 100") math(EXPR _out_version_minor "${_out_version} % 100") - # Zero pad the minor version so `_out_version_minor` is always two characters. - # This is needed if the minor version is a single digit. - string(REGEX REPLACE "^([0-9])$" "0\\1" _out_version_minor "${_out_version_minor}") - # output vars set(BLENDER_VERSION "${_out_version_major}.${_out_version_minor}" PARENT_SCOPE) set(BLENDER_VERSION_MAJOR "${_out_version_major}" PARENT_SCOPE) diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index f9055f02bcf..201d2208486 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "V2.93" +PROJECT_NUMBER = "V3.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/python_api/examples/gpu.4.py b/doc/python_api/examples/gpu.4.py index e05290a9442..b1a1ba827db 100644 --- a/doc/python_api/examples/gpu.4.py +++ b/doc/python_api/examples/gpu.4.py @@ -4,7 +4,6 @@ Mesh with Random Vertex Colors """ import bpy import gpu -import bgl import numpy as np from random import random from gpu_extras.batch import batch_for_shader @@ -31,9 +30,10 @@ batch = batch_for_shader( def draw(): - bgl.glEnable(bgl.GL_DEPTH_TEST) + gpu.state.depth_test_set('LESS_EQUAL') + gpu.state.depth_mask_set(True) batch.draw(shader) - bgl.glDisable(bgl.GL_DEPTH_TEST) + gpu.state.depth_mask_set(False) bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW') diff --git a/doc/python_api/examples/gpu.6.py b/doc/python_api/examples/gpu.6.py index 69af65e163e..334a606055a 100644 --- a/doc/python_api/examples/gpu.6.py +++ b/doc/python_api/examples/gpu.6.py @@ -6,11 +6,11 @@ To use this example you have to provide an image that should be displayed. """ import bpy import gpu -import bgl from gpu_extras.batch import batch_for_shader IMAGE_NAME = "Untitled" image = bpy.data.images[IMAGE_NAME] +texture = gpu.texture.from_image(image) shader = gpu.shader.from_builtin('2D_IMAGE') batch = batch_for_shader( @@ -21,16 +21,9 @@ batch = batch_for_shader( }, ) -if image.gl_load(): - raise Exception() - - def draw(): - bgl.glActiveTexture(bgl.GL_TEXTURE0) - bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode) - shader.bind() - shader.uniform_int("image", 0) + shader.uniform_sampler("image", texture) batch.draw(shader) diff --git a/doc/python_api/examples/gpu.7.py b/doc/python_api/examples/gpu.7.py index 80cda45e690..01082e7b6fe 100644 --- a/doc/python_api/examples/gpu.7.py +++ b/doc/python_api/examples/gpu.7.py @@ -9,7 +9,6 @@ Generate a texture using Offscreen Rendering """ import bpy import gpu -import bgl from mathutils import Matrix from gpu_extras.batch import batch_for_shader from gpu_extras.presets import draw_circle_2d @@ -20,8 +19,8 @@ from gpu_extras.presets import draw_circle_2d offscreen = gpu.types.GPUOffScreen(512, 512) with offscreen.bind(): - bgl.glClearColor(0.0, 0.0, 0.0, 0.0) - bgl.glClear(bgl.GL_COLOR_BUFFER_BIT) + fb = gpu.state.active_framebuffer_get() + fb.clear(color=(0.0, 0.0, 0.0, 0.0)) with gpu.matrix.push_pop(): # reset matrices -> use normalized device coordinates [-1, 1] gpu.matrix.load_matrix(Matrix.Identity(4)) @@ -75,13 +74,10 @@ batch = batch_for_shader( def draw(): - bgl.glActiveTexture(bgl.GL_TEXTURE0) - bgl.glBindTexture(bgl.GL_TEXTURE_2D, offscreen.color_texture) - shader.bind() shader.uniform_float("modelMatrix", Matrix.Translation((1, 2, 3)) @ Matrix.Scale(3, 4)) shader.uniform_float("viewProjectionMatrix", bpy.context.region_data.perspective_matrix) - shader.uniform_float("image", 0) + shader.uniform_sampler("image", offscreen.texture_color) batch.draw(shader) diff --git a/doc/python_api/examples/gpu.8.py b/doc/python_api/examples/gpu.8.py index e67c601def9..664f14a23ca 100644 --- a/doc/python_api/examples/gpu.8.py +++ b/doc/python_api/examples/gpu.8.py @@ -7,11 +7,10 @@ If it already exists, it will override the existing one. Currently almost all of the execution time is spent in the last line. In the future this will hopefully be solved by implementing the Python buffer protocol -for :class:`bgl.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``). +for :class:`gpu.types.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``). """ import bpy import gpu -import bgl import random from mathutils import Matrix from gpu_extras.presets import draw_circle_2d @@ -25,8 +24,8 @@ RING_AMOUNT = 10 offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT) with offscreen.bind(): - bgl.glClearColor(0.0, 0.0, 0.0, 0.0) - bgl.glClear(bgl.GL_COLOR_BUFFER_BIT) + fb = gpu.state.active_framebuffer_get() + fb.clear(color=(0.0, 0.0, 0.0, 0.0)) with gpu.matrix.push_pop(): # reset matrices -> use normalized device coordinates [-1, 1] gpu.matrix.load_matrix(Matrix.Identity(4)) @@ -37,9 +36,7 @@ with offscreen.bind(): (random.uniform(-1, 1), random.uniform(-1, 1)), (1, 1, 1, 1), random.uniform(0.1, 1), 20) - buffer = bgl.Buffer(bgl.GL_BYTE, WIDTH * HEIGHT * 4) - bgl.glReadBuffer(bgl.GL_BACK) - bgl.glReadPixels(0, 0, WIDTH, HEIGHT, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer) + buffer = fb.read_color(0, 0, WIDTH, HEIGHT, 4, 0, 'UBYTE') offscreen.free() @@ -48,4 +45,6 @@ if not IMAGE_NAME in bpy.data.images: bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT) image = bpy.data.images[IMAGE_NAME] image.scale(WIDTH, HEIGHT) + +buffer.dimensions = WIDTH * HEIGHT * 4 image.pixels = [v / 255 for v in buffer] diff --git a/doc/python_api/examples/gpu.9.py b/doc/python_api/examples/gpu.9.py index a4db576ecc0..e358cb517bd 100644 --- a/doc/python_api/examples/gpu.9.py +++ b/doc/python_api/examples/gpu.9.py @@ -7,7 +7,6 @@ You could also make this independent of a specific camera, but Blender does not expose good functions to create view and projection matrices yet. """ import bpy -import bgl import gpu from gpu_extras.presets import draw_texture_2d @@ -34,8 +33,8 @@ def draw(): view_matrix, projection_matrix) - bgl.glDisable(bgl.GL_DEPTH_TEST) - draw_texture_2d(offscreen.color_texture, (10, 10), WIDTH, HEIGHT) + gpu.state.depth_mask_set(False) + draw_texture_2d(offscreen.texture_color, (10, 10), WIDTH, HEIGHT) bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL') diff --git a/doc/python_api/rst/info_gotcha.rst b/doc/python_api/rst/info_gotcha.rst index e5ff56063b5..df6e7297e26 100644 --- a/doc/python_api/rst/info_gotcha.rst +++ b/doc/python_api/rst/info_gotcha.rst @@ -50,7 +50,8 @@ you should be able to find the poll function with no knowledge of C. Blender does have the functionality for poll functions to describe why they fail, but its currently not used much, if you're interested to help improve the API - feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g: + feel free to add calls to :class:`bpy.types.Operator.poll_message_set` (``CTX_wm_operator_poll_msg_set`` in C) + where its not obvious why poll fails, e.g: >>> bpy.ops.gpencil.draw() RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index 67b852013f3..7a1e5d62dd2 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -71,6 +71,16 @@ if(WITH_CYCLES_STANDALONE) target_link_libraries(cycles ${LIBRARIES}) cycles_target_link_libraries(cycles) + if(APPLE) + if(WITH_OPENCOLORIO) + set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit") + endif() + if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") + # OpenImageDenoise uses BNNS from the Accelerate framework. + set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework Accelerate") + endif() + endif() + if(UNIX AND NOT APPLE) set_target_properties(cycles PROPERTIES INSTALL_RPATH $ORIGIN/lib) endif() diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 54128cf82fc..19f49acddd7 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -560,10 +560,12 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, if (!cancel && !motion) { sync_background_light(b_v3d, use_portal); - /* handle removed data and modified pointers */ + /* Handle removed data and modified pointers, as this may free memory, delete Nodes in the + * right order to ensure that dependent data is freed after their users. Objects should be + * freed before particle systems and geometries. */ light_map.post_sync(); - geometry_map.post_sync(); object_map.post_sync(); + geometry_map.post_sync(); particle_system_map.post_sync(); } diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index fcf8fab9cc4..cce11507fa1 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -955,14 +955,21 @@ class OptiXDevice : public CUDADevice { // Create OptiX denoiser handle on demand when it is first used OptixDenoiserOptions denoiser_options = {}; assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3); +# if OPTIX_ABI_VERSION >= 47 + denoiser_options.guideAlbedo = task.denoising.input_passes >= 2; + denoiser_options.guideNormal = task.denoising.input_passes >= 3; + check_result_optix_ret(optixDenoiserCreate( + context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser)); +# else denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>( OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1)); -# if OPTIX_ABI_VERSION < 28 +# if OPTIX_ABI_VERSION < 28 denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3; -# endif +# endif check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser)); check_result_optix_ret( optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0)); +# endif // OptiX denoiser handle was created with the requested number of input passes denoiser_input_passes = task.denoising.input_passes; @@ -1032,10 +1039,34 @@ class OptiXDevice : public CUDADevice { # endif output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3; +# if OPTIX_ABI_VERSION >= 47 + OptixDenoiserLayer image_layers = {}; + image_layers.input = input_layers[0]; + image_layers.output = output_layers[0]; + + OptixDenoiserGuideLayer guide_layers = {}; + guide_layers.albedo = input_layers[1]; + guide_layers.normal = input_layers[2]; +# endif + // Finally run denonising OptixDenoiserParams params = {}; // All parameters are disabled/zero +# if OPTIX_ABI_VERSION >= 47 check_result_optix_ret(optixDenoiserInvoke(denoiser, - 0, + NULL, + ¶ms, + denoiser_state.device_pointer, + scratch_offset, + &guide_layers, + &image_layers, + 1, + overlap_offset.x, + overlap_offset.y, + denoiser_state.device_pointer + scratch_offset, + scratch_size)); +# else + check_result_optix_ret(optixDenoiserInvoke(denoiser, + NULL, ¶ms, denoiser_state.device_pointer, scratch_offset, @@ -1046,6 +1077,7 @@ class OptiXDevice : public CUDADevice { output_layers, denoiser_state.device_pointer + scratch_offset, scratch_size)); +# endif # if OPTIX_DENOISER_NO_PIXEL_STRIDE void *output_args[] = {&input_ptr, diff --git a/intern/cycles/graph/node.cpp b/intern/cycles/graph/node.cpp index c926f6ab8ef..57f25283f85 100644 --- a/intern/cycles/graph/node.cpp +++ b/intern/cycles/graph/node.cpp @@ -367,9 +367,17 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT case SocketType::TRANSFORM_ARRAY: copy_array<Transform>(this, socket, &other, other_socket); break; - case SocketType::NODE_ARRAY: + case SocketType::NODE_ARRAY: { copy_array<void *>(this, socket, &other, other_socket); + + array<Node *> &node_array = get_socket_value<array<Node *>>(this, socket); + + for (Node *node : node_array) { + node->reference(); + } + break; + } default: assert(0); break; @@ -379,6 +387,14 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT const void *src = ((char *)&other) + other_socket.struct_offset; void *dst = ((char *)this) + socket.struct_offset; memcpy(dst, src, socket.size()); + + if (socket.type == SocketType::NODE) { + Node *node = get_socket_value<Node *>(this, socket); + + if (node) { + node->reference(); + } + } } } @@ -773,6 +789,26 @@ void Node::set_owner(const NodeOwner *owner_) owner = owner_; } +void Node::dereference_all_used_nodes() +{ + foreach (const SocketType &socket, type->inputs) { + if (socket.type == SocketType::NODE) { + Node *node = get_socket_value<Node *>(this, socket); + + if (node) { + node->dereference(); + } + } + else if (socket.type == SocketType::NODE_ARRAY) { + const array<Node *> &nodes = get_socket_value<array<Node *>>(this, socket); + + for (Node *node : nodes) { + node->dereference(); + } + } + } +} + bool Node::socket_is_modified(const SocketType &input) const { return (socket_modified & input.modified_flag_bit) != 0; @@ -803,6 +839,25 @@ template<typename T> void Node::set_if_different(const SocketType &input, T valu socket_modified |= input.modified_flag_bit; } +void Node::set_if_different(const SocketType &input, Node *value) +{ + if (get_socket_value<Node *>(this, input) == value) { + return; + } + + Node *old_node = get_socket_value<Node *>(this, input); + if (old_node) { + old_node->dereference(); + } + + if (value) { + value->reference(); + } + + get_socket_value<Node *>(this, input) = value; + socket_modified |= input.modified_flag_bit; +} + template<typename T> void Node::set_if_different(const SocketType &input, array<T> &value) { if (!socket_is_modified(input)) { @@ -815,6 +870,27 @@ template<typename T> void Node::set_if_different(const SocketType &input, array< socket_modified |= input.modified_flag_bit; } +void Node::set_if_different(const SocketType &input, array<Node *> &value) +{ + if (!socket_is_modified(input)) { + if (get_socket_value<array<Node *>>(this, input) == value) { + return; + } + } + + array<Node *> &old_nodes = get_socket_value<array<Node *>>(this, input); + for (Node *old_node : old_nodes) { + old_node->dereference(); + } + + for (Node *new_node : value) { + new_node->reference(); + } + + get_socket_value<array<Node *>>(this, input).steal_data(value); + socket_modified |= input.modified_flag_bit; +} + void Node::print_modified_sockets() const { printf("Node : %s\n", name.c_str()); diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h index 2fc9a1e0281..aa365baeccd 100644 --- a/intern/cycles/graph/node.h +++ b/intern/cycles/graph/node.h @@ -177,8 +177,32 @@ struct Node { const NodeOwner *get_owner() const; void set_owner(const NodeOwner *owner_); + int reference_count() const + { + return ref_count; + } + + void reference() + { + ref_count += 1; + } + + void dereference() + { + ref_count -= 1; + } + + /* Set the reference count to zero. This should only be called when we know for sure that the + * Node is not used by anyone else. For now, this is only the case when "deleting" shaders, as + * they are never actually deleted. */ + void clear_reference_count() + { + ref_count = 0; + } + protected: const NodeOwner *owner; + int ref_count{0}; template<typename T> static T &get_socket_value(const Node *node, const SocketType &socket) { @@ -189,7 +213,19 @@ struct Node { template<typename T> void set_if_different(const SocketType &input, T value); + /* Explicit overload for Node sockets so we can handle reference counting. The old Node is + * dereferenced, and the new one is referenced. */ + void set_if_different(const SocketType &input, Node *value); + template<typename T> void set_if_different(const SocketType &input, array<T> &value); + + /* Explicit overload for Node sockets so we can handle reference counting. The old Nodes are + * dereferenced, and the new ones are referenced. */ + void set_if_different(const SocketType &input, array<Node *> &value); + + /* Call this function in derived classes' destructors to ensure that used Nodes are dereferenced + * properly. */ + void dereference_all_used_nodes(); }; CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index f6b4b963a7a..ea0f16c9233 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -93,6 +93,7 @@ set(SRC_BVH_HEADERS bvh/bvh_local.h bvh/bvh_traversal.h bvh/bvh_types.h + bvh/bvh_util.h bvh/bvh_volume.h bvh/bvh_volume_all.h bvh/bvh_embree.h diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 3049f243ae9..3a3f38539c5 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -29,9 +29,10 @@ # include "kernel/bvh/bvh_embree.h" #endif -CCL_NAMESPACE_BEGIN - #include "kernel/bvh/bvh_types.h" +#include "kernel/bvh/bvh_util.h" + +CCL_NAMESPACE_BEGIN #ifndef __KERNEL_OPTIX__ @@ -533,97 +534,4 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg, } #endif /* __VOLUME_RECORD_ALL__ */ -/* Ray offset to avoid self intersection. - * - * This function should be used to compute a modified ray start position for - * rays leaving from a surface. */ - -ccl_device_inline float3 ray_offset(float3 P, float3 Ng) -{ -#ifdef __INTERSECTION_REFINE__ - const float epsilon_f = 1e-5f; - /* ideally this should match epsilon_f, but instancing and motion blur - * precision makes it problematic */ - const float epsilon_test = 1.0f; - const int epsilon_i = 32; - - float3 res; - - /* x component */ - if (fabsf(P.x) < epsilon_test) { - res.x = P.x + Ng.x * epsilon_f; - } - else { - uint ix = __float_as_uint(P.x); - ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i; - res.x = __uint_as_float(ix); - } - - /* y component */ - if (fabsf(P.y) < epsilon_test) { - res.y = P.y + Ng.y * epsilon_f; - } - else { - uint iy = __float_as_uint(P.y); - iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i; - res.y = __uint_as_float(iy); - } - - /* z component */ - if (fabsf(P.z) < epsilon_test) { - res.z = P.z + Ng.z * epsilon_f; - } - else { - uint iz = __float_as_uint(P.z); - iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i; - res.z = __uint_as_float(iz); - } - - return res; -#else - const float epsilon_f = 1e-4f; - return P + epsilon_f * Ng; -#endif -} - -#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__)) -/* ToDo: Move to another file? */ -ccl_device int intersections_compare(const void *a, const void *b) -{ - const Intersection *isect_a = (const Intersection *)a; - const Intersection *isect_b = (const Intersection *)b; - - if (isect_a->t < isect_b->t) - return -1; - else if (isect_a->t > isect_b->t) - return 1; - else - return 0; -} -#endif - -#if defined(__SHADOW_RECORD_ALL__) -ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) -{ -# ifdef __KERNEL_GPU__ - /* Use bubble sort which has more friendly memory pattern on GPU. */ - bool swapped; - do { - swapped = false; - for (int j = 0; j < num_hits - 1; ++j) { - if (hits[j].t > hits[j + 1].t) { - struct Intersection tmp = hits[j]; - hits[j] = hits[j + 1]; - hits[j + 1] = tmp; - swapped = true; - } - } - --num_hits; - } while (swapped); -# else - qsort(hits, num_hits, sizeof(Intersection), intersections_compare); -# endif -} -#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */ - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index dccd257d2de..2e94b1d7c37 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -180,25 +180,10 @@ ccl_device_inline /* todo: optimize so primitive visibility flag indicates if * the primitive has a transparent shadow shader? */ - int prim = kernel_tex_fetch(__prim_index, isect_array->prim); - int shader = 0; - -#ifdef __HAIR__ - if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE) -#endif - { - shader = kernel_tex_fetch(__tri_shader, prim); - } -#ifdef __HAIR__ - else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } -#endif - int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; + const int flags = intersection_get_shader_flags(kg, isect_array); /* if no transparent shadows, all light is blocked */ - if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) { + if (!(flags & SD_HAS_TRANSPARENT_SHADOW)) { return true; } /* if maximum number of hits reached, block all light */ diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h new file mode 100644 index 00000000000..a694e4dc259 --- /dev/null +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -0,0 +1,162 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Ray offset to avoid self intersection. + * + * This function should be used to compute a modified ray start position for + * rays leaving from a surface. */ + +ccl_device_inline float3 ray_offset(float3 P, float3 Ng) +{ +#ifdef __INTERSECTION_REFINE__ + const float epsilon_f = 1e-5f; + /* ideally this should match epsilon_f, but instancing and motion blur + * precision makes it problematic */ + const float epsilon_test = 1.0f; + const int epsilon_i = 32; + + float3 res; + + /* x component */ + if (fabsf(P.x) < epsilon_test) { + res.x = P.x + Ng.x * epsilon_f; + } + else { + uint ix = __float_as_uint(P.x); + ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i; + res.x = __uint_as_float(ix); + } + + /* y component */ + if (fabsf(P.y) < epsilon_test) { + res.y = P.y + Ng.y * epsilon_f; + } + else { + uint iy = __float_as_uint(P.y); + iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i; + res.y = __uint_as_float(iy); + } + + /* z component */ + if (fabsf(P.z) < epsilon_test) { + res.z = P.z + Ng.z * epsilon_f; + } + else { + uint iz = __float_as_uint(P.z); + iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i; + res.z = __uint_as_float(iz); + } + + return res; +#else + const float epsilon_f = 1e-4f; + return P + epsilon_f * Ng; +#endif +} + +#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__)) +/* ToDo: Move to another file? */ +ccl_device int intersections_compare(const void *a, const void *b) +{ + const Intersection *isect_a = (const Intersection *)a; + const Intersection *isect_b = (const Intersection *)b; + + if (isect_a->t < isect_b->t) + return -1; + else if (isect_a->t > isect_b->t) + return 1; + else + return 0; +} +#endif + +#if defined(__SHADOW_RECORD_ALL__) +ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) +{ + kernel_assert(num_hits > 0); + +# ifdef __KERNEL_GPU__ + /* Use bubble sort which has more friendly memory pattern on GPU. */ + bool swapped; + do { + swapped = false; + for (int j = 0; j < num_hits - 1; ++j) { + if (hits[j].t > hits[j + 1].t) { + struct Intersection tmp = hits[j]; + hits[j] = hits[j + 1]; + hits[j + 1] = tmp; + swapped = true; + } + } + --num_hits; + } while (swapped); +# else + qsort(hits, num_hits, sizeof(Intersection), intersections_compare); +# endif +} +#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */ + +/* Utility to quickly get a shader flags from an intersection. */ + +ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_restrict kg, + const Intersection *isect) +{ + const int prim = kernel_tex_fetch(__prim_index, isect->prim); + int shader = 0; + +#ifdef __HAIR__ + if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) +#endif + { + shader = kernel_tex_fetch(__tri_shader, prim); + } +#ifdef __HAIR__ + else { + float4 str = kernel_tex_fetch(__curves, prim); + shader = __float_as_int(str.z); + } +#endif + + return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; +} + +ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict kg, + const Intersection *isect) +{ + const int prim = kernel_tex_fetch(__prim_index, isect->prim); + int shader = 0; + +#ifdef __HAIR__ + if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) +#endif + { + shader = kernel_tex_fetch(__tri_shader, prim); + } +#ifdef __HAIR__ + else { + float4 str = kernel_tex_fetch(__curves, prim); + shader = __float_as_int(str.z); + } +#endif + + return shader & SHADER_MASK; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 5681510fc25..dd2390808ea 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -65,7 +65,6 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg, uint visibility = path_state_ray_visibility(kg, state); if (path_state_ao_bounce(kg, state)) { - visibility = PATH_RAY_SHADOW; ray->t = kernel_data.background.ao_distance; } @@ -416,7 +415,13 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, break; } else if (path_state_ao_bounce(kg, state)) { - break; + if (intersection_get_shader_flags(kg, &isect) & + (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { + state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + else { + break; + } } /* Setup shader data. */ @@ -554,7 +559,13 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg, break; } else if (path_state_ao_bounce(kg, state)) { - break; + if (intersection_get_shader_flags(kg, &isect) & + (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { + state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + else { + break; + } } /* Setup shader data. */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index ab54fda14af..18c4d2f86ad 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -895,6 +895,8 @@ enum ShaderDataFlag { SD_HAS_CONSTANT_EMISSION = (1 << 27), /* Needs to access attributes for volume rendering */ SD_NEED_VOLUME_ATTRIBUTES = (1 << 28), + /* Shader has emission */ + SD_HAS_EMISSION = (1 << 29), SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME | SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR | diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index c67919b375a..feead27c5ca 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -24,6 +24,7 @@ set(INC_SYS set(SRC alembic.cpp + alembic_read.cpp attribute.cpp background.cpp bake.cpp @@ -67,6 +68,7 @@ set(SRC set(SRC_HEADERS alembic.h + alembic_read.h attribute.h bake.h background.h diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index c3a7b20f512..5842f4c313d 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -16,6 +16,7 @@ #include "render/alembic.h" +#include "render/alembic_read.h" #include "render/camera.h" #include "render/curves.h" #include "render/mesh.h" @@ -34,7 +35,48 @@ using namespace Alembic::AbcGeom; CCL_NAMESPACE_BEGIN -/* TODO(@kevindietrich): motion blur support */ +/* TODO(kevindietrich): motion blur support. */ + +template<typename SchemaType> +static vector<FaceSetShaderIndexPair> parse_face_sets_for_shader_assignment( + SchemaType &schema, const array<Node *> &used_shaders) +{ + vector<FaceSetShaderIndexPair> result; + + std::vector<std::string> face_set_names; + schema.getFaceSetNames(face_set_names); + + if (face_set_names.empty()) { + return result; + } + + for (const std::string &face_set_name : face_set_names) { + int shader_index = 0; + + for (Node *node : used_shaders) { + if (node->name == face_set_name) { + break; + } + + ++shader_index; + } + + if (shader_index >= used_shaders.size()) { + /* use the first shader instead if none was found */ + shader_index = 0; + } + + const Alembic::AbcGeom::IFaceSet face_set = schema.getFaceSet(face_set_name); + + if (!face_set.valid()) { + continue; + } + + result.push_back({face_set, shader_index}); + } + + return result; +} void CachedData::clear() { @@ -54,7 +96,7 @@ void CachedData::clear() subd_start_corner.clear(); transforms.clear(); triangles.clear(); - triangles_loops.clear(); + uv_loops.clear(); vertices.clear(); for (CachedAttribute &attr : attributes) { @@ -101,7 +143,7 @@ bool CachedData::is_constant() const CHECK_IF_CONSTANT(subd_start_corner) CHECK_IF_CONSTANT(transforms) CHECK_IF_CONSTANT(triangles) - CHECK_IF_CONSTANT(triangles_loops) + CHECK_IF_CONSTANT(uv_loops) CHECK_IF_CONSTANT(vertices) for (const CachedAttribute &attr : attributes) { @@ -140,7 +182,7 @@ void CachedData::invalidate_last_loaded_time(bool attributes_only) subd_start_corner.invalidate_last_loaded_time(); transforms.invalidate_last_loaded_time(); triangles.invalidate_last_loaded_time(); - triangles_loops.invalidate_last_loaded_time(); + uv_loops.invalidate_last_loaded_time(); vertices.invalidate_last_loaded_time(); } @@ -161,7 +203,7 @@ void CachedData::set_time_sampling(TimeSampling time_sampling) subd_start_corner.set_time_sampling(time_sampling); transforms.set_time_sampling(time_sampling); triangles.set_time_sampling(time_sampling); - triangles_loops.set_time_sampling(time_sampling); + uv_loops.set_time_sampling(time_sampling); vertices.set_time_sampling(time_sampling); for (CachedAttribute &attr : attributes) { @@ -169,36 +211,6 @@ void CachedData::set_time_sampling(TimeSampling time_sampling) } } -/* get the sample times to load data for the given the start and end frame of the procedural */ -static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc, - const TimeSampling &time_sampling, - size_t num_samples) -{ - set<chrono_t> result; - - if (num_samples < 2) { - result.insert(0.0); - return result; - } - - double start_frame = (double)(proc->get_start_frame() / proc->get_frame_rate()); - double end_frame = (double)((proc->get_end_frame() + 1) / proc->get_frame_rate()); - - size_t start_index = time_sampling.getFloorIndex(start_frame, num_samples).first; - size_t end_index = time_sampling.getCeilIndex(end_frame, num_samples).first; - - for (size_t i = start_index; i < end_index; ++i) { - result.insert(time_sampling.getSampleTime(i)); - } - - return result; -} - -static float3 make_float3_from_yup(const V3f &v) -{ - return make_float3(v.x, -v.z, v.y); -} - static M44d convert_yup_zup(const M44d &mtx, float scale_mult) { V3d scale, shear, rotation, translation; @@ -366,249 +378,6 @@ static Transform make_transform(const M44d &a, float scale) return trans; } -static void add_uvs(AlembicProcedural *proc, - const IV2fGeomParam &uvs, - CachedData &cached_data, - Progress &progress) -{ - if (uvs.getScope() != kFacevaryingScope) { - return; - } - - const TimeSamplingPtr time_sampling_ptr = uvs.getTimeSampling(); - - TimeSampling time_sampling; - if (time_sampling_ptr) { - time_sampling = *time_sampling_ptr; - } - - std::string name = Alembic::Abc::GetSourceName(uvs.getMetaData()); - - /* According to the convention, primary UVs should have had their name - * set using Alembic::Abc::SetSourceName, but you can't expect everyone - * to follow it! :) */ - if (name.empty()) { - name = uvs.getName(); - } - - CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(name), time_sampling); - attr.std = ATTR_STD_UV; - - ccl::set<chrono_t> times = get_relevant_sample_times(proc, time_sampling, uvs.getNumSamples()); - - /* Keys used to determine if the UVs do actually change over time. */ - ArraySample::Key previous_indices_key; - ArraySample::Key previous_values_key; - - foreach (chrono_t time, times) { - if (progress.get_cancel()) { - return; - } - - const ISampleSelector iss = ISampleSelector(time); - const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss); - - if (!uvsample.valid()) { - continue; - } - - const array<int3> *triangles = - cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); - const array<int3> *triangles_loops = - cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null(); - - if (!triangles || !triangles_loops) { - continue; - } - - array<char> data; - data.resize(triangles->size() * 3 * sizeof(float2)); - - float2 *data_float2 = reinterpret_cast<float2 *>(data.data()); - - const ArraySample::Key indices_key = uvsample.getIndices()->getKey(); - const ArraySample::Key values_key = uvsample.getVals()->getKey(); - - if (indices_key == previous_indices_key && values_key == previous_values_key) { - attr.data.reuse_data_for_last_time(time); - } - else { - const unsigned int *indices = uvsample.getIndices()->get(); - const V2f *values = uvsample.getVals()->get(); - - for (const int3 &loop : *triangles_loops) { - unsigned int v0 = indices[loop.x]; - unsigned int v1 = indices[loop.y]; - unsigned int v2 = indices[loop.z]; - - data_float2[0] = make_float2(values[v0][0], values[v0][1]); - data_float2[1] = make_float2(values[v1][0], values[v1][1]); - data_float2[2] = make_float2(values[v2][0], values[v2][1]); - data_float2 += 3; - } - - attr.data.add_data(data, time); - } - - previous_indices_key = indices_key; - previous_values_key = values_key; - } -} - -static void add_normals(const Int32ArraySamplePtr face_indices, - const IN3fGeomParam &normals, - double time, - CachedData &cached_data) -{ - switch (normals.getScope()) { - case kFacevaryingScope: { - const ISampleSelector iss = ISampleSelector(time); - const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss); - - if (!sample.valid()) { - return; - } - - CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()), - *normals.getTimeSampling()); - attr.std = ATTR_STD_VERTEX_NORMAL; - - const array<float3> *vertices = - cached_data.vertices.data_for_time_no_check(time).get_data_or_null(); - - if (!vertices) { - return; - } - - array<char> data; - data.resize(vertices->size() * sizeof(float3)); - - float3 *data_float3 = reinterpret_cast<float3 *>(data.data()); - - const int *face_indices_array = face_indices->get(); - const N3fArraySamplePtr values = sample.getVals(); - - for (size_t i = 0; i < face_indices->size(); ++i) { - int point_index = face_indices_array[i]; - data_float3[point_index] = make_float3_from_yup(values->get()[i]); - } - - attr.data.add_data(data, time); - break; - } - case kVaryingScope: - case kVertexScope: { - const ISampleSelector iss = ISampleSelector(time); - const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss); - - if (!sample.valid()) { - return; - } - - CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()), - *normals.getTimeSampling()); - attr.std = ATTR_STD_VERTEX_NORMAL; - - const array<float3> *vertices = - cached_data.vertices.data_for_time_no_check(time).get_data_or_null(); - - if (!vertices) { - return; - } - - array<char> data; - data.resize(vertices->size() * sizeof(float3)); - - float3 *data_float3 = reinterpret_cast<float3 *>(data.data()); - - const Imath::V3f *values = sample.getVals()->get(); - - for (size_t i = 0; i < vertices->size(); ++i) { - data_float3[i] = make_float3_from_yup(values[i]); - } - - attr.data.add_data(data, time); - - break; - } - default: { - break; - } - } -} - -static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data) -{ - if (!positions) { - return; - } - - array<float3> vertices; - vertices.reserve(positions->size()); - - for (size_t i = 0; i < positions->size(); i++) { - V3f f = positions->get()[i]; - vertices.push_back_reserved(make_float3_from_yup(f)); - } - - cached_data.vertices.add_data(vertices, time); -} - -static void add_triangles(const Int32ArraySamplePtr face_counts, - const Int32ArraySamplePtr face_indices, - double time, - CachedData &cached_data, - const array<int> &polygon_to_shader) -{ - if (!face_counts || !face_indices) { - return; - } - - const size_t num_faces = face_counts->size(); - const int *face_counts_array = face_counts->get(); - const int *face_indices_array = face_indices->get(); - - size_t num_triangles = 0; - for (size_t i = 0; i < face_counts->size(); i++) { - num_triangles += face_counts_array[i] - 2; - } - - array<int> shader; - array<int3> triangles; - array<int3> triangles_loops; - shader.reserve(num_triangles); - triangles.reserve(num_triangles); - triangles_loops.reserve(num_triangles); - int index_offset = 0; - - for (size_t i = 0; i < num_faces; i++) { - int current_shader = 0; - - if (!polygon_to_shader.empty()) { - current_shader = polygon_to_shader[i]; - } - - for (int j = 0; j < face_counts_array[i] - 2; j++) { - int v0 = face_indices_array[index_offset]; - int v1 = face_indices_array[index_offset + j + 1]; - int v2 = face_indices_array[index_offset + j + 2]; - - shader.push_back_reserved(current_shader); - - /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */ - triangles.push_back_reserved(make_int3(v2, v1, v0)); - triangles_loops.push_back_reserved( - make_int3(index_offset + j + 2, index_offset + j + 1, index_offset)); - } - - index_offset += face_counts_array[i]; - } - - cached_data.triangles.add_data(triangles, time); - cached_data.triangles_loops.add_data(triangles_loops, time); - cached_data.shader.add_data(shader, time); -} - NODE_DEFINE(AlembicObject) { NodeType *type = NodeType::add("alembic_object", create); @@ -648,398 +417,141 @@ bool AlembicObject::has_data_loaded() const return data_loaded; } -void AlembicObject::update_shader_attributes(const ICompoundProperty &arb_geom_params, - Progress &progress) -{ - AttributeRequestSet requested_attributes = get_requested_attributes(); - - foreach (const AttributeRequest &attr, requested_attributes.requests) { - if (progress.get_cancel()) { - return; - } - - bool attr_exists = false; - foreach (CachedData::CachedAttribute &cached_attr, cached_data.attributes) { - if (cached_attr.name == attr.name) { - attr_exists = true; - break; - } - } - - if (attr_exists) { - continue; - } - - read_attribute(arb_geom_params, attr.name, progress); - } - - cached_data.invalidate_last_loaded_time(true); - need_shader_update = false; -} - -template<typename SchemaType> -void AlembicObject::read_face_sets(SchemaType &schema, - array<int> &polygon_to_shader, - ISampleSelector sample_sel) +void AlembicObject::load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + IPolyMeshSchema &schema, + Progress &progress) { - std::vector<std::string> face_sets; - schema.getFaceSetNames(face_sets); - - if (face_sets.empty()) { - return; - } - - const Int32ArraySamplePtr face_counts = schema.getFaceCountsProperty().getValue(); - - polygon_to_shader.resize(face_counts->size()); - - foreach (const std::string &face_set_name, face_sets) { - int shader_index = 0; - - foreach (Node *node, get_used_shaders()) { - if (node->name == face_set_name) { - break; - } - - ++shader_index; - } - - if (shader_index >= get_used_shaders().size()) { - /* use the first shader instead if none was found */ - shader_index = 0; - } - - const IFaceSet face_set = schema.getFaceSet(face_set_name); - - if (!face_set.valid()) { - continue; - } - - const IFaceSetSchema face_schem = face_set.getSchema(); - const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel); - const Int32ArraySamplePtr group_faces = face_sample.getFaces(); - const size_t num_group_faces = group_faces->size(); - - for (size_t l = 0; l < num_group_faces; l++) { - size_t pos = (*group_faces)[l]; - - if (pos >= polygon_to_shader.size()) { - continue; - } - - polygon_to_shader[pos] = shader_index; - } - } -} - -void AlembicObject::load_all_data(AlembicProcedural *proc, - IPolyMeshSchema &schema, - Progress &progress) -{ - cached_data.clear(); - /* Only load data for the original Geometry. */ if (instance_of) { return; } - const TimeSamplingPtr time_sampling = schema.getTimeSampling(); - cached_data.set_time_sampling(*time_sampling); - - const IN3fGeomParam &normals = schema.getNormalsParam(); - - ccl::set<chrono_t> times = get_relevant_sample_times( - proc, *time_sampling, schema.getNumSamples()); - - /* Key used to determine if the triangles change over time, if the key is the same as the - * last one, we can avoid creating a new entry in the cache and simply point to the last - * frame. */ - ArraySample::Key previous_key; - - /* read topology */ - foreach (chrono_t time, times) { - if (progress.get_cancel()) { - return; - } - - const ISampleSelector iss = ISampleSelector(time); - const IPolyMeshSchema::Sample sample = schema.getValue(iss); - - add_positions(sample.getPositions(), time, cached_data); - - /* Only copy triangles for other frames if the topology is changing over time as well. */ - if (schema.getTopologyVariance() != kHomogenousTopology || cached_data.triangles.size() == 0) { - const ArraySample::Key key = sample.getFaceIndices()->getKey(); - - if (key == previous_key) { - cached_data.triangles.reuse_data_for_last_time(time); - cached_data.triangles_loops.reuse_data_for_last_time(time); - cached_data.shader.reuse_data_for_last_time(time); - } - else { - /* start by reading the face sets (per face shader), as we directly split polygons to - * triangles - */ - array<int> polygon_to_shader; - read_face_sets(schema, polygon_to_shader, iss); - - add_triangles( - sample.getFaceCounts(), sample.getFaceIndices(), time, cached_data, polygon_to_shader); - } + cached_data.clear(); - previous_key = key; - } + PolyMeshSchemaData data; + data.topology_variance = schema.getTopologyVariance(); + data.time_sampling = schema.getTimeSampling(); + data.positions = schema.getPositionsProperty(); + data.face_counts = schema.getFaceCountsProperty(); + data.face_indices = schema.getFaceIndicesProperty(); + data.normals = schema.getNormalsParam(); + data.num_samples = schema.getNumSamples(); + data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders()); - if (normals.valid()) { - add_normals(sample.getFaceIndices(), normals, time, cached_data); - } - } + read_geometry_data(proc, cached_data, data, progress); if (progress.get_cancel()) { return; } - update_shader_attributes(schema.getArbGeomParams(), progress); + /* Use the schema as the base compound property to also be able to look for top level properties. + */ + read_attributes( + proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress); if (progress.get_cancel()) { return; } - const IV2fGeomParam &uvs = schema.getUVsParam(); - - if (uvs.valid()) { - add_uvs(proc, uvs, cached_data, progress); - } - + cached_data.invalidate_last_loaded_time(true); data_loaded = true; } -void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema, Progress &progress) +void AlembicObject::load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + ISubDSchema &schema, + Progress &progress) { - cached_data.clear(); - /* Only load data for the original Geometry. */ if (instance_of) { return; } - AttributeRequestSet requested_attributes = get_requested_attributes(); - - const TimeSamplingPtr time_sampling = schema.getTimeSampling(); - cached_data.set_time_sampling(*time_sampling); - - ccl::set<chrono_t> times = get_relevant_sample_times( - proc, *time_sampling, schema.getNumSamples()); - - /* read topology */ - foreach (chrono_t time, times) { - if (progress.get_cancel()) { - return; - } - - const ISampleSelector iss = ISampleSelector(time); - const ISubDSchema::Sample sample = schema.getValue(iss); - - add_positions(sample.getPositions(), time, cached_data); - - const Int32ArraySamplePtr face_counts = sample.getFaceCounts(); - const Int32ArraySamplePtr face_indices = sample.getFaceIndices(); - - /* start by reading the face sets (per face shader) */ - array<int> polygon_to_shader; - read_face_sets(schema, polygon_to_shader, iss); - - /* read faces */ - array<int> subd_start_corner; - array<int> shader; - array<int> subd_num_corners; - array<bool> subd_smooth; - array<int> subd_ptex_offset; - array<int> subd_face_corners; - - const size_t num_faces = face_counts->size(); - const int *face_counts_array = face_counts->get(); - const int *face_indices_array = face_indices->get(); - - int num_ngons = 0; - int num_corners = 0; - for (size_t i = 0; i < face_counts->size(); i++) { - num_ngons += (face_counts_array[i] == 4 ? 0 : 1); - num_corners += face_counts_array[i]; - } - - subd_start_corner.reserve(num_faces); - subd_num_corners.reserve(num_faces); - subd_smooth.reserve(num_faces); - subd_ptex_offset.reserve(num_faces); - shader.reserve(num_faces); - subd_face_corners.reserve(num_corners); - - int start_corner = 0; - int current_shader = 0; - int ptex_offset = 0; - - for (size_t i = 0; i < face_counts->size(); i++) { - num_corners = face_counts_array[i]; - - if (!polygon_to_shader.empty()) { - current_shader = polygon_to_shader[i]; - } - - subd_start_corner.push_back_reserved(start_corner); - subd_num_corners.push_back_reserved(num_corners); - - for (int j = 0; j < num_corners; ++j) { - subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]); - } - - shader.push_back_reserved(current_shader); - subd_smooth.push_back_reserved(1); - subd_ptex_offset.push_back_reserved(ptex_offset); - - ptex_offset += (num_corners == 4 ? 1 : num_corners); - - start_corner += num_corners; - } - - cached_data.shader.add_data(shader, time); - cached_data.subd_start_corner.add_data(subd_start_corner, time); - cached_data.subd_num_corners.add_data(subd_num_corners, time); - cached_data.subd_smooth.add_data(subd_smooth, time); - cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time); - cached_data.subd_face_corners.add_data(subd_face_corners, time); - cached_data.num_ngons.add_data(num_ngons, time); - - /* read creases */ - Int32ArraySamplePtr creases_length = sample.getCreaseLengths(); - Int32ArraySamplePtr creases_indices = sample.getCreaseIndices(); - FloatArraySamplePtr creases_sharpnesses = sample.getCreaseSharpnesses(); - - if (creases_length && creases_indices && creases_sharpnesses) { - array<int> creases_edge; - array<float> creases_weight; - - creases_edge.reserve(creases_sharpnesses->size() * 2); - creases_weight.reserve(creases_sharpnesses->size()); - - int length_offset = 0; - int weight_offset = 0; - for (size_t c = 0; c < creases_length->size(); ++c) { - const int crease_length = creases_length->get()[c]; - - for (size_t j = 0; j < crease_length - 1; ++j) { - creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]); - creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]); - creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]); - } - - length_offset += crease_length; - } - - cached_data.subd_creases_edge.add_data(creases_edge, time); - cached_data.subd_creases_weight.add_data(creases_weight, time); - } - } + cached_data.clear(); - /* TODO(@kevindietrich) : attributes, need test files */ + SubDSchemaData data; + data.time_sampling = schema.getTimeSampling(); + data.num_samples = schema.getNumSamples(); + data.topology_variance = schema.getTopologyVariance(); + data.face_counts = schema.getFaceCountsProperty(); + data.face_indices = schema.getFaceIndicesProperty(); + data.positions = schema.getPositionsProperty(); + data.face_varying_interpolate_boundary = schema.getFaceVaryingInterpolateBoundaryProperty(); + data.face_varying_propagate_corners = schema.getFaceVaryingPropagateCornersProperty(); + data.interpolate_boundary = schema.getInterpolateBoundaryProperty(); + data.crease_indices = schema.getCreaseIndicesProperty(); + data.crease_lengths = schema.getCreaseLengthsProperty(); + data.crease_sharpnesses = schema.getCreaseSharpnessesProperty(); + data.corner_indices = schema.getCornerIndicesProperty(); + data.corner_sharpnesses = schema.getCornerSharpnessesProperty(); + data.holes = schema.getHolesProperty(); + data.subdivision_scheme = schema.getSubdivisionSchemeProperty(); + data.velocities = schema.getVelocitiesProperty(); + data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders()); + + read_geometry_data(proc, cached_data, data, progress); if (progress.get_cancel()) { return; } + /* Use the schema as the base compound property to also be able to look for top level properties. + */ + read_attributes(proc, + cached_data, + schema.getArbGeomParams(), + schema.getUVsParam(), + get_requested_attributes(), + progress); + + cached_data.invalidate_last_loaded_time(true); data_loaded = true; } -void AlembicObject::load_all_data(AlembicProcedural *proc, - const ICurvesSchema &schema, - Progress &progress, - float default_radius) +void AlembicObject::load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + const ICurvesSchema &schema, + Progress &progress) { - cached_data.clear(); - /* Only load data for the original Geometry. */ if (instance_of) { return; } - const TimeSamplingPtr time_sampling = schema.getTimeSampling(); - cached_data.set_time_sampling(*time_sampling); - - ccl::set<chrono_t> times = get_relevant_sample_times( - proc, *time_sampling, schema.getNumSamples()); - - foreach (chrono_t time, times) { - if (progress.get_cancel()) { - return; - } - - const ISampleSelector iss = ISampleSelector(time); - const ICurvesSchema::Sample sample = schema.getValue(iss); - - const Int32ArraySamplePtr curves_num_vertices = sample.getCurvesNumVertices(); - const P3fArraySamplePtr position = sample.getPositions(); - - const IFloatGeomParam widths_param = schema.getWidthsParam(); - FloatArraySamplePtr radiuses; - - if (widths_param.valid()) { - IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(iss); - radiuses = wsample.getVals(); - } - - const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1); - float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : default_radius; - - array<float3> curve_keys; - array<float> curve_radius; - array<int> curve_first_key; - array<int> curve_shader; - - const bool is_homogenous = schema.getTopologyVariance() == kHomogenousTopology; - - curve_keys.reserve(position->size()); - curve_radius.reserve(position->size()); - curve_first_key.reserve(curves_num_vertices->size()); - curve_shader.reserve(curves_num_vertices->size()); - - int offset = 0; - for (size_t i = 0; i < curves_num_vertices->size(); i++) { - const int num_vertices = curves_num_vertices->get()[i]; - - for (int j = 0; j < num_vertices; j++) { - const V3f &f = position->get()[offset + j]; - curve_keys.push_back_reserved(make_float3_from_yup(f)); - - if (do_radius) { - radius = (*radiuses)[offset + j]; - } - - curve_radius.push_back_reserved(radius * radius_scale); - } - - if (!is_homogenous || cached_data.curve_first_key.size() == 0) { - curve_first_key.push_back_reserved(offset); - curve_shader.push_back_reserved(0); - } - - offset += num_vertices; - } + cached_data.clear(); - cached_data.curve_keys.add_data(curve_keys, time); - cached_data.curve_radius.add_data(curve_radius, time); + CurvesSchemaData data; + data.positions = schema.getPositionsProperty(); + data.position_weights = schema.getPositionWeightsProperty(); + data.normals = schema.getNormalsParam(); + data.knots = schema.getKnotsProperty(); + data.orders = schema.getOrdersProperty(); + data.widths = schema.getWidthsParam(); + data.velocities = schema.getVelocitiesProperty(); + data.time_sampling = schema.getTimeSampling(); + data.topology_variance = schema.getTopologyVariance(); + data.num_samples = schema.getNumSamples(); + data.num_vertices = schema.getNumVerticesProperty(); + data.default_radius = proc->get_default_radius(); + data.radius_scale = get_radius_scale(); + + read_geometry_data(proc, cached_data, data, progress); - if (!is_homogenous || cached_data.curve_first_key.size() == 0) { - cached_data.curve_first_key.add_data(curve_first_key, time); - cached_data.curve_shader.add_data(curve_shader, time); - } + if (progress.get_cancel()) { + return; } - // TODO(@kevindietrich): attributes, need example files + /* Use the schema as the base compound property to also be able to look for top level properties. + */ + read_attributes( + proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress); + cached_data.invalidate_last_loaded_time(true); data_loaded = true; } -void AlembicObject::setup_transform_cache(float scale) +void AlembicObject::setup_transform_cache(CachedData &cached_data, float scale) { cached_data.transforms.clear(); cached_data.transforms.invalidate_last_loaded_time(); @@ -1102,188 +614,6 @@ AttributeRequestSet AlembicObject::get_requested_attributes() return requested_attributes; } -void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params, - const ustring &attr_name, - Progress &progress) -{ - const PropertyHeader *prop = arb_geom_params.getPropertyHeader(attr_name.c_str()); - - if (prop == nullptr) { - return; - } - - if (IV2fProperty::matches(prop->getMetaData()) && Alembic::AbcGeom::isUV(*prop)) { - const IV2fGeomParam ¶m = IV2fGeomParam(arb_geom_params, prop->getName()); - - CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name, - *param.getTimeSampling()); - - for (size_t i = 0; i < param.getNumSamples(); ++i) { - if (progress.get_cancel()) { - return; - } - - ISampleSelector iss = ISampleSelector(index_t(i)); - - IV2fGeomParam::Sample sample; - param.getIndexed(sample, iss); - - const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i)); - - if (param.getScope() == kFacevaryingScope) { - V2fArraySamplePtr values = sample.getVals(); - UInt32ArraySamplePtr indices = sample.getIndices(); - - attribute.std = ATTR_STD_NONE; - attribute.element = ATTR_ELEMENT_CORNER; - attribute.type_desc = TypeFloat2; - - const array<int3> *triangles = - cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); - const array<int3> *triangles_loops = - cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null(); - - if (!triangles || !triangles_loops) { - return; - } - - array<char> data; - data.resize(triangles->size() * 3 * sizeof(float2)); - - float2 *data_float2 = reinterpret_cast<float2 *>(data.data()); - - for (const int3 &loop : *triangles_loops) { - unsigned int v0 = (*indices)[loop.x]; - unsigned int v1 = (*indices)[loop.y]; - unsigned int v2 = (*indices)[loop.z]; - - data_float2[0] = make_float2((*values)[v0][0], (*values)[v0][1]); - data_float2[1] = make_float2((*values)[v1][0], (*values)[v1][1]); - data_float2[2] = make_float2((*values)[v2][0], (*values)[v2][1]); - data_float2 += 3; - } - - attribute.data.set_time_sampling(*param.getTimeSampling()); - attribute.data.add_data(data, time); - } - } - } - else if (IC3fProperty::matches(prop->getMetaData())) { - const IC3fGeomParam ¶m = IC3fGeomParam(arb_geom_params, prop->getName()); - - CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name, - *param.getTimeSampling()); - - for (size_t i = 0; i < param.getNumSamples(); ++i) { - if (progress.get_cancel()) { - return; - } - - ISampleSelector iss = ISampleSelector(index_t(i)); - - IC3fGeomParam::Sample sample; - param.getIndexed(sample, iss); - - const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i)); - - C3fArraySamplePtr values = sample.getVals(); - - attribute.std = ATTR_STD_NONE; - - if (param.getScope() == kVaryingScope) { - attribute.element = ATTR_ELEMENT_CORNER_BYTE; - attribute.type_desc = TypeRGBA; - - const array<int3> *triangles = - cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); - - if (!triangles) { - return; - } - - array<char> data; - data.resize(triangles->size() * 3 * sizeof(uchar4)); - - uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data()); - - int offset = 0; - for (const int3 &tri : *triangles) { - Imath::C3f v = (*values)[tri.x]; - data_uchar4[offset + 0] = color_float_to_byte(make_float3(v.x, v.y, v.z)); - - v = (*values)[tri.y]; - data_uchar4[offset + 1] = color_float_to_byte(make_float3(v.x, v.y, v.z)); - - v = (*values)[tri.z]; - data_uchar4[offset + 2] = color_float_to_byte(make_float3(v.x, v.y, v.z)); - - offset += 3; - } - - attribute.data.set_time_sampling(*param.getTimeSampling()); - attribute.data.add_data(data, time); - } - } - } - else if (IC4fProperty::matches(prop->getMetaData())) { - const IC4fGeomParam ¶m = IC4fGeomParam(arb_geom_params, prop->getName()); - - CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name, - *param.getTimeSampling()); - - for (size_t i = 0; i < param.getNumSamples(); ++i) { - if (progress.get_cancel()) { - return; - } - - ISampleSelector iss = ISampleSelector(index_t(i)); - - IC4fGeomParam::Sample sample; - param.getIndexed(sample, iss); - - const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i)); - - C4fArraySamplePtr values = sample.getVals(); - - attribute.std = ATTR_STD_NONE; - - if (param.getScope() == kVaryingScope) { - attribute.element = ATTR_ELEMENT_CORNER_BYTE; - attribute.type_desc = TypeRGBA; - - const array<int3> *triangles = - cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); - - if (!triangles) { - return; - } - - array<char> data; - data.resize(triangles->size() * 3 * sizeof(uchar4)); - - uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data()); - - int offset = 0; - for (const int3 &tri : *triangles) { - Imath::C4f v = (*values)[tri.x]; - data_uchar4[offset + 0] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a)); - - v = (*values)[tri.y]; - data_uchar4[offset + 1] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a)); - - v = (*values)[tri.z]; - data_uchar4[offset + 2] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a)); - - offset += 3; - } - - attribute.data.set_time_sampling(*param.getTimeSampling()); - attribute.data.add_data(data, time); - } - } - } -} - /* Update existing attributes and remove any attribute not in the cached_data, those attributes * were added by Cycles (e.g. face normals) */ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, double frame_time) @@ -1291,7 +621,11 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, set<Attribute *> cached_attributes; for (CachedData::CachedAttribute &attribute : cached_data.attributes) { - const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null(); + const CacheLookupResult<array<char>> result = attribute.data.data_for_time(frame_time); + + if (result.has_no_data_for_time()) { + continue; + } Attribute *attr = nullptr; if (attribute.std != ATTR_STD_NONE) { @@ -1304,18 +638,19 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, cached_attributes.insert(attr); - if (!attr_data) { - /* no new data */ + if (!result.has_new_data()) { continue; } + const ccl::array<char> &attr_data = result.get_data(); + /* weak way of detecting if the topology has changed * todo: reuse code from device_update patch */ - if (attr->buffer.size() != attr_data->size()) { - attr->buffer.resize(attr_data->size()); + if (attr->buffer.size() != attr_data.size()) { + attr->buffer.resize(attr_data.size()); } - memcpy(attr->data(), attr_data->data(), attr_data->size()); + memcpy(attr->data(), attr_data.data(), attr_data.size()); attr->modified = true; } @@ -1476,6 +811,7 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) read_subd(object, frame_time); } + object->need_shader_update = false; object->clear_modified(); } @@ -1960,12 +1296,17 @@ void AlembicProcedural::build_caches(Progress &progress) if (!object->has_data_loaded()) { IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting); IPolyMeshSchema schema = polymesh.getSchema(); - object->load_all_data(this, schema, progress); + object->load_data_in_cache(object->get_cached_data(), this, schema, progress); } else if (object->need_shader_update) { IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting); IPolyMeshSchema schema = polymesh.getSchema(); - object->update_shader_attributes(schema.getArbGeomParams(), progress); + read_attributes(this, + object->get_cached_data(), + schema, + schema.getUVsParam(), + object->get_requested_attributes(), + progress); } } else if (object->schema_type == AlembicObject::CURVES) { @@ -1973,24 +1314,29 @@ void AlembicProcedural::build_caches(Progress &progress) object->radius_scale_is_modified()) { ICurves curves(object->iobject, Alembic::Abc::kWrapExisting); ICurvesSchema schema = curves.getSchema(); - object->load_all_data(this, schema, progress, default_radius); + object->load_data_in_cache(object->get_cached_data(), this, schema, progress); } } else if (object->schema_type == AlembicObject::SUBD) { if (!object->has_data_loaded()) { ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting); ISubDSchema schema = subd_mesh.getSchema(); - object->load_all_data(this, schema, progress); + object->load_data_in_cache(object->get_cached_data(), this, schema, progress); } else if (object->need_shader_update) { ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting); ISubDSchema schema = subd_mesh.getSchema(); - object->update_shader_attributes(schema.getArbGeomParams(), progress); + read_attributes(this, + object->get_cached_data(), + schema, + schema.getUVsParam(), + object->get_requested_attributes(), + progress); } } if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) { - object->setup_transform_cache(scale); + object->setup_transform_cache(object->get_cached_data(), scale); } } } diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index 3bbd10fad61..61c0e40fe4a 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -152,6 +152,10 @@ template<typename T> class DataStore { double last_loaded_time = std::numeric_limits<double>::max(); public: + /* Keys used to compare values. */ + Alembic::AbcCoreAbstract::ArraySample::Key key1; + Alembic::AbcCoreAbstract::ArraySample::Key key2; + void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_) { time_sampling = time_sampling_; @@ -225,6 +229,11 @@ template<typename T> class DataStore { index_data_map.push_back({time, data_index.source_time, data_index.index}); } + void add_no_data(double time) + { + index_data_map.push_back({time, time, -1ul}); + } + bool is_constant() const { return data.size() <= 1; @@ -284,7 +293,7 @@ struct CachedData { DataStore<array<int3>> triangles{}; /* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes * (like UVs) */ - DataStore<array<int3>> triangles_loops{}; + DataStore<array<int>> uv_loops{}; DataStore<array<int>> shader{}; /* subd data */ @@ -362,16 +371,18 @@ class AlembicObject : public Node { void set_object(Object *object); Object *get_object(); - void load_all_data(AlembicProcedural *proc, - Alembic::AbcGeom::IPolyMeshSchema &schema, - Progress &progress); - void load_all_data(AlembicProcedural *proc, - Alembic::AbcGeom::ISubDSchema &schema, - Progress &progress); - void load_all_data(AlembicProcedural *proc, - const Alembic::AbcGeom::ICurvesSchema &schema, - Progress &progress, - float default_radius); + void load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + Alembic::AbcGeom::IPolyMeshSchema &schema, + Progress &progress); + void load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + Alembic::AbcGeom::ISubDSchema &schema, + Progress &progress); + void load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + const Alembic::AbcGeom::ICurvesSchema &schema, + Progress &progress); bool has_data_loaded() const; @@ -397,33 +408,21 @@ class AlembicObject : public Node { CachedData &get_cached_data() { - return cached_data; + return cached_data_; } bool is_constant() const { - return cached_data.is_constant(); + return cached_data_.is_constant(); } Object *object = nullptr; bool data_loaded = false; - CachedData cached_data; - - void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params, - Progress &progress); - - void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params, - const ustring &attr_name, - Progress &progress); - - template<typename SchemaType> - void read_face_sets(SchemaType &schema, - array<int> &polygon_to_shader, - Alembic::AbcGeom::ISampleSelector sample_sel); + CachedData cached_data_; - void setup_transform_cache(float scale); + void setup_transform_cache(CachedData &cached_data, float scale); AttributeRequestSet get_requested_attributes(); }; diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp new file mode 100644 index 00000000000..9fdc7561957 --- /dev/null +++ b/intern/cycles/render/alembic_read.cpp @@ -0,0 +1,1003 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "render/alembic_read.h" + +#include "render/alembic.h" +#include "render/mesh.h" + +#include "util/util_progress.h" + +#ifdef WITH_ALEMBIC + +using namespace Alembic::AbcGeom; + +CCL_NAMESPACE_BEGIN + +static float3 make_float3_from_yup(const V3f &v) +{ + return make_float3(v.x, -v.z, v.y); +} + +/* get the sample times to load data for the given the start and end frame of the procedural */ +static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc, + const TimeSampling &time_sampling, + size_t num_samples) +{ + set<chrono_t> result; + + if (num_samples < 2) { + result.insert(0.0); + return result; + } + + // load the data for the entire animation + const double start_frame = static_cast<double>(proc->get_start_frame()); + const double end_frame = static_cast<double>(proc->get_end_frame()); + + const double frame_rate = static_cast<double>(proc->get_frame_rate()); + const double start_time = start_frame / frame_rate; + const double end_time = (end_frame + 1) / frame_rate; + + const size_t start_index = time_sampling.getFloorIndex(start_time, num_samples).first; + const size_t end_index = time_sampling.getCeilIndex(end_time, num_samples).first; + + for (size_t i = start_index; i < end_index; ++i) { + result.insert(time_sampling.getSampleTime(i)); + } + + return result; +} + +/* Main function to read data, this will iterate over all the relevant sample times for the + * duration of the requested animation, and call the DataReadingFunc for each of those sample time. + */ +template<typename Params, typename DataReadingFunc> +static void read_data_loop(AlembicProcedural *proc, + CachedData &cached_data, + const Params ¶ms, + DataReadingFunc &&func, + Progress &progress) +{ + const std::set<chrono_t> times = get_relevant_sample_times( + proc, *params.time_sampling, params.num_samples); + + cached_data.set_time_sampling(*params.time_sampling); + + for (chrono_t time : times) { + if (progress.get_cancel()) { + return; + } + + func(cached_data, params, time); + } +} + +/* Polygon Mesh Geometries. */ + +/* Compute the vertex normals in case none are present in the IPolyMeshSchema, this is mostly used + * to avoid computing them in the GeometryManager in order to speed up data updates. */ +static void compute_vertex_normals(CachedData &cache, double current_time) +{ + if (cache.vertices.size() == 0) { + return; + } + + CachedData::CachedAttribute &attr_normal = cache.add_attribute( + ustring("N"), cache.vertices.get_time_sampling()); + attr_normal.std = ATTR_STD_VERTEX_NORMAL; + attr_normal.element = ATTR_ELEMENT_VERTEX; + attr_normal.type_desc = TypeNormal; + + const array<float3> *vertices = + cache.vertices.data_for_time_no_check(current_time).get_data_or_null(); + const array<int3> *triangles = + cache.triangles.data_for_time_no_check(current_time).get_data_or_null(); + + if (!vertices || !triangles) { + attr_normal.data.add_no_data(current_time); + return; + } + + array<char> attr_data(vertices->size() * sizeof(float3)); + float3 *attr_ptr = reinterpret_cast<float3 *>(attr_data.data()); + memset(attr_ptr, 0, vertices->size() * sizeof(float3)); + + for (size_t t = 0; t < triangles->size(); ++t) { + const int3 tri_int3 = triangles->data()[t]; + Mesh::Triangle tri{}; + tri.v[0] = tri_int3[0]; + tri.v[1] = tri_int3[1]; + tri.v[2] = tri_int3[2]; + + const float3 tri_N = tri.compute_normal(vertices->data()); + + for (int v = 0; v < 3; ++v) { + attr_ptr[tri_int3[v]] += tri_N; + } + } + + for (size_t v = 0; v < vertices->size(); ++v) { + attr_ptr[v] = normalize(attr_ptr[v]); + } + + attr_normal.data.add_data(attr_data, current_time); +} + +static void add_normals(const Int32ArraySamplePtr face_indices, + const IN3fGeomParam &normals, + double time, + CachedData &cached_data) +{ + switch (normals.getScope()) { + case kFacevaryingScope: { + const ISampleSelector iss = ISampleSelector(time); + const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss); + + if (!sample.valid()) { + return; + } + + CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()), + *normals.getTimeSampling()); + attr.std = ATTR_STD_VERTEX_NORMAL; + + const array<float3> *vertices = + cached_data.vertices.data_for_time_no_check(time).get_data_or_null(); + + if (!vertices) { + return; + } + + array<char> data; + data.resize(vertices->size() * sizeof(float3)); + + float3 *data_float3 = reinterpret_cast<float3 *>(data.data()); + + const int *face_indices_array = face_indices->get(); + const N3fArraySamplePtr values = sample.getVals(); + + for (size_t i = 0; i < face_indices->size(); ++i) { + int point_index = face_indices_array[i]; + data_float3[point_index] = make_float3_from_yup(values->get()[i]); + } + + attr.data.add_data(data, time); + break; + } + case kVaryingScope: + case kVertexScope: { + const ISampleSelector iss = ISampleSelector(time); + const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss); + + if (!sample.valid()) { + return; + } + + CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()), + *normals.getTimeSampling()); + attr.std = ATTR_STD_VERTEX_NORMAL; + + const array<float3> *vertices = + cached_data.vertices.data_for_time_no_check(time).get_data_or_null(); + + if (!vertices) { + return; + } + + array<char> data; + data.resize(vertices->size() * sizeof(float3)); + + float3 *data_float3 = reinterpret_cast<float3 *>(data.data()); + + const Imath::V3f *values = sample.getVals()->get(); + + for (size_t i = 0; i < vertices->size(); ++i) { + data_float3[i] = make_float3_from_yup(values[i]); + } + + attr.data.add_data(data, time); + + break; + } + default: { + break; + } + } +} + +static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data) +{ + if (!positions) { + return; + } + + array<float3> vertices; + vertices.reserve(positions->size()); + + for (size_t i = 0; i < positions->size(); i++) { + V3f f = positions->get()[i]; + vertices.push_back_reserved(make_float3_from_yup(f)); + } + + cached_data.vertices.add_data(vertices, time); +} + +static void add_triangles(const Int32ArraySamplePtr face_counts, + const Int32ArraySamplePtr face_indices, + double time, + CachedData &cached_data, + const array<int> &polygon_to_shader) +{ + if (!face_counts || !face_indices) { + return; + } + + const size_t num_faces = face_counts->size(); + const int *face_counts_array = face_counts->get(); + const int *face_indices_array = face_indices->get(); + + size_t num_triangles = 0; + for (size_t i = 0; i < face_counts->size(); i++) { + num_triangles += face_counts_array[i] - 2; + } + + array<int> shader; + array<int3> triangles; + array<int> uv_loops; + shader.reserve(num_triangles); + triangles.reserve(num_triangles); + uv_loops.reserve(num_triangles * 3); + int index_offset = 0; + + for (size_t i = 0; i < num_faces; i++) { + int current_shader = 0; + + if (!polygon_to_shader.empty()) { + current_shader = polygon_to_shader[i]; + } + + for (int j = 0; j < face_counts_array[i] - 2; j++) { + int v0 = face_indices_array[index_offset]; + int v1 = face_indices_array[index_offset + j + 1]; + int v2 = face_indices_array[index_offset + j + 2]; + + shader.push_back_reserved(current_shader); + + /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */ + triangles.push_back_reserved(make_int3(v2, v1, v0)); + uv_loops.push_back_reserved(index_offset + j + 2); + uv_loops.push_back_reserved(index_offset + j + 1); + uv_loops.push_back_reserved(index_offset); + } + + index_offset += face_counts_array[i]; + } + + cached_data.triangles.add_data(triangles, time); + cached_data.uv_loops.add_data(uv_loops, time); + cached_data.shader.add_data(shader, time); +} + +static array<int> compute_polygon_to_shader_map( + const Int32ArraySamplePtr &face_counts, + const vector<FaceSetShaderIndexPair> &face_set_shader_index, + ISampleSelector sample_sel) +{ + if (face_set_shader_index.empty()) { + return {}; + } + + if (!face_counts) { + return {}; + } + + if (face_counts->size() == 0) { + return {}; + } + + array<int> polygon_to_shader(face_counts->size()); + + for (const FaceSetShaderIndexPair &pair : face_set_shader_index) { + const IFaceSet &face_set = pair.face_set; + const IFaceSetSchema face_schem = face_set.getSchema(); + const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel); + const Int32ArraySamplePtr group_faces = face_sample.getFaces(); + const size_t num_group_faces = group_faces->size(); + + for (size_t l = 0; l < num_group_faces; l++) { + size_t pos = (*group_faces)[l]; + + if (pos >= polygon_to_shader.size()) { + continue; + } + + polygon_to_shader[pos] = pair.shader_index; + } + } + + return polygon_to_shader; +} + +static void read_poly_mesh_geometry(CachedData &cached_data, + const PolyMeshSchemaData &data, + chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + add_positions(data.positions.getValue(iss), time, cached_data); + + const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss); + const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss); + + /* Only copy triangles for other frames if the topology is changing over time as well. */ + if (data.topology_variance != kHomogeneousTopology || cached_data.triangles.size() == 0) { + bool do_triangles = true; + + /* Compare key with last one to check whether the topology changed. */ + if (cached_data.triangles.size() > 0) { + const ArraySample::Key key = face_indices->getKey(); + + if (key == cached_data.triangles.key1) { + do_triangles = false; + } + + cached_data.triangles.key1 = key; + } + + if (do_triangles) { + const array<int> polygon_to_shader = compute_polygon_to_shader_map( + face_counts, data.shader_face_sets, iss); + add_triangles(face_counts, face_indices, time, cached_data, polygon_to_shader); + } + else { + cached_data.triangles.reuse_data_for_last_time(time); + cached_data.uv_loops.reuse_data_for_last_time(time); + cached_data.shader.reuse_data_for_last_time(time); + } + + /* Initialize the first key. */ + if (data.topology_variance != kHomogeneousTopology && cached_data.triangles.size() == 1) { + cached_data.triangles.key1 = face_indices->getKey(); + } + } + + if (data.normals.valid()) { + add_normals(face_indices, data.normals, time, cached_data); + } + else { + compute_vertex_normals(cached_data, time); + } +} + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const PolyMeshSchemaData &data, + Progress &progress) +{ + read_data_loop(proc, cached_data, data, read_poly_mesh_geometry, progress); +} + +/* Subdivision Geometries */ + +static void add_subd_polygons(CachedData &cached_data, const SubDSchemaData &data, chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss); + const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss); + + array<int> subd_start_corner; + array<int> shader; + array<int> subd_num_corners; + array<bool> subd_smooth; + array<int> subd_ptex_offset; + array<int> subd_face_corners; + array<int> uv_loops; + + const size_t num_faces = face_counts->size(); + const int *face_counts_array = face_counts->get(); + const int *face_indices_array = face_indices->get(); + + int num_ngons = 0; + int num_corners = 0; + for (size_t i = 0; i < face_counts->size(); i++) { + num_ngons += (face_counts_array[i] == 4 ? 0 : 1); + num_corners += face_counts_array[i]; + } + + subd_start_corner.reserve(num_faces); + subd_num_corners.reserve(num_faces); + subd_smooth.reserve(num_faces); + subd_ptex_offset.reserve(num_faces); + shader.reserve(num_faces); + subd_face_corners.reserve(num_corners); + uv_loops.reserve(num_corners); + + int start_corner = 0; + int current_shader = 0; + int ptex_offset = 0; + + const array<int> polygon_to_shader = compute_polygon_to_shader_map( + face_counts, data.shader_face_sets, iss); + + for (size_t i = 0; i < face_counts->size(); i++) { + num_corners = face_counts_array[i]; + + if (!polygon_to_shader.empty()) { + current_shader = polygon_to_shader[i]; + } + + subd_start_corner.push_back_reserved(start_corner); + subd_num_corners.push_back_reserved(num_corners); + + for (int j = 0; j < num_corners; ++j) { + subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]); + uv_loops.push_back_reserved(start_corner + j); + } + + shader.push_back_reserved(current_shader); + subd_smooth.push_back_reserved(1); + subd_ptex_offset.push_back_reserved(ptex_offset); + + ptex_offset += (num_corners == 4 ? 1 : num_corners); + + start_corner += num_corners; + } + + cached_data.shader.add_data(shader, time); + cached_data.subd_start_corner.add_data(subd_start_corner, time); + cached_data.subd_num_corners.add_data(subd_num_corners, time); + cached_data.subd_smooth.add_data(subd_smooth, time); + cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time); + cached_data.subd_face_corners.add_data(subd_face_corners, time); + cached_data.num_ngons.add_data(num_ngons, time); + cached_data.uv_loops.add_data(uv_loops, time); +} + +static void add_subd_creases(CachedData &cached_data, const SubDSchemaData &data, chrono_t time) +{ + if (!(data.crease_indices.valid() && data.crease_indices.valid() && + data.crease_sharpnesses.valid())) { + return; + } + + const ISampleSelector iss = ISampleSelector(time); + + const Int32ArraySamplePtr creases_length = data.crease_lengths.getValue(iss); + const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss); + const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss); + + if (creases_length && creases_indices && creases_sharpnesses) { + array<int> creases_edge; + array<float> creases_weight; + + creases_edge.reserve(creases_sharpnesses->size() * 2); + creases_weight.reserve(creases_sharpnesses->size()); + + int length_offset = 0; + int weight_offset = 0; + for (size_t c = 0; c < creases_length->size(); ++c) { + const int crease_length = creases_length->get()[c]; + + for (size_t j = 0; j < crease_length - 1; ++j) { + creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]); + creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]); + creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]); + } + + length_offset += crease_length; + } + + cached_data.subd_creases_edge.add_data(creases_edge, time); + cached_data.subd_creases_weight.add_data(creases_weight, time); + } +} + +static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &data, chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + add_positions(data.positions.getValue(iss), time, cached_data); + + if (data.topology_variance != kHomogenousTopology || cached_data.shader.size() == 0) { + add_subd_polygons(cached_data, data, time); + add_subd_creases(cached_data, data, time); + } +} + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const SubDSchemaData &data, + Progress &progress) +{ + read_data_loop(proc, cached_data, data, read_subd_geometry, progress); +} + +/* Curve Geometries. */ + +static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &data, chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + const Int32ArraySamplePtr curves_num_vertices = data.num_vertices.getValue(iss); + const P3fArraySamplePtr position = data.positions.getValue(iss); + + FloatArraySamplePtr radiuses; + + if (data.widths.valid()) { + IFloatGeomParam::Sample wsample = data.widths.getExpandedValue(iss); + radiuses = wsample.getVals(); + } + + const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1); + float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius; + + array<float3> curve_keys; + array<float> curve_radius; + array<int> curve_first_key; + array<int> curve_shader; + + const bool is_homogenous = data.topology_variance == kHomogenousTopology; + + curve_keys.reserve(position->size()); + curve_radius.reserve(position->size()); + curve_first_key.reserve(curves_num_vertices->size()); + curve_shader.reserve(curves_num_vertices->size()); + + int offset = 0; + for (size_t i = 0; i < curves_num_vertices->size(); i++) { + const int num_vertices = curves_num_vertices->get()[i]; + + for (int j = 0; j < num_vertices; j++) { + const V3f &f = position->get()[offset + j]; + // todo(@kevindietrich): we are reading too much data? + curve_keys.push_back_slow(make_float3_from_yup(f)); + + if (do_radius) { + radius = (*radiuses)[offset + j]; + } + + curve_radius.push_back_slow(radius * data.radius_scale); + } + + if (!is_homogenous || cached_data.curve_first_key.size() == 0) { + curve_first_key.push_back_reserved(offset); + curve_shader.push_back_reserved(0); + } + + offset += num_vertices; + } + + cached_data.curve_keys.add_data(curve_keys, time); + cached_data.curve_radius.add_data(curve_radius, time); + + if (!is_homogenous || cached_data.curve_first_key.size() == 0) { + cached_data.curve_first_key.add_data(curve_first_key, time); + cached_data.curve_shader.add_data(curve_shader, time); + } +} + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const CurvesSchemaData &data, + Progress &progress) +{ + read_data_loop(proc, cached_data, data, read_curves_data, progress); +} + +/* Attributes conversions. */ + +/* Type traits for converting between Alembic and Cycles types. + */ + +template<typename T> struct value_type_converter { + using cycles_type = float; + static constexpr TypeDesc type_desc = TypeFloat; + static constexpr const char *type_name = "float (default)"; + + static cycles_type convert_value(T value) + { + return static_cast<float>(value); + } +}; + +template<> struct value_type_converter<Imath::V2f> { + using cycles_type = float2; + static constexpr TypeDesc type_desc = TypeFloat2; + static constexpr const char *type_name = "float2"; + + static cycles_type convert_value(Imath::V2f value) + { + return make_float2(value.x, value.y); + } +}; + +template<> struct value_type_converter<Imath::V3f> { + using cycles_type = float3; + static constexpr TypeDesc type_desc = TypeVector; + static constexpr const char *type_name = "float3"; + + static cycles_type convert_value(Imath::V3f value) + { + return make_float3_from_yup(value); + } +}; + +template<> struct value_type_converter<Imath::C3f> { + using cycles_type = uchar4; + static constexpr TypeDesc type_desc = TypeRGBA; + static constexpr const char *type_name = "rgb"; + + static cycles_type convert_value(Imath::C3f value) + { + return color_float_to_byte(make_float3(value.x, value.y, value.z)); + } +}; + +template<> struct value_type_converter<Imath::C4f> { + using cycles_type = uchar4; + static constexpr TypeDesc type_desc = TypeRGBA; + static constexpr const char *type_name = "rgba"; + + static cycles_type convert_value(Imath::C4f value) + { + return color_float4_to_uchar4(make_float4(value.r, value.g, value.b, value.a)); + } +}; + +/* Main function used to read attributes of any type. */ +template<typename TRAIT> +static void process_attribute(CachedData &cache, + CachedData::CachedAttribute &attribute, + GeometryScope scope, + const typename ITypedGeomParam<TRAIT>::Sample &sample, + double time) +{ + using abc_type = typename TRAIT::value_type; + using cycles_type = typename value_type_converter<abc_type>::cycles_type; + + const TypedArraySample<TRAIT> &values = *sample.getVals(); + + switch (scope) { + case kConstantScope: + case kVertexScope: { + const array<float3> *vertices = + cache.vertices.data_for_time_no_check(time).get_data_or_null(); + + if (!vertices) { + attribute.data.add_no_data(time); + return; + } + + if (vertices->size() != values.size()) { + attribute.data.add_no_data(time); + return; + } + + array<char> data(vertices->size() * sizeof(cycles_type)); + + cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data()); + + for (size_t i = 0; i < values.size(); ++i) { + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[i]); + } + + attribute.data.add_data(data, time); + break; + } + case kVaryingScope: { + const array<int3> *triangles = + cache.triangles.data_for_time_no_check(time).get_data_or_null(); + + if (!triangles) { + attribute.data.add_no_data(time); + return; + } + + array<char> data(triangles->size() * 3 * sizeof(cycles_type)); + + cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data()); + + for (const int3 &tri : *triangles) { + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.x]); + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.y]); + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.z]); + } + + attribute.data.add_data(data, time); + break; + } + default: { + break; + } + } +} + +/* UVs are processed separately as their indexing is based on loops, instead of vertices or + * corners. */ +static void process_uvs(CachedData &cache, + CachedData::CachedAttribute &attribute, + GeometryScope scope, + const IV2fGeomParam::Sample &sample, + double time) +{ + if (scope != kFacevaryingScope) { + return; + } + + const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null(); + + if (!uv_loops) { + return; + } + + const array<int3> *triangles = cache.triangles.data_for_time_no_check(time).get_data_or_null(); + const array<int> *corners = + cache.subd_face_corners.data_for_time_no_check(time).get_data_or_null(); + + array<char> data; + if (triangles) { + data.resize(triangles->size() * 3 * sizeof(float2)); + } + else if (corners) { + data.resize(corners->size() * sizeof(float2)); + } + else { + return; + } + + float2 *data_float2 = reinterpret_cast<float2 *>(data.data()); + + const uint32_t *indices = sample.getIndices()->get(); + const V2f *values = sample.getVals()->get(); + + for (const int uv_loop_index : *uv_loops) { + const uint32_t index = indices[uv_loop_index]; + *data_float2++ = make_float2(values[index][0], values[index][1]); + } + + attribute.data.add_data(data, time); +} + +/* Type of the function used to parse one time worth of data, either process_uvs or + * process_attribute_generic. */ +template<typename TRAIT> +using process_callback_type = void (*)(CachedData &, + CachedData::CachedAttribute &, + GeometryScope, + const typename ITypedGeomParam<TRAIT>::Sample &, + double); + +/* Main loop to process the attributes, this will look at the given param's TimeSampling and + * extract data based on which frame time is requested by the procedural and execute the callback + * for each of those requested time. */ +template<typename TRAIT> +static void read_attribute_loop(AlembicProcedural *proc, + CachedData &cache, + const ITypedGeomParam<TRAIT> ¶m, + process_callback_type<TRAIT> callback, + Progress &progress, + AttributeStandard std = ATTR_STD_NONE) +{ + const std::set<chrono_t> times = get_relevant_sample_times( + proc, *param.getTimeSampling(), param.getNumSamples()); + + if (times.empty()) { + return; + } + + std::string name = param.getName(); + + if (std == ATTR_STD_UV) { + std::string uv_source_name = Alembic::Abc::GetSourceName(param.getMetaData()); + + /* According to the convention, primary UVs should have had their name + * set using Alembic::Abc::SetSourceName, but you can't expect everyone + * to follow it! :) */ + if (!uv_source_name.empty()) { + name = uv_source_name; + } + } + + CachedData::CachedAttribute &attribute = cache.add_attribute(ustring(name), + *param.getTimeSampling()); + + using abc_type = typename TRAIT::value_type; + + attribute.data.set_time_sampling(*param.getTimeSampling()); + attribute.std = std; + attribute.type_desc = value_type_converter<abc_type>::type_desc; + + if (attribute.type_desc == TypeRGBA) { + attribute.element = ATTR_ELEMENT_CORNER_BYTE; + } + else { + if (param.getScope() == kVaryingScope || param.getScope() == kFacevaryingScope) { + attribute.element = ATTR_ELEMENT_CORNER; + } + else { + attribute.element = ATTR_ELEMENT_VERTEX; + } + } + + for (const chrono_t time : times) { + if (progress.get_cancel()) { + return; + } + + ISampleSelector iss = ISampleSelector(time); + typename ITypedGeomParam<TRAIT>::Sample sample; + param.getIndexed(sample, iss); + + if (!sample.valid()) { + continue; + } + + if (!sample.getVals()) { + attribute.data.add_no_data(time); + continue; + } + + /* Check whether we already loaded constant data. */ + if (attribute.data.size() != 0) { + if (param.isConstant()) { + return; + } + + const ArraySample::Key indices_key = sample.getIndices()->getKey(); + const ArraySample::Key values_key = sample.getVals()->getKey(); + + const bool is_same_as_last_time = (indices_key == attribute.data.key1 && + values_key == attribute.data.key2); + + attribute.data.key1 = indices_key; + attribute.data.key2 = values_key; + + if (is_same_as_last_time) { + attribute.data.reuse_data_for_last_time(time); + continue; + } + } + + callback(cache, attribute, param.getScope(), sample, time); + } +} + +/* Attributes requests. */ + +/* This structure is used to tell which ICoumpoundProperty the PropertyHeader comes from, as we + * need the parent when downcasting to the proper type. */ +struct PropHeaderAndParent { + const PropertyHeader *prop; + ICompoundProperty parent; +}; + +/* Parse the ICompoundProperty to look for properties whose names appear in the + * AttributeRequestSet. This also looks into any child ICompoundProperty of the given + * ICompoundProperty. If no property of the given name is found, let it be that way, Cycles will + * use a zero value for the missing attribute. */ +static void parse_requested_attributes_recursive(const AttributeRequestSet &requested_attributes, + const ICompoundProperty &arb_geom_params, + vector<PropHeaderAndParent> &requested_properties) +{ + for (const AttributeRequest &req : requested_attributes.requests) { + const PropertyHeader *property_header = arb_geom_params.getPropertyHeader(req.name.c_str()); + + if (!property_header) { + continue; + } + + requested_properties.push_back({property_header, arb_geom_params}); + } + + /* Look into children compound properties. */ + for (size_t i = 0; i < arb_geom_params.getNumProperties(); ++i) { + const PropertyHeader &property_header = arb_geom_params.getPropertyHeader(i); + + if (property_header.isCompound()) { + ICompoundProperty compound_property = ICompoundProperty(arb_geom_params, + property_header.getName()); + parse_requested_attributes_recursive( + requested_attributes, compound_property, requested_properties); + } + } +} + +/* Main entry point for parsing requested attributes from an ICompoundProperty, this exists so that + * we can simply return the list of properties instead of allocating it on the stack and passing it + * as a parameter. */ +static vector<PropHeaderAndParent> parse_requested_attributes( + const AttributeRequestSet &requested_attributes, const ICompoundProperty &arb_geom_params) +{ + vector<PropHeaderAndParent> requested_properties; + parse_requested_attributes_recursive( + requested_attributes, arb_geom_params, requested_properties); + return requested_properties; +} + +/* Read the attributes requested by the shaders from the archive. This will recursively find named + * attributes from the AttributeRequestSet in the ICompoundProperty and any of its compound child. + * The attributes are added to the CachedData's attribute list. For each attribute we will try to + * deduplicate data across consecutive frames. */ +void read_attributes(AlembicProcedural *proc, + CachedData &cache, + const ICompoundProperty &arb_geom_params, + const IV2fGeomParam &default_uvs_param, + const AttributeRequestSet &requested_attributes, + Progress &progress) +{ + if (default_uvs_param.valid()) { + /* Only the default UVs should be treated as the standard UV attribute. */ + read_attribute_loop(proc, cache, default_uvs_param, process_uvs, progress, ATTR_STD_UV); + } + + vector<PropHeaderAndParent> requested_properties = parse_requested_attributes( + requested_attributes, arb_geom_params); + + for (const PropHeaderAndParent &prop_and_parent : requested_properties) { + if (progress.get_cancel()) { + return; + } + + const PropertyHeader *prop = prop_and_parent.prop; + const ICompoundProperty &parent = prop_and_parent.parent; + + if (IBoolGeomParam::matches(*prop)) { + const IBoolGeomParam ¶m = IBoolGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<BooleanTPTraits>, progress); + } + else if (IInt32GeomParam::matches(*prop)) { + const IInt32GeomParam ¶m = IInt32GeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<Int32TPTraits>, progress); + } + else if (IFloatGeomParam::matches(*prop)) { + const IFloatGeomParam ¶m = IFloatGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<Float32TPTraits>, progress); + } + else if (IV2fGeomParam::matches(*prop)) { + const IV2fGeomParam ¶m = IV2fGeomParam(parent, prop->getName()); + if (Alembic::AbcGeom::isUV(*prop)) { + read_attribute_loop(proc, cache, param, process_uvs, progress); + } + else { + read_attribute_loop(proc, cache, param, process_attribute<V2fTPTraits>, progress); + } + } + else if (IV3fGeomParam::matches(*prop)) { + const IV3fGeomParam ¶m = IV3fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<V3fTPTraits>, progress); + } + else if (IN3fGeomParam::matches(*prop)) { + const IN3fGeomParam ¶m = IN3fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<N3fTPTraits>, progress); + } + else if (IC3fGeomParam::matches(*prop)) { + const IC3fGeomParam ¶m = IC3fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<C3fTPTraits>, progress); + } + else if (IC4fGeomParam::matches(*prop)) { + const IC4fGeomParam ¶m = IC4fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<C4fTPTraits>, progress); + } + } + + cache.invalidate_last_loaded_time(true); +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/alembic_read.h b/intern/cycles/render/alembic_read.h new file mode 100644 index 00000000000..9cc8622a1ba --- /dev/null +++ b/intern/cycles/render/alembic_read.h @@ -0,0 +1,134 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_ALEMBIC + +# include <Alembic/AbcCoreFactory/All.h> +# include <Alembic/AbcGeom/All.h> + +# include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +class AlembicProcedural; +class AttributeRequestSet; +class Progress; +struct CachedData; + +/* Maps a FaceSet whose name matches that of a Shader to the index of said shader in the Geometry's + * used_shaders list. */ +struct FaceSetShaderIndexPair { + Alembic::AbcGeom::IFaceSet face_set; + int shader_index; +}; + +/* Data of an IPolyMeshSchema that we need to read. */ +struct PolyMeshSchemaData { + Alembic::AbcGeom::TimeSamplingPtr time_sampling; + size_t num_samples; + Alembic::AbcGeom::MeshTopologyVariance topology_variance; + + Alembic::AbcGeom::IP3fArrayProperty positions; + Alembic::AbcGeom::IInt32ArrayProperty face_indices; + Alembic::AbcGeom::IInt32ArrayProperty face_counts; + + Alembic::AbcGeom::IN3fGeomParam normals; + + vector<FaceSetShaderIndexPair> shader_face_sets; + + // Unsupported for now. + Alembic::AbcGeom::IV3fArrayProperty velocities; +}; + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const PolyMeshSchemaData &data, + Progress &progress); + +/* Data of an ISubDSchema that we need to read. */ +struct SubDSchemaData { + Alembic::AbcGeom::TimeSamplingPtr time_sampling; + size_t num_samples; + Alembic::AbcGeom::MeshTopologyVariance topology_variance; + + Alembic::AbcGeom::IInt32ArrayProperty face_counts; + Alembic::AbcGeom::IInt32ArrayProperty face_indices; + Alembic::AbcGeom::IP3fArrayProperty positions; + + Alembic::AbcGeom::IInt32ArrayProperty crease_indices; + Alembic::AbcGeom::IInt32ArrayProperty crease_lengths; + Alembic::AbcGeom::IFloatArrayProperty crease_sharpnesses; + + vector<FaceSetShaderIndexPair> shader_face_sets; + + // Those are unsupported for now. + Alembic::AbcGeom::IInt32ArrayProperty corner_indices; + Alembic::AbcGeom::IFloatArrayProperty corner_sharpnesses; + Alembic::AbcGeom::IInt32Property face_varying_interpolate_boundary; + Alembic::AbcGeom::IInt32Property face_varying_propagate_corners; + Alembic::AbcGeom::IInt32Property interpolate_boundary; + Alembic::AbcGeom::IInt32ArrayProperty holes; + Alembic::AbcGeom::IStringProperty subdivision_scheme; + Alembic::AbcGeom::IV3fArrayProperty velocities; +}; + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const SubDSchemaData &data, + Progress &progress); + +/* Data of a ICurvesSchema that we need to read. */ +struct CurvesSchemaData { + Alembic::AbcGeom::TimeSamplingPtr time_sampling; + size_t num_samples; + Alembic::AbcGeom::MeshTopologyVariance topology_variance; + + Alembic::AbcGeom::IP3fArrayProperty positions; + + Alembic::AbcGeom::IInt32ArrayProperty num_vertices; + + float default_radius; + float radius_scale; + + // Those are unsupported for now. + Alembic::AbcGeom::IV3fArrayProperty velocities; + // if this property is invalid then the weight for every point is 1 + Alembic::AbcGeom::IFloatArrayProperty position_weights; + Alembic::AbcGeom::IN3fGeomParam normals; + Alembic::AbcGeom::IFloatGeomParam widths; + Alembic::AbcGeom::IUcharArrayProperty orders; + Alembic::AbcGeom::IFloatArrayProperty knots; + + // TODO(@kevindietrich): type, basis, wrap +}; + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const CurvesSchemaData &data, + Progress &progress); + +void read_attributes(AlembicProcedural *proc, + CachedData &cache, + const Alembic::AbcGeom::ICompoundProperty &arb_geom_params, + const Alembic::AbcGeom::IV2fGeomParam &default_uvs_param, + const AttributeRequestSet &requested_attributes, + Progress &progress); + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp index f0a779da012..b925e755434 100644 --- a/intern/cycles/render/background.cpp +++ b/intern/cycles/render/background.cpp @@ -59,6 +59,7 @@ Background::Background() : Node(get_node_type()) Background::~Background() { + dereference_all_used_nodes(); } void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index bd2b8164cbb..aac5af06e9f 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -79,6 +79,7 @@ Geometry::Geometry(const NodeType *node_type, const Type type) Geometry::~Geometry() { + dereference_all_used_nodes(); delete bvh; } diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index a069b850512..5290d68e75a 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -159,6 +159,7 @@ NODE_DEFINE(Light) Light::Light() : Node(get_node_type()) { + dereference_all_used_nodes(); } void Light::tag_update(Scene *scene) diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 53c67049571..9bd6e3a5e2d 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -91,10 +91,10 @@ void OSLShaderManager::reset(Scene * /*scene*/) shading_system_init(); } -void OSLShaderManager::device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) +void OSLShaderManager::device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { if (!need_update()) return; @@ -1149,7 +1149,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) shader->has_integrator_dependency = false; /* generate surface shader */ - if (shader->used && graph && output->input("Surface")->link) { + if (shader->reference_count() && graph && output->input("Surface")->link) { shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE); if (has_bump) @@ -1165,7 +1165,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) } /* generate volume shader */ - if (shader->used && graph && output->input("Volume")->link) { + if (shader->reference_count() && graph && output->input("Volume")->link) { shader->osl_volume_ref = compile_type(shader, shader->graph, SHADER_TYPE_VOLUME); shader->has_volume = true; } @@ -1173,7 +1173,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) shader->osl_volume_ref = OSL::ShaderGroupRef(); /* generate displacement shader */ - if (shader->used && graph && output->input("Displacement")->link) { + if (shader->reference_count() && graph && output->input("Displacement")->link) { shader->osl_displacement_ref = compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT); shader->has_displacement = true; } diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index ea2d0ca492c..c9eaf26e70a 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -79,7 +79,10 @@ class OSLShaderManager : public ShaderManager { return true; } - void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); + void device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) override; void device_free(Device *device, DeviceScene *dscene, Scene *scene); /* osl compile and query */ diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 9713741d043..f753bb43c42 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -143,21 +143,27 @@ void Scene::free_memory(bool final) delete bvh; bvh = NULL; - foreach (Shader *s, shaders) - delete s; - /* delete procedurals before other types as they may hold pointers to those types */ + /* The order of deletion is important to make sure data is freed based on possible dependencies + * as the Nodes' reference counts are decremented in the destructors: + * + * - Procedurals can create and hold pointers to any other types. + * - Objects can hold pointers to Geometries and ParticleSystems + * - Lights and Geometries can hold pointers to Shaders. + * + * Similarly, we first delete all nodes and their associated device data, and then the managers + * and their associated device data. + */ foreach (Procedural *p, procedurals) delete p; - foreach (Geometry *g, geometry) - delete g; foreach (Object *o, objects) delete o; - foreach (Light *l, lights) - delete l; + foreach (Geometry *g, geometry) + delete g; foreach (ParticleSystem *p, particle_systems) delete p; + foreach (Light *l, lights) + delete l; - shaders.clear(); geometry.clear(); objects.clear(); lights.clear(); @@ -169,7 +175,25 @@ void Scene::free_memory(bool final) film->device_free(device, &dscene, this); background->device_free(device, &dscene); integrator->device_free(device, &dscene, true); + } + + if (final) { + delete camera; + delete dicing_camera; + delete film; + delete background; + delete integrator; + } + + /* Delete Shaders after every other nodes to ensure that we do not try to decrement the reference + * count on some dangling pointer. */ + foreach (Shader *s, shaders) + delete s; + + shaders.clear(); + /* Now that all nodes have been deleted, we can safely delete managers and device data. */ + if (device) { object_manager->device_free(device, &dscene, true); geometry_manager->device_free(device, &dscene, true); shader_manager->device_free(device, &dscene, this); @@ -189,11 +213,6 @@ void Scene::free_memory(bool final) if (final) { delete lookup_tables; - delete camera; - delete dicing_camera; - delete film; - delete background; - delete integrator; delete object_manager; delete geometry_manager; delete shader_manager; @@ -504,9 +523,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed) { /* update scene */ if (need_update()) { - /* Updated used shader tag so we know which features are need for the kernel. */ - shader_manager->update_shaders_used(this); - /* Update max_closures. */ KernelIntegrator *kintegrator = &dscene.data.integrator; if (params.background) { @@ -584,7 +600,7 @@ int Scene::get_max_closure_count() int max_closures = 0; for (int i = 0; i < shaders.size(); i++) { Shader *shader = shaders[i]; - if (shader->used) { + if (shader->reference_count()) { int num_closures = shader->graph->get_num_closures(); max_closures = max(max_closures, num_closures); } @@ -745,9 +761,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node) particle_system_manager->tag_update(this); } -template<> void Scene::delete_node_impl(Shader * /*node*/) +template<> void Scene::delete_node_impl(Shader *shader) { /* don't delete unused shaders, not supported */ + shader->clear_reference_count(); } template<> void Scene::delete_node_impl(Procedural *node) @@ -814,9 +831,12 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No particle_system_manager->tag_update(this); } -template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeOwner * /*owner*/) +template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner * /*owner*/) { /* don't delete unused shaders, not supported */ + for (Shader *shader : nodes) { + shader->clear_reference_count(); + } } template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner) diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 5ecbd92d96d..59b60904746 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -16,7 +16,6 @@ #include "device/device.h" -#include "render/alembic.h" #include "render/background.h" #include "render/camera.h" #include "render/colorspace.h" @@ -27,6 +26,7 @@ #include "render/nodes.h" #include "render/object.h" #include "render/osl.h" +#include "render/procedural.h" #include "render/scene.h" #include "render/shader.h" #include "render/svm.h" @@ -218,7 +218,6 @@ Shader::Shader() : Node(get_node_type()) displacement_method = DISPLACE_BUMP; id = -1; - used = false; need_update_uvs = true; need_update_attribute = true; @@ -382,8 +381,9 @@ void Shader::tag_used(Scene *scene) { /* if an unused shader suddenly gets used somewhere, it needs to be * recompiled because it was skipped for compilation before */ - if (!used) { + if (!reference_count()) { tag_modified(); + /* We do not reference here as the shader will be referenced when added to a socket. */ scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED); } } @@ -461,52 +461,28 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth) return id; } -void ShaderManager::update_shaders_used(Scene *scene) +void ShaderManager::device_update(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { if (!need_update()) { return; } - /* figure out which shaders are in use, so SVM/OSL can skip compiling them - * for speed and avoid loading image textures into memory */ uint id = 0; foreach (Shader *shader, scene->shaders) { - shader->used = false; shader->id = id++; } - scene->default_surface->used = true; - scene->default_light->used = true; - scene->default_background->used = true; - scene->default_empty->used = true; + /* Those shaders should always be compiled as they are used as fallback if a shader cannot be + * found, e.g. bad shader index for the triangle shaders on a Mesh. */ + assert(scene->default_surface->reference_count() != 0); + assert(scene->default_light->reference_count() != 0); + assert(scene->default_background->reference_count() != 0); + assert(scene->default_empty->reference_count() != 0); - if (scene->background->get_shader()) - scene->background->get_shader()->used = true; - -#ifdef WITH_ALEMBIC - foreach (Procedural *procedural, scene->procedurals) { - AlembicProcedural *abc_proc = static_cast<AlembicProcedural *>(procedural); - - foreach (Node *abc_node, abc_proc->get_objects()) { - AlembicObject *abc_object = static_cast<AlembicObject *>(abc_node); - - foreach (Node *node, abc_object->get_used_shaders()) { - Shader *shader = static_cast<Shader *>(node); - shader->used = true; - } - } - } -#endif - - foreach (Geometry *geom, scene->geometry) - foreach (Node *node, geom->get_used_shaders()) { - Shader *shader = static_cast<Shader *>(node); - shader->used = true; - } - - foreach (Light *light, scene->lights) - if (light->get_shader()) - const_cast<Shader *>(light->get_shader())->used = true; + device_update_specific(device, dscene, scene, progress); } void ShaderManager::device_update_common(Device *device, @@ -528,6 +504,8 @@ void ShaderManager::device_update_common(Device *device, if (shader->get_use_mis()) flag |= SD_USE_MIS; + if (shader->has_surface_emission) + flag |= SD_HAS_EMISSION; if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) flag |= SD_HAS_TRANSPARENT_SHADOW; if (shader->has_volume) { @@ -637,6 +615,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_surface"; shader->set_graph(graph); + shader->reference(); scene->default_surface = shader; shader->tag_update(scene); } @@ -655,6 +634,8 @@ void ShaderManager::add_default(Scene *scene) shader->set_graph(graph); scene->default_volume = shader; shader->tag_update(scene); + /* No default reference for the volume to avoid compiling volume kernels if there are no actual + * volumes in the scene */ } /* default light */ @@ -671,6 +652,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_light"; shader->set_graph(graph); + shader->reference(); scene->default_light = shader; shader->tag_update(scene); } @@ -682,6 +664,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_background"; shader->set_graph(graph); + shader->reference(); scene->default_background = shader; shader->tag_update(scene); } @@ -693,6 +676,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_empty"; shader->set_graph(graph); + shader->reference(); scene->default_empty = shader; shader->tag_update(scene); } @@ -733,7 +717,7 @@ void ShaderManager::get_requested_features(Scene *scene, requested_features->nodes_features = 0; for (int i = 0; i < scene->shaders.size(); i++) { Shader *shader = scene->shaders[i]; - if (!shader->used) { + if (!shader->reference_count()) { continue; } diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index f47d64f346c..50c8bed4669 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -132,7 +132,6 @@ class Shader : public Node { /* determined before compiling */ uint id; - bool used; #ifdef WITH_OSL /* osl shading state references */ @@ -187,10 +186,11 @@ class ShaderManager { } /* device update */ - virtual void device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) = 0; + void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); + virtual void device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) = 0; virtual void device_free(Device *device, DeviceScene *dscene, Scene *scene) = 0; void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); @@ -208,7 +208,6 @@ class ShaderManager { static void add_default(Scene *scene); /* Selective nodes compilation. */ - void update_shaders_used(Scene *scene); void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features); static void free_memory(); diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index fce604234f1..5c793c5c016 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -69,10 +69,10 @@ void SVMShaderManager::device_update_shader(Scene *scene, << summary.full_report(); } -void SVMShaderManager::device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) +void SVMShaderManager::device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { if (!need_update()) return; @@ -776,7 +776,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset); } - if (shader->used) { + if (shader->reference_count()) { CompilerState state(graph); if (clin->link) { bool generate = false; diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index a4ca68e1d8d..85b4b9c419f 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -46,7 +46,10 @@ class SVMShaderManager : public ShaderManager { void reset(Scene *scene); - void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); + void device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) override; void device_free(Device *device, DeviceScene *dscene, Scene *scene); protected: diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h index 107b36ce6cd..5ae56290f05 100644 --- a/intern/cycles/util/util_math_fast.h +++ b/intern/cycles/util/util_math_fast.h @@ -362,7 +362,7 @@ ccl_device float fast_atan2f(float y, float x) ccl_device float fast_log2f(float x) { /* NOTE: clamp to avoid special cases and make result "safe" from large - * negative values/nans. */ + * negative values/NAN's. */ x = clamp(x, FLT_MIN, FLT_MAX); unsigned bits = __float_as_uint(x); int exponent = (int)(bits >> 23) - 127; diff --git a/intern/cycles/util/util_simd.h b/intern/cycles/util/util_simd.h index 718ec9266b1..8e8caa98a1b 100644 --- a/intern/cycles/util/util_simd.h +++ b/intern/cycles/util/util_simd.h @@ -124,7 +124,7 @@ static struct StepTy { template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const type &a) { if (i0 == i1 && i0 == i2 && i0 == i3) { - return vdupq_laneq_s32(a, i0); + return type(vdupq_laneq_s32(int32x4_t(a), i0)); } static const uint8_t tbl[16] = {(i0 * 4) + 0, (i0 * 4) + 1, @@ -143,7 +143,7 @@ template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const typ (i3 * 4) + 2, (i3 * 4) + 3}; - return vqtbl1q_s8(int8x16_t(a), *(int8x16_t *)tbl); + return type(vqtbl1q_s8(int8x16_t(a), *(uint8x16_t *)tbl)); } template<class type, int i0, int i1, int i2, int i3> @@ -167,7 +167,7 @@ type shuffle_neon(const type &a, const type &b) (i3 * 4) + 2, (i3 * 4) + 3}; - return vqtbl1q_s8(int8x16_t(b), *(int8x16_t *)tbl); + return type(vqtbl1q_s8(int8x16_t(b), *(uint8x16_t *)tbl)); } else { @@ -188,7 +188,7 @@ type shuffle_neon(const type &a, const type &b) (i3 * 4) + 2 + 16, (i3 * 4) + 3 + 16}; - return vqtbl2q_s8((int8x16x2_t){a, b}, *(int8x16_t *)tbl); + return type(vqtbl2q_s8((int8x16x2_t){int8x16_t(a), int8x16_t(b)}, *(uint8x16_t *)tbl)); } } #endif /* __KERNEL_NEON */ diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/util_sseb.h index 1488da46b09..4dbd5b8046e 100644 --- a/intern/cycles/util/util_sseb.h +++ b/intern/cycles/util/util_sseb.h @@ -283,7 +283,7 @@ __forceinline uint32_t popcnt(const sseb &a) { # if defined(__KERNEL_NEON__) const int32x4_t mask = {1, 1, 1, 1}; - int32x4_t t = vandq_s32(a.m128, mask); + int32x4_t t = vandq_s32(vreinterpretq_s32_m128(a.m128), mask); return vaddvq_s32(t); # else return _mm_popcnt_u32(_mm_movemask_ps(a)); @@ -299,7 +299,7 @@ __forceinline uint32_t popcnt(const sseb &a) __forceinline bool reduce_and(const sseb &a) { # if defined(__KERNEL_NEON__) - return vaddvq_s32(a.m128) == -4; + return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) == -4; # else return _mm_movemask_ps(a) == 0xf; # endif @@ -307,7 +307,7 @@ __forceinline bool reduce_and(const sseb &a) __forceinline bool reduce_or(const sseb &a) { # if defined(__KERNEL_NEON__) - return vaddvq_s32(a.m128) != 0x0; + return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) != 0x0; # else return _mm_movemask_ps(a) != 0x0; # endif @@ -315,7 +315,7 @@ __forceinline bool reduce_or(const sseb &a) __forceinline bool all(const sseb &b) { # if defined(__KERNEL_NEON__) - return vaddvq_s32(b.m128) == -4; + return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == -4; # else return _mm_movemask_ps(b) == 0xf; # endif @@ -323,7 +323,7 @@ __forceinline bool all(const sseb &b) __forceinline bool any(const sseb &b) { # if defined(__KERNEL_NEON__) - return vaddvq_s32(b.m128) != 0x0; + return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) != 0x0; # else return _mm_movemask_ps(b) != 0x0; # endif @@ -331,7 +331,7 @@ __forceinline bool any(const sseb &b) __forceinline bool none(const sseb &b) { # if defined(__KERNEL_NEON__) - return vaddvq_s32(b.m128) == 0x0; + return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == 0x0; # else return _mm_movemask_ps(b) == 0x0; # endif diff --git a/intern/cycles/util/util_ssef.h b/intern/cycles/util/util_ssef.h index d039b50a7d2..0c81ed87553 100644 --- a/intern/cycles/util/util_ssef.h +++ b/intern/cycles/util/util_ssef.h @@ -596,7 +596,7 @@ template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssef shuffle(const ssef &b) { # ifdef __KERNEL_NEON__ - return shuffle_neon<ssef, i0, i1, i2, i3>(b.m128); + return shuffle_neon<float32x4_t, i0, i1, i2, i3>(b.m128); # else return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(b), _MM_SHUFFLE(i3, i2, i1, i0))); # endif @@ -625,7 +625,7 @@ __forceinline const ssef shuffle(const ssef &a, const ssef &b) template<size_t i0> __forceinline const ssef shuffle(const ssef &a, const ssef &b) { # ifdef __KERNEL_NEON__ - return shuffle<float32x4_t, i0, i0, i0, i0>(a, b); + return shuffle_neon<float32x4_t, i0, i0, i0, i0>(a, b); # else return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i0, i0, i0, i0)); # endif diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/util_ssei.h index 3ec69ab3700..cd51dbff2f1 100644 --- a/intern/cycles/util/util_ssei.h +++ b/intern/cycles/util/util_ssei.h @@ -446,7 +446,8 @@ template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssei shuffle(const ssei &a) { # ifdef __KERNEL_NEON__ - return shuffle_neon<ssei, i0, i1, i2, i3>(a); + int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(result); # else return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0)); # endif @@ -456,7 +457,9 @@ template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssei shuffle(const ssei &a, const ssei &b) { # ifdef __KERNEL_NEON__ - return shuffle_neon<ssei, i0, i1, i2, i3>(a, b); + int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a), + vreinterpretq_s32_m128i(b)); + return vreinterpretq_m128i_s32(result); # else return _mm_castps_si128( _mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(i3, i2, i1, i0))); @@ -514,7 +517,7 @@ __forceinline const ssei vreduce_add(const ssei &v) __forceinline int reduce_min(const ssei &v) { # ifdef __KERNEL_NEON__ - return vminvq_s32(v); + return vminvq_s32(vreinterpretq_s32_m128i(v)); # else return extract<0>(vreduce_min(v)); # endif @@ -522,7 +525,7 @@ __forceinline int reduce_min(const ssei &v) __forceinline int reduce_max(const ssei &v) { # ifdef __KERNEL_NEON__ - return vmaxvq_s32(v); + return vmaxvq_s32(vreinterpretq_s32_m128i(v)); # else return extract<0>(vreduce_max(v)); # endif @@ -530,7 +533,7 @@ __forceinline int reduce_max(const ssei &v) __forceinline int reduce_add(const ssei &v) { # ifdef __KERNEL_NEON__ - return vaddvq_s32(v); + return vaddvq_s32(vreinterpretq_s32_m128i(v)); # else return extract<0>(vreduce_add(v)); # endif diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h index b445ab1488f..71bf9c65911 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/util_texture.h @@ -21,18 +21,12 @@ CCL_NAMESPACE_BEGIN -/* Texture limits on devices. */ -#define TEX_NUM_MAX (INT_MAX >> 4) - /* Color to use when textures are not found. */ #define TEX_IMAGE_MISSING_R 1 #define TEX_IMAGE_MISSING_G 0 #define TEX_IMAGE_MISSING_B 1 #define TEX_IMAGE_MISSING_A 1 -/* Texture type. */ -#define kernel_tex_type(tex) (tex & IMAGE_DATA_TYPE_MASK) - /* Interpolation types for textures * cuda also use texture space to store other objects */ typedef enum InterpolationType { @@ -45,9 +39,6 @@ typedef enum InterpolationType { INTERPOLATION_NUM_TYPES, } InterpolationType; -/* Texture types - * Since we store the type in the lower bits of a flat index, - * the shift and bit mask constant below need to be kept in sync. */ typedef enum ImageDataType { IMAGE_DATA_TYPE_FLOAT4 = 0, IMAGE_DATA_TYPE_BYTE4 = 1, @@ -75,9 +66,6 @@ typedef enum ImageAlphaType { IMAGE_ALPHA_NUM_TYPES, } ImageAlphaType; -#define IMAGE_DATA_TYPE_SHIFT 4 -#define IMAGE_DATA_TYPE_MASK 0xF - /* Extension types for textures. * * Defines how the image is extrapolated past its original bounds. */ diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index d79111b742f..c776eb6b44c 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -276,7 +276,7 @@ extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle); * wait (block) until the next event before returning. * \return Indication of the presence of events. */ -extern int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent); +extern bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent); /** * Retrieves events from the queue and send them to the event consumers. diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index bd7ba6657f0..7d819913efc 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -416,7 +416,10 @@ typedef enum { GHOST_kGrabNormal, /** Wrap the mouse location to prevent limiting screen bounds. */ GHOST_kGrabWrap, - /** Hide the mouse while grabbing and restore the original location on release (numbuts). */ + /** + * Hide the mouse while grabbing and restore the original location on release + * (used for number buttons and some other draggable UI elements). + */ GHOST_kGrabHide, } GHOST_TGrabCursorMode; diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index af65e1cd4d2..1d96354c504 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -248,11 +248,11 @@ int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle) return (int)system->getFullScreen(); } -int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent) +bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; - return (int)system->processEvents(waitForEvent ? true : false); + return system->processEvents(waitForEvent); } void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle) diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp index 82ed9de230d..a62db31c952 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cpp +++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp @@ -112,8 +112,7 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11() } } -/* based on a code from Saul Rennison - * http://stackoverflow.com/questions/2673207/c-c-url-decode-library */ +/* Based on: https://stackoverflow.com/a/2766963/432509 */ typedef enum DecodeState_e { STATE_SEARCH = 0, ///< searching for an ampersand to convert diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 050c90097c5..6ddb7f031f5 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1711,7 +1711,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) dx = [event scrollingDeltaX]; dy = [event scrollingDeltaY]; - /* However, wacom tablet (intuos5) needs old deltas, + /* However, Wacom tablet (intuos5) needs old deltas, * it then has momentum and phase at zero. */ if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) { dx = [event deltaX]; diff --git a/intern/ghost/test/gears/GHOST_C-Test.c b/intern/ghost/test/gears/GHOST_C-Test.c index 4283f990cfb..8cd1b5acb89 100644 --- a/intern/ghost/test/gears/GHOST_C-Test.c +++ b/intern/ghost/test/gears/GHOST_C-Test.c @@ -477,7 +477,7 @@ int main(int argc, char **argv) /* Enter main loop */ while (!sExitRequested) { - if (!GHOST_ProcessEvents(shSystem, 0)) { + if (!GHOST_ProcessEvents(shSystem, false)) { #ifdef WIN32 /* If there were no events, be nice to other applications */ Sleep(10); diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c index 8c8858fc6d8..3b424f1ca89 100644 --- a/intern/ghost/test/multitest/MultiTest.c +++ b/intern/ghost/test/multitest/MultiTest.c @@ -926,7 +926,7 @@ void multitestapp_exit(MultiTestApp *app) void multitestapp_run(MultiTestApp *app) { while (!app->exit) { - GHOST_ProcessEvents(app->sys, 1); + GHOST_ProcessEvents(app->sys, true); GHOST_DispatchEvents(app->sys); } } diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex 70a68ce6847..babb3e30c6d 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 4ea8c88e8d9..aa540eeb23b 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -657,16 +657,14 @@ class Gizmo(StructRNA): use_blend = color[3] < 1.0 if use_blend: - # TODO: wrap GPU_blend from GPU state. - from bgl import glEnable, glDisable, GL_BLEND - glEnable(GL_BLEND) + gpu.state.blend_set('ALPHA') with gpu.matrix.push_pop(): gpu.matrix.multiply_matrix(matrix) batch.draw() if use_blend: - glDisable(GL_BLEND) + gpu.state.blend_set('NONE') @staticmethod def new_custom_shape(type, verts): diff --git a/release/scripts/modules/gpu_extras/presets.py b/release/scripts/modules/gpu_extras/presets.py index 81d515904a1..f490e1e74ba 100644 --- a/release/scripts/modules/gpu_extras/presets.py +++ b/release/scripts/modules/gpu_extras/presets.py @@ -57,12 +57,12 @@ def draw_circle_2d(position, color, radius, segments=32): batch.draw() -def draw_texture_2d(texture_id, position, width, height): +def draw_texture_2d(texture, position, width, height): """ Draw a 2d texture. - :arg texture_id: OpenGL id of the texture (e.g. :class:`bpy.types.Image.bindcode`). - :type texture_id: int + :arg texture: GPUTexture to draw (e.g. gpu.texture.from_image(image) for :class:`bpy.types.Image`). + :type texture: :class:`gpu.types.GPUTexture` :arg position: Position of the lower left corner. :type position: 2D Vector :arg width: Width of the image when drawn (not necessarily @@ -72,7 +72,6 @@ def draw_texture_2d(texture_id, position, width, height): :type height: float """ import gpu - import bgl from . batch import batch_for_shader coords = ((0, 0), (1, 0), (1, 1), (0, 1)) @@ -83,14 +82,20 @@ def draw_texture_2d(texture_id, position, width, height): {"pos": coords, "texCoord": coords}, ) - bgl.glActiveTexture(bgl.GL_TEXTURE0) - bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture_id) - with gpu.matrix.push_pop(): gpu.matrix.translate(position) gpu.matrix.scale((width, height)) shader = gpu.shader.from_builtin('2D_IMAGE') shader.bind() - shader.uniform_int("image", 0) + + if isinstance(texture, int): + # Call the legacy bgl to not break the existing API + import bgl + bgl.glActiveTexture(bgl.GL_TEXTURE0) + bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture) + shader.uniform_int("image", 0) + else: + shader.uniform_sampler("image", texture) + batch.draw(shader) diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py index 190f0282339..b7a15bbbc19 100644 --- a/release/scripts/modules/keyingsets_utils.py +++ b/release/scripts/modules/keyingsets_utils.py @@ -219,6 +219,40 @@ def RKS_GEN_scaling(_ksi, _context, ks, data): else: ks.paths.add(id_block, path) + +# Custom Properties +def RKS_GEN_custom_props(_ksi, _context, ks, data): + # get id-block and path info + id_block, base_path, grouping = get_transform_generators_base_info(data) + + # Only some RNA types can be animated. + prop_type_compat = {bpy.types.BoolProperty, + bpy.types.IntProperty, + bpy.types.FloatProperty} + + # When working with a pose, 'id_block' is the armature object (which should + # get the animation data), whereas 'data' is the bone being keyed. + for cprop_name in data.keys(): + # ignore special "_RNA_UI" used for UI editing + if cprop_name == "_RNA_UI": + continue + + prop_path = '["%s"]' % bpy.utils.escape_identifier(cprop_name) + try: + rna_property = data.path_resolve(prop_path, False) + except ValueError as ex: + # This happens when a custom property is set to None. In that case it cannot + # be converted to an FCurve-compatible value, so we can't keyframe it anyway. + continue + if rna_property.rna_type not in prop_type_compat: + continue + + path = "%s%s" % (base_path, prop_path) + if grouping: + ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path) + # ------ diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index b6fda8911cc..4478b0dc367 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -2115,4 +2115,9 @@ url_manual_mapping = ( ("bpy.ops.ed*", "interface/undo_redo.html#bpy-ops-ed"), ("bpy.ops.ui*", "interface/index.html#bpy-ops-ui"), ("bpy.ops.wm*", "interface/index.html#bpy-ops-wm"), + ("bpy.ops.file.pack_all", "blend/packed_data#pack-resources"), + ("bpy.ops.file.unpack_all", "blend/packed_data#unpack-resources"), + ("bpy.ops.file.autopack_toggle", "blend/packed_data#automatically-pack-resources"), + ("bpy.ops.file.pack_libraries", "blend/packed_data#pack-linked-libraries"), + ("bpy.ops.file.unpack_libraries", "blend/packed_data#unpack-linked-libraries"), ) diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py index cb80529f0f3..5116e0f0088 100644 --- a/release/scripts/modules/sys_info.py +++ b/release/scripts/modules/sys_info.py @@ -23,6 +23,7 @@ def write_sysinfo(filepath): import sys + import platform import subprocess @@ -63,7 +64,7 @@ def write_sysinfo(filepath): )) output.write("build date: %s, %s\n" % (prepr(bpy.app.build_date), prepr(bpy.app.build_time))) - output.write("platform: %s\n" % prepr(bpy.app.build_platform)) + output.write("platform: %s\n" % prepr(platform.platform())) output.write("binary path: %s\n" % prepr(bpy.app.binary_path)) output.write("build cflags: %s\n" % prepr(bpy.app.build_cflags)) output.write("build cxxflags: %s\n" % prepr(bpy.app.build_cxxflags)) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 9404bfe327a..8d0a5806adc 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2009,8 +2009,7 @@ def km_file_browser_main(params): ) items.extend([ - ("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, - {"properties": [("need_active", True)]}), + ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), # Both .execute and .select are needed here. The former only works if # there's a file operator (i.e. not in regular editor mode) but is # needed to load files. The latter makes selection work if there's no diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 91f153a0f42..31f2be57e38 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1263,8 +1263,7 @@ def km_file_browser_main(params): ) items.extend([ - ("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, - {"properties": [("need_active", True)]}), + ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 7547184dc04..f77f00d2234 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -99,6 +99,15 @@ class PREFERENCES_OT_copy_prev(Operator): version = bpy.app.version version_new = ((version[0] * 100) + version[1]) version_old = ((version[0] * 100) + version[1]) - 1 + + # Special case, remove when the version is > 3.0. + if version_new == 300: + version_new = 294 + version_old = 293 + else: + print("TODO: remove exception!") + # End special case. + # Ensure we only try to copy files from a point release. # The check below ensures the second numbers match. while (version_new % 100) // 10 == (version_old % 100) // 10: diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index c1d60a127d2..3064b33c7f7 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -821,6 +821,12 @@ class GreasePencilLayerMasksPanel: col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="") col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="") + col2.separator() + + sub = col2.column(align=True) + sub.operator("gpencil.layer_mask_move", icon='TRIA_UP', text="").type = 'UP' + sub.operator("gpencil.layer_mask_move", icon='TRIA_DOWN', text="").type = 'DOWN' + class GreasePencilLayerRelationsPanel: diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index aca7ba3c5ad..217ec248764 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -286,19 +286,17 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True mat = context.material lineart = mat.lineart layout.prop(lineart, "use_transparency") - if lineart.use_transparency: - - layout.label(text="Transparency Masks:") - - row = layout.row(align=True) - for i in range(8): - row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True) + row = layout.row(align=True, heading="Masks") + row.active = lineart.use_transparency + for i in range(8): + row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True) classes = ( diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index 033e6196323..4ea1ec26738 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -329,7 +329,9 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel): row = layout.row(heading="Override Crease") row.prop(lineart, "use_crease_override", text="") - row.prop(lineart, "crease_threshold", slider=True, text="") + subrow = row.row() + subrow.active = lineart.use_crease_override + subrow.prop(lineart, "crease_threshold", slider=True, text="") class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 22ef0fe77dd..4e3e4c2d58d 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1225,7 +1225,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row = layout.row(align=True) row.prop(gp_settings, "fill_factor") row = layout.row(align=True) - row.prop(gp_settings, "fill_leak", text="Leak Size") + row.prop(gp_settings, "dilate_pixels") row = layout.row(align=True) row.prop(brush, "size", text="Thickness") layout.use_property_split = use_property_split_prev diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index cd65980fc0d..3a97b104271 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -92,16 +92,15 @@ class INFO_MT_area(Menu): layout.separator() - layout.operator("screen.area_dupli", icon='WINDOW') - - layout.separator() - layout.operator("screen.screen_full_area") layout.operator( "screen.screen_full_area", - text="Toggle Fullscreen Area", - icon='FULLSCREEN_ENTER', - ).use_hide_panels = True + text="Toggle Fullscreen Area").use_hide_panels = True + layout.operator("screen.area_dupli") + + layout.separator() + + layout.operator("screen.area_close") class INFO_MT_context_menu(Menu): diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index adab0b0c88a..5e68896a2a7 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -504,8 +504,6 @@ class TOPBAR_MT_file_external_data(Menu): icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT' layout.operator("file.autopack_toggle", icon=icon) - layout.separator() - pack_all = layout.row() pack_all.operator("file.pack_all") pack_all.active = not bpy.data.use_autopack @@ -516,8 +514,16 @@ class TOPBAR_MT_file_external_data(Menu): layout.separator() + layout.operator("file.pack_libraries") + layout.operator("file.unpack_libraries") + + layout.separator() + layout.operator("file.make_paths_relative") layout.operator("file.make_paths_absolute") + + layout.separator() + layout.operator("file.report_missing_files") layout.operator("file.find_missing_files") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 04b7a11bde1..de78b88c0f6 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -570,7 +570,7 @@ class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel): layout.prop(system, "audio_device", expand=False) sub = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False) - sub.active = system.audio_device not in {'NONE', 'Null'} + sub.active = system.audio_device not in {'NONE', 'None'} sub.prop(system, "audio_channels", text="Channels") sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer") sub.prop(system, "audio_sample_rate", text="Sample Rate") diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index ab012a6f2ef..08d581bfa24 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1468,6 +1468,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): row.prop(gp_settings, "show_fill_extend", text="", icon='GRID') col.separator() + col.prop(gp_settings, "fill_leak", text="Leak Size") + + col.separator() col.prop(gp_settings, "fill_simplify_level", text="Simplify") if gp_settings.fill_draw_mode != 'STROKE': col = layout.column(align=False, heading="Ignore Transparent") diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py index 012febc7cc7..83151a3480c 100644 --- a/release/scripts/startup/keyingsets_builtins.py +++ b/release/scripts/startup/keyingsets_builtins.py @@ -44,6 +44,7 @@ ANIM_KS_LOCATION_ID = "Location" ANIM_KS_ROTATION_ID = "Rotation" ANIM_KS_SCALING_ID = "Scaling" ANIM_KS_LOC_ROT_SCALE_ID = "LocRotScale" +ANIM_KS_LOC_ROT_SCALE_CPROP_ID = "LocRotScaleCProp" ANIM_KS_AVAILABLE_ID = "Available" ANIM_KS_WHOLE_CHARACTER_ID = "WholeCharacter" ANIM_KS_WHOLE_CHARACTER_SELECTED_ID = "WholeCharacterSelected" @@ -159,6 +160,22 @@ class BUILTIN_KSI_LocRotScale(KeyingSetInfo): keyingsets_utils.RKS_GEN_scaling(self, context, ks, data) +# LocRotScaleCProp +class BUILTIN_KSI_LocRotScaleCProp(KeyingSetInfo): + """Key location/rotation/scale as well as custom properties""" + bl_idname = ANIM_KS_LOC_ROT_SCALE_CPROP_ID + bl_label = "Location, Rotation, Scale & Custom Properties" + + poll = keyingsets_utils.RKS_POLL_selected_items + iterator = keyingsets_utils.RKS_ITER_selected_item + + def generate(self, context, ks, data): + keyingsets_utils.RKS_GEN_location(self, context, ks, data) + keyingsets_utils.RKS_GEN_rotation(self, context, ks, data) + keyingsets_utils.RKS_GEN_scaling(self, context, ks, data) + keyingsets_utils.RKS_GEN_custom_props(self, context, ks, data) + + # RotScale class BUILTIN_KSI_RotScale(KeyingSetInfo): """Insert a keyframe on each of the rotation and scale channels""" @@ -350,7 +367,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo): bl_label = "Available" # poll - selected objects or selected object with animation data - def poll(ksi, context): + def poll(self, context): ob = context.active_object if ob: # TODO: this fails if one animation-less object is active, but many others are selected @@ -366,14 +383,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo): ############################### - -# All properties that are likely to get animated in a character rig -class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): - """Insert a keyframe for all properties that are likely to get animated in a character rig """ \ - """(useful when blocking out a shot)""" - bl_idname = ANIM_KS_WHOLE_CHARACTER_ID - bl_label = "Whole Character" - +class WholeCharacterMixin: # these prefixes should be avoided, as they are not really bones # that animators should be touching (or need to touch) badBonePrefixes = ( @@ -387,38 +397,37 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): ) # poll - pose-mode on active object only - def poll(ksi, context): + def poll(self, context): return ((context.active_object) and (context.active_object.pose) and (context.active_object.mode == 'POSE')) # iterator - all bones regardless of selection - def iterator(ksi, context, ks): + def iterator(self, context, ks): for bone in context.active_object.pose.bones: - if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes): - ksi.generate(context, ks, bone) + if not bone.name.startswith(self.badBonePrefixes): + self.generate(context, ks, bone) # generator - all unlocked bone transforms + custom properties - def generate(ksi, context, ks, bone): + def generate(self, context, ks, bone): # loc, rot, scale - only include unlocked ones if not bone.bone.use_connect: - ksi.doLoc(ks, bone) + self.doLoc(ks, bone) if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}: - ksi.doRot4d(ks, bone) + self.doRot4d(ks, bone) else: - ksi.doRot3d(ks, bone) - ksi.doScale(ks, bone) + self.doRot3d(ks, bone) + self.doScale(ks, bone) # bbone properties? - ksi.doBBone(context, ks, bone) + self.doBBone(context, ks, bone) # custom props? - ksi.doCustomProps(ks, bone) - + self.doCustomProps(ks, bone) # ---------------- # helper to add some bone's property to the Keying Set - def addProp(ksi, ks, bone, prop, index=-1, use_groups=True): + def addProp(self, ks, bone, prop, index=-1, use_groups=True): # add the property name to the base path id_path = bone.path_from_id() id_block = bone.id_data @@ -439,16 +448,16 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): # ---------------- # location properties - def doLoc(ksi, ks, bone): + def doLoc(self, ks, bone): if bone.lock_location == (False, False, False): - ksi.addProp(ks, bone, "location") + self.addProp(ks, bone, "location") else: for i in range(3): if not bone.lock_location[i]: - ksi.addProp(ks, bone, "location", i) + self.addProp(ks, bone, "location", i) # rotation properties - def doRot4d(ksi, ks, bone): + def doRot4d(self, ks, bone): # rotation mode affects the property used if bone.rotation_mode == 'QUATERNION': prop = "rotation_quaternion" @@ -459,40 +468,40 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): if bone.lock_rotations_4d: # can check individually if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w is False): - ksi.addProp(ks, bone, prop) + self.addProp(ks, bone, prop) else: if bone.lock_rotation_w is False: - ksi.addProp(ks, bone, prop, 0) # w = 0 + self.addProp(ks, bone, prop, 0) # w = 0 for i in range(3): if not bone.lock_rotation[i]: - ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0 + self.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0 elif True not in bone.lock_rotation: # if axis-angle rotations get locked as eulers, then it's too messy to allow anything # other than all open unless we keyframe the whole lot - ksi.addProp(ks, bone, prop) + self.addProp(ks, bone, prop) - def doRot3d(ksi, ks, bone): + def doRot3d(self, ks, bone): if bone.lock_rotation == (False, False, False): - ksi.addProp(ks, bone, "rotation_euler") + self.addProp(ks, bone, "rotation_euler") else: for i in range(3): if not bone.lock_rotation[i]: - ksi.addProp(ks, bone, "rotation_euler", i) + self.addProp(ks, bone, "rotation_euler", i) # scale properties - def doScale(ksi, ks, bone): + def doScale(self, ks, bone): if bone.lock_scale == (0, 0, 0): - ksi.addProp(ks, bone, "scale") + self.addProp(ks, bone, "scale") else: for i in range(3): if not bone.lock_scale[i]: - ksi.addProp(ks, bone, "scale", i) + self.addProp(ks, bone, "scale", i) # ---------------- # bendy bone properties - def doBBone(ksi, context, ks, pchan): + def doBBone(self, context, ks, pchan): bone = pchan.bone # This check is crude, but is the best we can do for now @@ -500,12 +509,12 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): # (and the bone is a control bone). This may lead to some # false positives... if bone.bbone_segments > 1: - keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan) + keyingsets_utils.RKS_GEN_bendy_bones(self, context, ks, pchan) # ---------------- # custom properties - def doCustomProps(ksi, ks, bone): + def doCustomProps(self, ks, bone): prop_type_compat = {bpy.types.BoolProperty, bpy.types.IntProperty, @@ -528,39 +537,34 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): # be converted to an FCurve-compatible value, so we can't keyframe it anyway. continue if rna_property.rna_type in prop_type_compat: - ksi.addProp(ks, bone, prop_path) + self.addProp(ks, bone, prop_path) elif prop_rna.is_animatable: - ksi.addProp(ks, bone, prop) + self.addProp(ks, bone, prop) -# All properties that are likely to get animated in a character rig, only selected bones. + +class BUILTIN_KSI_WholeCharacter(WholeCharacterMixin, KeyingSetInfo): + """Insert a keyframe for all properties that are likely to get animated in a character rig """ \ + """(useful when blocking out a shot)""" + bl_idname = ANIM_KS_WHOLE_CHARACTER_ID + bl_label = "Whole Character" -class BUILTIN_KSI_WholeCharacterSelected(KeyingSetInfo): +class BUILTIN_KSI_WholeCharacterSelected(WholeCharacterMixin, KeyingSetInfo): """Insert a keyframe for all properties that are likely to get animated in a character rig """ \ """(only selected bones)""" bl_idname = ANIM_KS_WHOLE_CHARACTER_SELECTED_ID bl_label = "Whole Character (Selected Bones Only)" # iterator - all bones regardless of selection - def iterator(ksi, context, ks): + def iterator(self, context, ks): # Use either the selected bones, or all of them if none are selected. bones = context.selected_pose_bones_from_active_object or context.active_object.pose.bones for bone in bones: - if bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes): + if bone.name.startswith(self.badBonePrefixes): continue - ksi.generate(context, ks, bone) - - # Poor man's subclassing. Blender breaks when we actually subclass BUILTIN_KSI_WholeCharacter. - poll = BUILTIN_KSI_WholeCharacter.poll - generate = BUILTIN_KSI_WholeCharacter.generate - addProp = BUILTIN_KSI_WholeCharacter.addProp - doLoc = BUILTIN_KSI_WholeCharacter.doLoc - doRot4d = BUILTIN_KSI_WholeCharacter.doRot4d - doRot3d = BUILTIN_KSI_WholeCharacter.doRot3d - doScale = BUILTIN_KSI_WholeCharacter.doScale - doBBone = BUILTIN_KSI_WholeCharacter.doBBone - doCustomProps = BUILTIN_KSI_WholeCharacter.doCustomProps + self.generate(context, ks, bone) + ############################### @@ -578,7 +582,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(ksi, context, ks, data): + def generate(self, context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -604,7 +608,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(ksi, context, ks, data): + def generate(self, context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -638,7 +642,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(ksi, context, ks, data): + def generate(self, context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -664,6 +668,7 @@ classes = ( BUILTIN_KSI_Scaling, BUILTIN_KSI_LocRot, BUILTIN_KSI_LocRotScale, + BUILTIN_KSI_LocRotScaleCProp, BUILTIN_KSI_LocScale, BUILTIN_KSI_RotScale, BUILTIN_KSI_DeltaLocation, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index ab4d9353e1b..379fa145c92 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -498,12 +498,16 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeSeparateXYZ"), NodeItem("GeometryNodeAttributeRemove"), NodeItem("GeometryNodeAttributeMapRange"), + NodeItem("GeometryNodeAttributeTransfer"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeValToRGB"), NodeItem("ShaderNodeSeparateRGB"), NodeItem("ShaderNodeCombineRGB"), ]), + GeometryNodeCategory("GEO_CURVE", "Curve", items=[ + NodeItem("GeometryNodeCurveToMesh"), + ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), NodeItem("GeometryNodeTransform"), @@ -550,6 +554,7 @@ geometry_node_categories = [ NodeItem("ShaderNodeMath"), NodeItem("FunctionNodeBooleanMath"), NodeItem("FunctionNodeFloatCompare"), + NodeItem("GeometryNodeSwitch"), ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ NodeItem("ShaderNodeSeparateXYZ"), diff --git a/release/scripts/templates_py/operator_modal_draw.py b/release/scripts/templates_py/operator_modal_draw.py index 1e185ecfe2b..16c6f6dbe22 100644 --- a/release/scripts/templates_py/operator_modal_draw.py +++ b/release/scripts/templates_py/operator_modal_draw.py @@ -1,5 +1,4 @@ import bpy -import bgl import blf import gpu from gpu_extras.batch import batch_for_shader @@ -17,16 +16,16 @@ def draw_callback_px(self, context): # 50% alpha, 2 pixel width line shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(2) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(2.0) batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": self.mouse_path}) shader.bind() shader.uniform_float("color", (0.0, 0.0, 0.0, 0.5)) batch.draw(shader) # restore opengl defaults - bgl.glLineWidth(1) - bgl.glDisable(bgl.GL_BLEND) + gpu.state.line_width_set(1.0) + gpu.state.blend_set('NONE') class ModalDrawOperator(bpy.types.Operator): diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index e27cb2be8ee..3d81fcba37d 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -140,7 +140,7 @@ void BKE_pose_channel_free_bbone_cache(struct bPoseChannel_Runtime *runtime); void BKE_pose_channels_free(struct bPose *pose); void BKE_pose_channels_free_ex(struct bPose *pose, bool do_id_user); -void BKE_pose_channels_hash_make(struct bPose *pose); +void BKE_pose_channels_hash_ensure(struct bPose *pose); void BKE_pose_channels_hash_free(struct bPose *pose); void BKE_pose_channels_remove(struct Object *ob, @@ -161,7 +161,7 @@ void BKE_pose_channel_session_uuid_generate(struct bPoseChannel *pchan); struct bPoseChannel *BKE_pose_channel_find_name(const struct bPose *pose, const char *name); struct bPoseChannel *BKE_pose_channel_active(struct Object *ob); struct bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob); -struct bPoseChannel *BKE_pose_channel_verify(struct bPose *pose, const char *name); +struct bPoseChannel *BKE_pose_channel_ensure(struct bPose *pose, const char *name); struct bPoseChannel *BKE_pose_channel_get_mirrored(const struct bPose *pose, const char *name); void BKE_pose_check_uuids_unique_and_report(const struct bPose *pose); diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 120b4e08b9c..5feae1bf4ca 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -20,6 +20,7 @@ #include "FN_cpp_type.hh" #include "FN_generic_span.hh" +#include "FN_generic_virtual_array.hh" #include "BKE_attribute.h" @@ -30,6 +31,10 @@ namespace blender::bke { using fn::CPPType; +using fn::GVArray; +using fn::GVArrayPtr; +using fn::GVMutableArray; +using fn::GVMutableArrayPtr; const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); CustomDataType cpp_type_to_custom_data_type(const CPPType &type); @@ -37,282 +42,193 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains); /** - * This class offers an indirection for reading an attribute. - * This is useful for the following reasons: - * - Blender does not store all attributes the same way. - * The simplest case are custom data layers with primitive types. - * A bit more complex are mesh attributes like the position of vertices, - * which are embedded into the MVert struct. - * Even more complex to access are vertex weights. - * - Sometimes attributes are stored on one domain, but we want to access - * the attribute on a different domain. Therefore, we have to interpolate - * between the domains. + * Used when looking up a "plain attribute" based on a name for reading from it. */ -class ReadAttribute { - protected: - const AttributeDomain domain_; - const CPPType &cpp_type_; - const CustomDataType custom_data_type_; - const int64_t size_; - - /* Protects the span below, so that no two threads initialize it at the same time. */ - mutable std::mutex span_mutex_; - /* When it is not null, it points to the attribute array or a temporary array that contains all - * the attribute values. */ - mutable void *array_buffer_ = nullptr; - /* Is true when the buffer above is owned by the attribute accessor. */ - mutable bool array_is_temporary_ = false; +struct ReadAttributeLookup { + /* The virtual array that is used to read from this attribute. */ + GVArrayPtr varray; + /* Domain the attribute lives on in the geometry. */ + AttributeDomain domain; - public: - ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) - : domain_(domain), - cpp_type_(cpp_type), - custom_data_type_(cpp_type_to_custom_data_type(cpp_type)), - size_(size) - { - } - - virtual ~ReadAttribute(); - - AttributeDomain domain() const + /* Convenience function to check if the attribute has been found. */ + operator bool() const { - return domain_; + return this->varray.get() != nullptr; } +}; - const CPPType &cpp_type() const - { - return cpp_type_; - } +/** + * Used when looking up a "plain attribute" based on a name for reading from it and writing to it. + */ +struct WriteAttributeLookup { + /* The virtual array that is used to read from and write to the attribute. */ + GVMutableArrayPtr varray; + /* Domain the attributes lives on in the geometry. */ + AttributeDomain domain; - CustomDataType custom_data_type() const + /* Convenience function to check if the attribute has been found. */ + operator bool() const { - return custom_data_type_; + return this->varray.get() != nullptr; } +}; - int64_t size() const - { - return size_; - } +/** + * An output attribute allows writing to an attribute (and optionally reading as well). It adds + * some convenience features on top of `GVMutableArray` that are very commonly used. + * + * Supported convenience features: + * - Implicit type conversion when writing to builtin attributes. + * - Supports simple access to a span containing the attribute values (that avoids the use of + * VMutableArray_Span in many cases). + * - An output attribute can live side by side with an existing attribute with a different domain + * or data type. The old attribute will only be overwritten when the #save function is called. + */ +class OutputAttribute { + public: + using SaveFn = std::function<void(OutputAttribute &)>; - void get(const int64_t index, void *r_value) const - { - BLI_assert(index < size_); - this->get_internal(index, r_value); - } + private: + GVMutableArrayPtr varray_; + AttributeDomain domain_; + SaveFn save_; + std::optional<fn::GVMutableArray_GSpan> optional_span_varray_; + bool ignore_old_values_ = false; + bool save_has_been_called_ = false; - /* Get a span that contains all attribute values. */ - fn::GSpan get_span() const; + public: + OutputAttribute() = default; - template<typename T> Span<T> get_span() const + OutputAttribute(GVMutableArrayPtr varray, + AttributeDomain domain, + SaveFn save, + const bool ignore_old_values) + : varray_(std::move(varray)), + domain_(domain), + save_(std::move(save)), + ignore_old_values_(ignore_old_values) { - return this->get_span().typed<T>(); } - protected: - /* r_value is expected to be uninitialized. */ - virtual void get_internal(const int64_t index, void *r_value) const = 0; + OutputAttribute(OutputAttribute &&other) = default; - virtual void initialize_span() const; -}; + ~OutputAttribute(); -/** - * This exists for similar reasons as the ReadAttribute class, except that - * it does not deal with interpolation between domains. - */ -class WriteAttribute { - protected: - const AttributeDomain domain_; - const CPPType &cpp_type_; - const CustomDataType custom_data_type_; - const int64_t size_; - - /* When not null, this points either to the attribute array or to a temporary array. */ - void *array_buffer_ = nullptr; - /* True, when the buffer points to a temporary array. */ - bool array_is_temporary_ = false; - /* This helps to protect against forgetting to apply changes done to the array. */ - bool array_should_be_applied_ = false; - - public: - WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) - : domain_(domain), - cpp_type_(cpp_type), - custom_data_type_(cpp_type_to_custom_data_type(cpp_type)), - size_(size) + operator bool() const { + return varray_.get() != nullptr; } - virtual ~WriteAttribute(); - - AttributeDomain domain() const + GVMutableArray &operator*() { - return domain_; + return *varray_; } - const CPPType &cpp_type() const + GVMutableArray *operator->() { - return cpp_type_; + return varray_.get(); } - CustomDataType custom_data_type() const + GVMutableArray &varray() { - return custom_data_type_; + return *varray_; } - int64_t size() const + AttributeDomain domain() const { - return size_; + return domain_; } - void get(const int64_t index, void *r_value) const + const CPPType &cpp_type() const { - BLI_assert(index < size_); - this->get_internal(index, r_value); + return varray_->type(); } - void set(const int64_t index, const void *value) + CustomDataType custom_data_type() const { - BLI_assert(index < size_); - this->set_internal(index, value); + return cpp_type_to_custom_data_type(this->cpp_type()); } - /* Get a span that new attribute values can be written into. When all values have been changed, - * #apply_span has to be called. */ - fn::GMutableSpan get_span(); - /* The span returned by this method might not contain the current attribute values. */ - fn::GMutableSpan get_span_for_write_only(); - /* Write the changes to the span into the actual attribute, if they aren't already. */ - void apply_span(); - - template<typename T> MutableSpan<T> get_span() + fn::GMutableSpan as_span() { - return this->get_span().typed<T>(); + if (!optional_span_varray_.has_value()) { + const bool materialize_old_values = !ignore_old_values_; + optional_span_varray_.emplace(*varray_, materialize_old_values); + } + fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; + return span_varray; } - template<typename T> MutableSpan<T> get_span_for_write_only() + template<typename T> MutableSpan<T> as_span() { - return this->get_span_for_write_only().typed<T>(); + return this->as_span().typed<T>(); } - protected: - virtual void get_internal(const int64_t index, void *r_value) const = 0; - virtual void set_internal(const int64_t index, const void *value) = 0; - - virtual void initialize_span(const bool write_only); - virtual void apply_span_if_necessary(); + void save(); }; -using ReadAttributePtr = std::unique_ptr<ReadAttribute>; -using WriteAttributePtr = std::unique_ptr<WriteAttribute>; - -/* This provides type safe access to an attribute. - * The underlying ReadAttribute is owned optionally. */ -template<typename T> class TypedReadAttribute { +/** + * Same as OutputAttribute, but should be used when the data type is known at compile time. + */ +template<typename T> class OutputAttribute_Typed { private: - std::unique_ptr<const ReadAttribute> owned_attribute_; - const ReadAttribute *attribute_; + OutputAttribute attribute_; + std::optional<fn::GVMutableArray_Typed<T>> optional_varray_; + VMutableArray<T> *varray_ = nullptr; public: - TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute) - { - owned_attribute_ = std::move(attribute); - BLI_assert(owned_attribute_); - } - - TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute) + OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) { - BLI_assert(attribute_->cpp_type().is<T>()); + if (attribute_) { + optional_varray_.emplace(attribute_.varray()); + varray_ = &**optional_varray_; + } } - int64_t size() const + operator bool() const { - return attribute_->size(); + return varray_ != nullptr; } - T operator[](const int64_t index) const + VMutableArray<T> &operator*() { - BLI_assert(index < attribute_->size()); - T value; - value.~T(); - attribute_->get(index, &value); - return value; + return *varray_; } - /* Get a span to that contains all attribute values for faster and more convenient access. */ - Span<T> get_span() const + VMutableArray<T> *operator->() { - return attribute_->get_span().template typed<T>(); + return varray_; } -}; - -/* This provides type safe access to an attribute. - * The underlying WriteAttribute is owned optionally. */ -template<typename T> class TypedWriteAttribute { - private: - std::unique_ptr<WriteAttribute> owned_attribute_; - WriteAttribute *attribute_; - public: - TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute) + VMutableArray<T> &varray() { - owned_attribute_ = std::move(attribute); - BLI_assert(owned_attribute_); + return *varray_; } - TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute) - { - BLI_assert(attribute_->cpp_type().is<T>()); - } - - int64_t size() const + AttributeDomain domain() const { - return attribute_->size(); + return attribute_.domain(); } - T operator[](const int64_t index) const + const CPPType &cpp_type() const { - BLI_assert(index < attribute_->size()); - T value; - value.~T(); - attribute_->get(index, &value); - return value; + return CPPType::get<T>(); } - void set(const int64_t index, const T &value) + CustomDataType custom_data_type() const { - attribute_->set(index, &value); + return cpp_type_to_custom_data_type(this->cpp_type()); } - /* Get a span that new values can be written into. Once all values have been updated #apply_span - * has to be called. */ - MutableSpan<T> get_span() + MutableSpan<T> as_span() { - return attribute_->get_span().typed<T>(); - } - /* The span returned by this method might not contain the current attribute values. */ - MutableSpan<T> get_span_for_write_only() - { - return attribute_->get_span_for_write_only().typed<T>(); + return attribute_.as_span<T>(); } - /* Write back all changes to the actual attribute, if necessary. */ - void apply_span() + void save() { - attribute_->apply_span(); + attribute_.save(); } }; -using BooleanReadAttribute = TypedReadAttribute<bool>; -using FloatReadAttribute = TypedReadAttribute<float>; -using Float2ReadAttribute = TypedReadAttribute<float2>; -using Float3ReadAttribute = TypedReadAttribute<float3>; -using Int32ReadAttribute = TypedReadAttribute<int>; -using Color4fReadAttribute = TypedReadAttribute<Color4f>; -using BooleanWriteAttribute = TypedWriteAttribute<bool>; -using FloatWriteAttribute = TypedWriteAttribute<float>; -using Float2WriteAttribute = TypedWriteAttribute<float2>; -using Float3WriteAttribute = TypedWriteAttribute<float3>; -using Int32WriteAttribute = TypedWriteAttribute<int>; -using Color4fWriteAttribute = TypedWriteAttribute<Color4f>; - } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 16fc0db60fb..b0d32b20d44 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once + #include "BLI_array.hh" #include "BLI_color.hh" #include "BLI_float2.hh" @@ -21,13 +23,17 @@ #include "DNA_customdata_types.h" +#include "FN_cpp_type.hh" + namespace blender::attribute_math { +using fn::CPPType; + /** * Utility function that simplifies calling a templated function based on a custom data type. */ template<typename Func> -void convert_to_static_type(const CustomDataType data_type, const Func &func) +inline void convert_to_static_type(const CustomDataType data_type, const Func &func) { switch (data_type) { case CD_PROP_FLOAT: @@ -54,6 +60,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func) } } +template<typename Func> +inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func) +{ + if (cpp_type.is<float>()) { + func(float()); + } + else if (cpp_type.is<float2>()) { + func(float2()); + } + else if (cpp_type.is<float3>()) { + func(float3()); + } + else if (cpp_type.is<int>()) { + func(int()); + } + else if (cpp_type.is<bool>()) { + func(bool()); + } + else if (cpp_type.is<Color4f>()) { + func(Color4f()); + } + else { + BLI_assert_unreachable(); + } +} + /* -------------------------------------------------------------------- */ /** \name Mix three values of the same type. * @@ -101,6 +133,48 @@ inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, /** \} */ /* -------------------------------------------------------------------- */ +/** \name Mix two values of the same type. + * + * This is just basic linear interpolation. + * \{ */ + +template<typename T> T mix2(const float factor, const T &a, const T &b); + +template<> inline bool mix2(const float factor, const bool &a, const bool &b) +{ + return ((1.0f - factor) * a + factor * b) >= 0.5f; +} + +template<> inline int mix2(const float factor, const int &a, const int &b) +{ + return static_cast<int>((1.0f - factor) * a + factor * b); +} + +template<> inline float mix2(const float factor, const float &a, const float &b) +{ + return (1.0f - factor) * a + factor * b; +} + +template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b) +{ + return float2::interpolate(a, b, factor); +} + +template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b) +{ + return float3::interpolate(a, b, factor); +} + +template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b) +{ + Color4f result; + interp_v4_v4v4(result, a, b, factor); + return result; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Mix a dynamic amount of values with weights for many elements. * * This section provides an abstraction for "mixers". The abstraction encapsulates details about diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index f74f7fe84de..0bab980cfcd 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,15 +31,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 293 +#define BLENDER_VERSION 300 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 20 +#define BLENDER_FILE_SUBVERSION 1 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 3d30188e517..50aa6027840 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area); void CTX_wm_region_set(bContext *C, struct ARegion *region); void CTX_wm_menu_set(bContext *C, struct ARegion *menu); void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup); -const char *CTX_wm_operator_poll_msg_get(struct bContext *C); + +/** + * Values to create the message that describes the reason poll failed. + * + * \note This must be called in the same context as the poll function that created it. + */ +struct bContextPollMsgDyn_Params { + /** The result is allocated . */ + char *(*get_fn)(bContext *C, void *user_data); + /** Optionally free the user-data. */ + void (*free_fn)(bContext *C, void *user_data); + void *user_data; +}; + +const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free); void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg); +void CTX_wm_operator_poll_msg_set_dynamic(bContext *C, + const struct bContextPollMsgDyn_Params *params); +void CTX_wm_operator_poll_msg_clear(struct bContext *C); /* Data Context * diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h index c00310408af..5f6a9ec7b91 100644 --- a/source/blender/blenkernel/BKE_geometry_set.h +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -36,25 +36,13 @@ typedef enum GeometryComponentType { GEO_COMPONENT_TYPE_POINT_CLOUD = 1, GEO_COMPONENT_TYPE_INSTANCES = 2, GEO_COMPONENT_TYPE_VOLUME = 3, + GEO_COMPONENT_TYPE_CURVE = 4, } GeometryComponentType; void BKE_geometry_set_free(struct GeometrySet *geometry_set); bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set); -typedef enum InstancedDataType { - INSTANCE_DATA_TYPE_OBJECT = 0, - INSTANCE_DATA_TYPE_COLLECTION = 1, -} InstancedDataType; - -typedef struct InstancedData { - InstancedDataType type; - union { - struct Object *object; - struct Collection *collection; - } data; -} InstancedData; - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index d94b2e7902b..106af8172d1 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -30,6 +30,7 @@ #include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_user_counter.hh" +#include "BLI_vector_set.hh" #include "BKE_attribute_access.hh" #include "BKE_geometry_set.h" @@ -39,6 +40,7 @@ struct Mesh; struct Object; struct PointCloud; struct Volume; +class CurveEval; enum class GeometryOwnershipType { /* The geometry is owned. This implies that it can be changed. */ @@ -56,72 +58,77 @@ class ComponentAttributeProviders; class GeometryComponent; /** - * An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final - * destination yet. Therefore, once the attribute has been filled with data, the #save method has - * to be called, to store the attribute where it belongs (possibly by replacing an existing - * attribute with the same name). - * - * This is useful for example in the Attribute Color Ramp node, when the same attribute name is - * used as input and output. Typically the input is a float attribute, and the output is a color. - * Those two attributes cannot exist at the same time, due to a name collision. To handle this - * situation well, first the output colors have to be computed before the input floats are deleted. - * Therefore, the outputs have to be written to a temporary buffer that replaces the existing - * attribute once all computations are done. + * Contains information about an attribute in a geometry component. + * More information can be added in the future. E.g. whether the attribute is builtin and how it is + * stored (uv map, vertex group, ...). */ -class OutputAttributePtr { - private: - blender::bke::WriteAttributePtr attribute_; - - public: - OutputAttributePtr() = default; - OutputAttributePtr(blender::bke::WriteAttributePtr attribute); - OutputAttributePtr(GeometryComponent &component, - AttributeDomain domain, - std::string name, - CustomDataType data_type); +struct AttributeMetaData { + AttributeDomain domain; + CustomDataType data_type; +}; - ~OutputAttributePtr(); +/* Returns false when the iteration should be stopped. */ +using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name, + const AttributeMetaData &meta_data)>; - /* Returns false, when this wrapper is empty. */ - operator bool() const +/** + * Base class for the attribute intializer types described below. + */ +struct AttributeInit { + enum class Type { + Default, + VArray, + MoveArray, + }; + Type type; + AttributeInit(const Type type) : type(type) { - return static_cast<bool>(attribute_); } +}; - /* Get a reference to the underlying #WriteAttribute. */ - blender::bke::WriteAttribute &get() +/** + * Create an attribute using the default value for the data type. + * The default values may depend on the attribute provider implementation. + */ +struct AttributeInitDefault : public AttributeInit { + AttributeInitDefault() : AttributeInit(Type::Default) { - BLI_assert(attribute_); - return *attribute_; } +}; - blender::bke::WriteAttribute &operator*() - { - return *attribute_; - } +/** + * Create an attribute by copying data from an existing virtual array. The virtual array + * must have the same type as the newly created attribute. + * + * Note that this can be used to fill the new attribute with the default + */ +struct AttributeInitVArray : public AttributeInit { + const blender::fn::GVArray *varray; - blender::bke::WriteAttribute *operator->() + AttributeInitVArray(const blender::fn::GVArray *varray) + : AttributeInit(Type::VArray), varray(varray) { - return attribute_.get(); } - - void save(); - void apply_span_and_save(); }; /** - * Contains information about an attribute in a geometry component. - * More information can be added in the future. E.g. whether the attribute is builtin and how it is - * stored (uv map, vertex group, ...). + * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data. + * Sometimes data is created before a geometry component is available. In that case, it's + * preferable to move data directly to the created attribute to avoid a new allocation and a copy. + * + * Note that this will only have a benefit for attributes that are stored directly as contiguous + * arrays, so not for some built-in attributes. + * + * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it + * can't be used directly, and that is generally how Blender expects custom data to be allocated. */ -struct AttributeMetaData { - AttributeDomain domain; - CustomDataType data_type; -}; +struct AttributeInitMove : public AttributeInit { + void *data = nullptr; -/* Returns false when the iteration should be stopped. */ -using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name, - const AttributeMetaData &meta_data)>; + AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data) + { + } +}; /** * This is the base class for specialized geometry component types. @@ -156,26 +163,34 @@ class GeometryComponent { /* Return true when any attribute with this name exists, including built in attributes. */ bool attribute_exists(const blender::StringRef attribute_name) const; + /* Return the data type and domain of an attribute with the given name if it exists. */ + std::optional<AttributeMetaData> attribute_get_meta_data( + const blender::StringRef attribute_name) const; + /* Returns true when the geometry component supports this attribute domain. */ bool attribute_domain_supported(const AttributeDomain domain) const; /* Can only be used with supported domain types. */ virtual int attribute_domain_size(const AttributeDomain domain) const; + bool attribute_is_builtin(const blender::StringRef attribute_name) const; + /* Get read-only access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ - blender::bke::ReadAttributePtr attribute_try_get_for_read( + blender::bke::ReadAttributeLookup attribute_try_get_for_read( const blender::StringRef attribute_name) const; /* Get read and write access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ - blender::bke::WriteAttributePtr attribute_try_get_for_write( + blender::bke::WriteAttributeLookup attribute_try_get_for_write( const blender::StringRef attribute_name); /* Get a read-only attribute for the domain based on the given attribute. This can be used to * interpolate from one domain to another. * Returns null if the interpolation is not implemented. */ - virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain( - blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const; + virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const; /* Returns true when the attribute has been deleted. */ bool attribute_try_delete(const blender::StringRef attribute_name); @@ -183,82 +198,104 @@ class GeometryComponent { /* Returns true when the attribute has been created. */ bool attribute_try_create(const blender::StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type); + const CustomDataType data_type, + const AttributeInit &initializer); + + /* Try to create the builtin attribute with the given name. No data type or domain has to be + * provided, because those are fixed for builtin attributes. */ + bool attribute_try_create_builtin(const blender::StringRef attribute_name, + const AttributeInit &initializer); blender::Set<std::string> attribute_names() const; bool attribute_foreach(const AttributeForeachCallback callback) const; virtual bool is_empty() const; - /* Get a read-only attribute for the given domain and data type. - * Returns null when it does not exist. */ - blender::bke::ReadAttributePtr attribute_try_get_for_read( + /* Get a virtual array to read the data of an attribute on the given domain and data type. + * Returns null when the attribute does not exist or cannot be converted to the requested domain + * and data type. */ + std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read( const blender::StringRef attribute_name, const AttributeDomain domain, const CustomDataType data_type) const; - /* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged. - * Returns null when the attribute does not exist. */ - blender::bke::ReadAttributePtr attribute_try_get_for_read( + /* Get a virtual array to read the data of an attribute on the given domain. The data type is + * left unchanged. Returns null when the attribute does not exist or cannot be adapted to the + * requested domain. */ + std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read( const blender::StringRef attribute_name, const AttributeDomain domain) const; - /* Get a read-only attribute for the given domain and data type. - * Returns a constant attribute based on the default value if the attribute does not exist. - * Never returns null. */ - blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) const; + /* Get a virtual array to read data of an attribute with the given data type. The domain is + * left unchanged. Returns null when the attribute does not exist or cannot be converted to the + * requested data type. */ + blender::bke::ReadAttributeLookup attribute_try_get_for_read( + const blender::StringRef attribute_name, const CustomDataType data_type) const; - /* Get a typed read-only attribute for the given domain and type. */ - template<typename T> - blender::bke::TypedReadAttribute<T> attribute_get_for_read( + /* Get a virtual array to read the data of an attribute. If that is not possible, the returned + * virtual array will contain a default value. This never returns null. */ + std::unique_ptr<blender::fn::GVArray> attribute_get_for_read( const blender::StringRef attribute_name, const AttributeDomain domain, - const T &default_value) const + const CustomDataType data_type, + const void *default_value = nullptr) const; + + /* Should be used instead of the method above when the requested data type is known at compile + * time for better type safety. */ + template<typename T> + blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name, + const AttributeDomain domain, + const T &default_value) const { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_get_for_read(attribute_name, domain, type, &default_value); + std::unique_ptr varray = this->attribute_get_for_read( + attribute_name, domain, type, &default_value); + return blender::fn::GVArray_Typed<T>(std::move(varray)); } - /* Get a read-only dummy attribute that always returns the same value. */ - blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain, - const CustomDataType data_type, - const void *value) const; + /** + * Returns an "output attribute", which is essentially a mutable virtual array with some commonly + * used convince features. The returned output attribute might be empty if requested attribute + * cannot exist on the geometry. + * + * The included convenience features are: + * - Implicit type conversion when writing to builtin attributes. + * - If the attribute name exists already, but has a different type/domain, a temporary attribute + * is created that will overwrite the existing attribute in the end. + */ + blender::bke::OutputAttribute attribute_try_get_for_output( + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value = nullptr); - /* Create a read-only dummy attribute that always returns the same value. - * The given value is converted to the correct type if necessary. */ - blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted( + /* Same as attribute_try_get_for_output, but should be used when the original values in the + * attributes are not read, i.e. the attribute is used only for output. Since values are not read + * from this attribute, no default value is necessary. */ + blender::bke::OutputAttribute attribute_try_get_for_output_only( + const blender::StringRef attribute_name, const AttributeDomain domain, - const CustomDataType in_data_type, - const CustomDataType out_data_type, - const void *value) const; + const CustomDataType data_type); - /* Get a read-only dummy attribute that always returns the same value. */ + /* Statically typed method corresponding to the equally named generic one. */ template<typename T> - blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain, - const T &value) const + blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output( + const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value) { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); - const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_get_constant_for_read(domain, type, &value); + const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value); } - /** - * If an attribute with the given params exist, it is returned. - * If no attribute with the given name exists, create it and - * fill it with the default value if it is provided. - * If an attribute with the given name but different domain or type exists, a temporary attribute - * is created that has to be saved after the output has been computed. This avoids deleting - * another attribute, before a computation is finished. - * - * This might return no attribute when the attribute cannot exist on the component. - */ - OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value = nullptr); + /* Statically typed method corresponding to the equally named generic one. */ + template<typename T> + blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only( + const blender::StringRef attribute_name, const AttributeDomain domain) + { + const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + return this->attribute_try_get_for_output_only(attribute_name, domain, data_type); + } private: virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const; @@ -328,23 +365,34 @@ struct GeometrySet { Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); static GeometrySet create_with_pointcloud( PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + static GeometrySet create_with_curve( + CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /* Utility methods for access. */ bool has_mesh() const; bool has_pointcloud() const; bool has_instances() const; bool has_volume() const; + bool has_curve() const; + const Mesh *get_mesh_for_read() const; const PointCloud *get_pointcloud_for_read() const; const Volume *get_volume_for_read() const; + const CurveEval *get_curve_for_read() const; + Mesh *get_mesh_for_write(); PointCloud *get_pointcloud_for_write(); Volume *get_volume_for_write(); + CurveEval *get_curve_for_write(); /* Utility methods for replacement. */ void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + void replace_volume(Volume *volume, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + void replace_curve(CurveEval *curve, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); }; /** A geometry component that can store a mesh. */ @@ -377,8 +425,10 @@ class MeshComponent : public GeometryComponent { Mesh *get_for_write(); int attribute_domain_size(const AttributeDomain domain) const final; - blender::bke::ReadAttributePtr attribute_try_adapt_domain( - blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final; + std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const final; bool is_empty() const final; @@ -424,12 +474,113 @@ class PointCloudComponent : public GeometryComponent { const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; }; +/** A geometry component that stores curve data, in other words, a group of splines. */ +class CurveComponent : public GeometryComponent { + private: + CurveEval *curve_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + + public: + CurveComponent(); + ~CurveComponent(); + GeometryComponent *copy() const override; + + void clear(); + bool has_curve() const; + void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + CurveEval *release(); + + const CurveEval *get_for_read() const; + CurveEval *get_for_write(); + + int attribute_domain_size(const AttributeDomain domain) const final; + + bool is_empty() const final; + + bool owns_direct_data() const override; + void ensure_owns_direct_data() override; + + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; + + private: + const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; +}; + +class InstanceReference { + public: + enum class Type { + /** + * An empty instance. This allows an `InstanceReference` to be default constructed without + * being in an invalid state. There might also be other use cases that we haven't explored much + * yet (such as changing the instance later on, and "disabling" some instances). + */ + None, + Object, + Collection, + }; + + private: + Type type_ = Type::None; + /** Depending on the type this is either null, an Object or Collection pointer. */ + void *data_ = nullptr; + + public: + InstanceReference() = default; + + InstanceReference(Object &object) : type_(Type::Object), data_(&object) + { + } + + InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection) + { + } + + Type type() const + { + return type_; + } + + Object &object() const + { + BLI_assert(type_ == Type::Object); + return *(Object *)data_; + } + + Collection &collection() const + { + BLI_assert(type_ == Type::Collection); + return *(Collection *)data_; + } + + uint64_t hash() const + { + return blender::get_default_hash(data_); + } + + friend bool operator==(const InstanceReference &a, const InstanceReference &b) + { + return a.data_ == b.data_; + } +}; + /** A geometry component that stores instances. */ class InstancesComponent : public GeometryComponent { private: - blender::Vector<blender::float4x4> transforms_; - blender::Vector<int> ids_; - blender::Vector<InstancedData> instanced_data_; + /** + * Indexed set containing information about the data that is instanced. + * Actual instances store an index ("handle") into this set. + */ + blender::VectorSet<InstanceReference> references_; + + /** Index into `references_`. Determines what data is instanced. */ + blender::Vector<int> instance_reference_handles_; + /** Transformation of the instances. */ + blender::Vector<blender::float4x4> instance_transforms_; + /** + * IDs of the instances. They are used for consistency over multiple frames for things like + * motion blur. + */ + blender::Vector<int> instance_ids_; /* These almost unique ids are generated based on `ids_`, which might not contain unique ids at * all. They are *almost* unique, because under certain very unlikely circumstances, they are not @@ -444,14 +595,20 @@ class InstancesComponent : public GeometryComponent { GeometryComponent *copy() const override; void clear(); - void add_instance(Object *object, blender::float4x4 transform, const int id = -1); - void add_instance(Collection *collection, blender::float4x4 transform, const int id = -1); - void add_instance(InstancedData data, blender::float4x4 transform, const int id = -1); - - blender::Span<InstancedData> instanced_data() const; - blender::Span<blender::float4x4> transforms() const; - blender::Span<int> ids() const; - blender::MutableSpan<blender::float4x4> transforms(); + + void reserve(int min_capacity); + + int add_reference(InstanceReference reference); + void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1); + + blender::Span<InstanceReference> references() const; + + blender::Span<int> instance_reference_handles() const; + blender::MutableSpan<blender::float4x4> instance_transforms(); + blender::Span<blender::float4x4> instance_transforms() const; + blender::MutableSpan<int> instance_ids(); + blender::Span<int> instance_ids() const; + int instances_amount() const; blender::Span<int> almost_unique_ids() const; diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index e39caac7c36..fae0ee4f81d 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -577,9 +577,9 @@ void BKE_mesh_polygons_flip(struct MPoly *mpoly, struct CustomData *ldata, int totpoly); -/* merge verts */ -/* Enum for merge_mode of CDDM_merge_verts. - * Refer to mesh.c for details. */ +/* Merge verts. */ +/* Enum for merge_mode of #BKE_mesh_merge_verts. + * Refer to mesh_merge.c for details. */ enum { MESH_MERGE_VERTS_DUMP_IF_MAPPED, MESH_MERGE_VERTS_DUMP_IF_EQUAL, diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh new file mode 100644 index 00000000000..f504650e349 --- /dev/null +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#include "FN_generic_virtual_array.hh" + +#include "BLI_float3.hh" + +#include "BKE_attribute.h" + +struct Mesh; + +namespace blender::bke::mesh_surface_sample { + +using fn::CPPType; +using fn::GMutableSpan; +using fn::GSpan; +using fn::GVArray; + +void sample_point_attribute(const Mesh &mesh, + Span<int> looptri_indices, + Span<float3> bary_coords, + const GVArray &data_in, + GMutableSpan data_out); + +void sample_corner_attribute(const Mesh &mesh, + Span<int> looptri_indices, + Span<float3> bary_coords, + const GVArray &data_in, + GMutableSpan data_out); + +void sample_face_attribute(const Mesh &mesh, + Span<int> looptri_indices, + const GVArray &data_in, + GMutableSpan data_out); + +} // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index ab2ecbe2507..c0153dd8241 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -258,10 +258,6 @@ typedef struct ModifierTypeInfo { const struct ModifierEvalContext *ctx, struct GeometrySet *geometry_set); - struct Volume *(*modifyVolume)(struct ModifierData *md, - const struct ModifierEvalContext *ctx, - struct Volume *volume); - /********************* Optional functions *********************/ /** diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 16d48024d07..af238fda659 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -58,7 +58,14 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain, struct NlaTrack *nlt, const bool use_same_actions, const int flag); -void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag); +void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, const ListBase *src, const int flag); + +/* Copy NLA tracks from #adt_source to #adt_dest, and update the active track/strip pointers to + * point at those copies. */ +void BKE_nla_tracks_copy_from_adt(struct Main *bmain, + struct AnimData *adt_dest, + const struct AnimData *adt_source, + int flag); struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt, struct NlaTrack *prev, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index d6c4ad037e2..bcab456b36e 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -410,6 +410,9 @@ typedef struct bNodeTreeType { void (*node_add_init)(struct bNodeTree *ntree, struct bNode *bnode); + /* Check if the socket type is valid for this tree type. */ + bool (*valid_socket_type)(enum eNodeSocketDatatype socket_type, struct bNodeTreeType *ntreetype); + /* RNA integration */ ExtensionRNA rna_ext; } bNodeTreeType; @@ -1413,6 +1416,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040 #define GEO_NODE_ATTRIBUTE_CLAMP 1041 #define GEO_NODE_BOUNDING_BOX 1042 +#define GEO_NODE_SWITCH 1043 +#define GEO_NODE_ATTRIBUTE_TRANSFER 1044 +#define GEO_NODE_CURVE_TO_MESH 1045 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 9fe286df36d..f3a5c794de8 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -374,6 +374,7 @@ struct MovieClip *BKE_object_movieclip_get(struct Scene *scene, void BKE_object_runtime_reset(struct Object *object); void BKE_object_runtime_reset_on_copy(struct Object *object, const int flag); +void BKE_object_runtime_free_data(struct Object *object); void BKE_object_batch_cache_dirty_tag(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh new file mode 100644 index 00000000000..098abf6de65 --- /dev/null +++ b/source/blender/blenkernel/BKE_spline.hh @@ -0,0 +1,478 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#include <mutex> + +#include "FN_generic_virtual_array.hh" + +#include "BLI_float3.hh" +#include "BLI_float4x4.hh" +#include "BLI_vector.hh" + +#include "BKE_attribute_math.hh" + +struct Curve; + +class Spline; +using SplinePtr = std::unique_ptr<Spline>; + +/** + * A spline is an abstraction of a single branch-less curve section, its evaluation methods, + * and data. The spline data itself is just control points and a set of attributes by the set + * of "evaluated" data is often used instead. + * + * Any derived class of Spline has to manage two things: + * 1. Interpolating arbitrary attribute data from the control points to evaluated points. + * 2. Evaluating the positions based on the stored control point data. + * + * Beyond that, everything is the base class's responsibility, with minor exceptions. Further + * evaluation happens in a layer on top of the evaluated points generated by the derived types. + * + * There are a few methods to evaluate a spline: + * 1. #evaluated_positions and #interpolate_to_evaluated_points give data at the initial + * evaluated points, depending on the resolution. + * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups + * along the length of a curve. + * + * Commonly used evaluated data is stored in caches on the spline itself so that operations on + * splines don't need to worry about taking ownership of evaluated data when they don't need to. + */ +class Spline { + public: + enum class Type { + Bezier, + NURBS, + Poly, + }; + + protected: + Type type_; + bool is_cyclic_ = false; + + public: + enum NormalCalculationMode { + ZUp, + Minimum, + Tangent, + }; + /* Only #Zup is supported at the moment. */ + NormalCalculationMode normal_mode; + + protected: + /** Direction of the spline at each evaluated point. */ + mutable blender::Vector<blender::float3> evaluated_tangents_cache_; + mutable std::mutex tangent_cache_mutex_; + mutable bool tangent_cache_dirty_ = true; + + /** Normal direction vectors for each evaluated point. */ + mutable blender::Vector<blender::float3> evaluated_normals_cache_; + mutable std::mutex normal_cache_mutex_; + mutable bool normal_cache_dirty_ = true; + + /** Accumulated lengths along the evaluated points. */ + mutable blender::Vector<float> evaluated_lengths_cache_; + mutable std::mutex length_cache_mutex_; + mutable bool length_cache_dirty_ = true; + + public: + virtual ~Spline() = default; + Spline(const Type type) : type_(type) + { + } + Spline(Spline &other) + : type_(other.type_), is_cyclic_(other.is_cyclic_), normal_mode(other.normal_mode) + { + } + + virtual SplinePtr copy() const = 0; + + Spline::Type type() const; + + /** Return the number of control points. */ + virtual int size() const = 0; + int segments_size() const; + bool is_cyclic() const; + void set_cyclic(const bool value); + + virtual blender::MutableSpan<blender::float3> positions() = 0; + virtual blender::Span<blender::float3> positions() const = 0; + virtual blender::MutableSpan<float> radii() = 0; + virtual blender::Span<float> radii() const = 0; + virtual blender::MutableSpan<float> tilts() = 0; + virtual blender::Span<float> tilts() const = 0; + + virtual void translate(const blender::float3 &translation); + virtual void transform(const blender::float4x4 &matrix); + + /** + * Mark all caches for re-computation. This must be called after any operation that would + * change the generated positions, tangents, normals, mapping, etc. of the evaluated points. + */ + virtual void mark_cache_invalid() = 0; + virtual int evaluated_points_size() const = 0; + int evaluated_edges_size() const; + + float length() const; + + virtual blender::Span<blender::float3> evaluated_positions() const = 0; + + blender::Span<float> evaluated_lengths() const; + blender::Span<blender::float3> evaluated_tangents() const; + blender::Span<blender::float3> evaluated_normals() const; + + void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const; + + struct LookupResult { + /** + * The index of the evaluated point before the result location. In other words, the index of + * the edge that the result lies on. If the sampled factor/length is the very end of the + * spline, this will be the second to last index, if it's the very beginning, this will be 0. + */ + int evaluated_index; + /** + * The index of the evaluated point after the result location, accounting for wrapping when + * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will + * be the last index (#evaluated_points_size - 1). + */ + int next_evaluated_index; + /** + * The portion of the way from the evaluated point at #evaluated_index to the next point. + * If the sampled factor/length is the very end of the spline, this will be the 1.0f + */ + float factor; + }; + LookupResult lookup_evaluated_factor(const float factor) const; + LookupResult lookup_evaluated_length(const float length) const; + + /** + * Interpolate a virtual array of data with the size of the number of control points to the + * evaluated points. For poly splines, the lifetime of the returned virtual array must not + * exceed the lifetime of the input data. + */ + virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const = 0; + + protected: + virtual void correct_end_tangents() const = 0; +}; + +/** + * A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature + * by constraining the alignment of curve handles. Evaluation stores the positions and a map of + * factors and indices in a list of floats, which is then used to interpolate any other data. + */ +class BezierSpline final : public Spline { + public: + enum class HandleType { + /** The handle can be moved anywhere, and doesn't influence the point's other handle. */ + Free, + /** The location is automatically calculated to be smooth. */ + Auto, + /** The location is calculated to point to the next/previous control point. */ + Vector, + /** The location is constrained to point in the opposite direction as the other handle. */ + Align, + }; + + private: + blender::Vector<HandleType> handle_types_left_; + blender::Vector<blender::float3> handle_positions_left_; + blender::Vector<blender::float3> positions_; + blender::Vector<HandleType> handle_types_right_; + blender::Vector<blender::float3> handle_positions_right_; + blender::Vector<float> radii_; + blender::Vector<float> tilts_; + int resolution_; + + /** Start index in evaluated points array for every control point. */ + mutable blender::Vector<int> offset_cache_; + mutable std::mutex offset_cache_mutex_; + mutable bool offset_cache_dirty_ = true; + + /** Cache of evaluated positions. */ + mutable blender::Vector<blender::float3> evaluated_position_cache_; + mutable std::mutex position_cache_mutex_; + mutable bool position_cache_dirty_ = true; + + /** Cache of "index factors" based calculated from the evaluated positions. */ + mutable blender::Vector<float> evaluated_mapping_cache_; + mutable std::mutex mapping_cache_mutex_; + mutable bool mapping_cache_dirty_ = true; + + public: + virtual SplinePtr copy() const final; + BezierSpline() : Spline(Type::Bezier) + { + } + BezierSpline(const BezierSpline &other) + : Spline((Spline &)other), + handle_types_left_(other.handle_types_left_), + handle_positions_left_(other.handle_positions_left_), + positions_(other.positions_), + handle_types_right_(other.handle_types_right_), + handle_positions_right_(other.handle_positions_right_), + radii_(other.radii_), + tilts_(other.tilts_), + resolution_(other.resolution_) + { + } + + int size() const final; + int resolution() const; + void set_resolution(const int value); + + void add_point(const blender::float3 position, + const HandleType handle_type_start, + const blender::float3 handle_position_start, + const HandleType handle_type_end, + const blender::float3 handle_position_end, + const float radius, + const float tilt); + + blender::MutableSpan<blender::float3> positions() final; + blender::Span<blender::float3> positions() const final; + blender::MutableSpan<float> radii() final; + blender::Span<float> radii() const final; + blender::MutableSpan<float> tilts() final; + blender::Span<float> tilts() const final; + blender::Span<HandleType> handle_types_left() const; + blender::MutableSpan<HandleType> handle_types_left(); + blender::Span<blender::float3> handle_positions_left() const; + blender::MutableSpan<blender::float3> handle_positions_left(); + blender::Span<HandleType> handle_types_right() const; + blender::MutableSpan<HandleType> handle_types_right(); + blender::Span<blender::float3> handle_positions_right() const; + blender::MutableSpan<blender::float3> handle_positions_right(); + + void translate(const blender::float3 &translation) override; + void transform(const blender::float4x4 &matrix) override; + + bool point_is_sharp(const int index) const; + + void mark_cache_invalid() final; + int evaluated_points_size() const final; + + blender::Span<int> control_point_offsets() const; + blender::Span<float> evaluated_mappings() const; + blender::Span<blender::float3> evaluated_positions() const final; + struct InterpolationData { + int control_point_index; + int next_control_point_index; + /** + * Linear interpolation weight between the two indices, from 0 to 1. + * Higher means next control point. + */ + float factor; + }; + InterpolationData interpolation_data_from_index_factor(const float index_factor) const; + + virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const; + + private: + void correct_end_tangents() const final; + bool segment_is_vector(const int start_index) const; + void evaluate_bezier_segment(const int index, + const int next_index, + blender::MutableSpan<blender::float3> positions) const; + blender::Array<int> evaluated_point_offsets() const; +}; + +/** + * Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is + * influenced by a vector of knots, weights for each point, and the order of the spline. Every + * mapping of data to evaluated points is handled the same way, but the positions are cached in + * the spline. + */ +class NURBSpline final : public Spline { + public: + enum class KnotsMode { + Normal, + EndPoint, + Bezier, + }; + KnotsMode knots_mode; + + struct BasisCache { + /** The influence at each control point `i + #start_index`. */ + blender::Vector<float> weights; + /** + * An offset for the start of #weights: the first control point index with a non-zero weight. + */ + int start_index; + }; + + private: + blender::Vector<blender::float3> positions_; + blender::Vector<float> radii_; + blender::Vector<float> tilts_; + blender::Vector<float> weights_; + int resolution_; + /** + * Defines the number of nearby control points that influence a given evaluated point. Higher + * orders give smoother results. The number of control points must be greater than or equal to + * this value. + */ + uint8_t order_; + + /** + * Determines where and how the control points affect the evaluated points. The length should + * always be the value returned by #knots_size(), and each value should be greater than or equal + * to the previous. Only invalidated when a point is added or removed. + */ + mutable blender::Vector<float> knots_; + mutable std::mutex knots_mutex_; + mutable bool knots_dirty_ = true; + + /** Cache of control point influences on each evaluated point. */ + mutable blender::Vector<BasisCache> basis_cache_; + mutable std::mutex basis_cache_mutex_; + mutable bool basis_cache_dirty_ = true; + + /** + * Cache of position data calculated from the basis cache. Though it is interpolated + * in the same way as any other attribute, it is stored to save unnecessary recalculation. + */ + mutable blender::Vector<blender::float3> evaluated_position_cache_; + mutable std::mutex position_cache_mutex_; + mutable bool position_cache_dirty_ = true; + + public: + SplinePtr copy() const final; + NURBSpline() : Spline(Type::NURBS) + { + } + NURBSpline(const NURBSpline &other) + : Spline((Spline &)other), + knots_mode(other.knots_mode), + positions_(other.positions_), + radii_(other.radii_), + tilts_(other.tilts_), + weights_(other.weights_), + resolution_(other.resolution_), + order_(other.order_) + { + } + + int size() const final; + int resolution() const; + void set_resolution(const int value); + uint8_t order() const; + void set_order(const uint8_t value); + + void add_point(const blender::float3 position, + const float radius, + const float tilt, + const float weight); + + bool check_valid_size_and_order() const; + int knots_size() const; + + blender::MutableSpan<blender::float3> positions() final; + blender::Span<blender::float3> positions() const final; + blender::MutableSpan<float> radii() final; + blender::Span<float> radii() const final; + blender::MutableSpan<float> tilts() final; + blender::Span<float> tilts() const final; + blender::Span<float> knots() const; + + blender::MutableSpan<float> weights(); + blender::Span<float> weights() const; + + void mark_cache_invalid() final; + int evaluated_points_size() const final; + + blender::Span<blender::float3> evaluated_positions() const final; + + blender::fn::GVArrayPtr interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const final; + + protected: + void correct_end_tangents() const final; + void calculate_knots() const; + void calculate_basis_cache() const; +}; + +/** + * A Poly spline is like a bezier spline with a resolution of one. The main reason to distinguish + * the two is for reduced complexity and increased performance, since interpolating data to control + * points does not change it. + */ +class PolySpline final : public Spline { + public: + blender::Vector<blender::float3> positions_; + blender::Vector<float> radii_; + blender::Vector<float> tilts_; + + private: + public: + SplinePtr copy() const final; + PolySpline() : Spline(Type::Poly) + { + } + PolySpline(const PolySpline &other) + : Spline((Spline &)other), + positions_(other.positions_), + radii_(other.radii_), + tilts_(other.tilts_) + { + } + + int size() const final; + + void add_point(const blender::float3 position, const float radius, const float tilt); + + blender::MutableSpan<blender::float3> positions() final; + blender::Span<blender::float3> positions() const final; + blender::MutableSpan<float> radii() final; + blender::Span<float> radii() const final; + blender::MutableSpan<float> tilts() final; + blender::Span<float> tilts() const final; + + void mark_cache_invalid() final; + int evaluated_points_size() const final; + + blender::Span<blender::float3> evaluated_positions() const final; + + blender::fn::GVArrayPtr interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const final; + + protected: + void correct_end_tangents() const final; +}; + +/** + * A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since + * more of the data is stored in the splines, but also just to be different than the name in DNA. + */ +class CurveEval { + public: + blender::Vector<SplinePtr> splines; + + CurveEval *copy(); + + void translate(const blender::float3 &translation); + void transform(const blender::float4x4 &matrix); + void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const; +}; + +std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 59e2c74ead1..0944bbed2f3 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -112,12 +112,13 @@ set(SRC intern/curve_convert.c intern/curve_decimate.c intern/curve_deform.c + intern/curve_eval.cc intern/curveprofile.c intern/customdata.c intern/customdata_file.c intern/data_transfer.c intern/deform.c - intern/displist.c + intern/displist.cc intern/displist_tangent.c intern/dynamicpaint.c intern/editlattice.c @@ -133,6 +134,7 @@ set(SRC intern/fmodifier.c intern/font.c intern/freestyle.c + intern/geometry_component_curve.cc intern/geometry_component_instances.cc intern/geometry_component_mesh.cc intern/geometry_component_pointcloud.cc @@ -190,6 +192,7 @@ set(SRC intern/mesh_remap.c intern/mesh_remesh_voxel.c intern/mesh_runtime.c + intern/mesh_sample.cc intern/mesh_tangent.c intern/mesh_validate.c intern/mesh_validate.cc @@ -240,6 +243,10 @@ set(SRC intern/softbody.c intern/sound.c intern/speaker.c + intern/spline_base.cc + intern/spline_bezier.cc + intern/spline_nurbs.cc + intern/spline_poly.cc intern/studiolight.c intern/subdiv.c intern/subdiv_ccg.c @@ -321,6 +328,7 @@ set(SRC BKE_customdata_file.h BKE_data_transfer.h BKE_deform.h + BKE_spline.hh BKE_displist.h BKE_displist_tangent.h BKE_duplilist.h @@ -379,6 +387,7 @@ set(SRC BKE_mesh_remap.h BKE_mesh_remesh_voxel.h BKE_mesh_runtime.h + BKE_mesh_sample.hh BKE_mesh_tangent.h BKE_mesh_types.h BKE_mesh_wrapper.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index c8416ac897e..ca1d92c001b 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -1859,9 +1859,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, BKE_id_free(nullptr, mesh_orco); } - /* Ensure normals calculation below is correct. */ - BLI_assert((mesh_input->flag & ME_AUTOSMOOTH) == (mesh_final->flag & ME_AUTOSMOOTH)); - BLI_assert(mesh_input->smoothresh == mesh_final->smoothresh); + /* Ensure normals calculation below is correct (normal settings have transferred properly). + * However, nodes modifiers might create meshes from scratch or transfer meshes from other + * objects with different settings, and in general it doesn't make sense to guarantee that + * the settings are the same as the original mesh. If necessary, this could become a modifier + * type flag. */ BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh); /* Compute normals. */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 0b38e2d9f75..3f3aa0386e2 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -635,7 +635,7 @@ bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name) * \note Use with care, not on Armature poses but for temporal ones. * \note (currently used for action constraints and in rebuild_pose). */ -bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name) +bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name) { bPoseChannel *chan; @@ -815,7 +815,7 @@ void BKE_pose_copy_data_ex(bPose **dst, */ if (outPose->chanbase.first != outPose->chanbase.last) { outPose->chanhash = NULL; - BKE_pose_channels_hash_make(outPose); + BKE_pose_channels_hash_ensure(outPose); } outPose->iksolver = src->iksolver; @@ -945,7 +945,7 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan) * Removes the hash for quick lookup of channels, must * be done when adding/removing channels. */ -void BKE_pose_channels_hash_make(bPose *pose) +void BKE_pose_channels_hash_ensure(bPose *pose) { if (!pose->chanhash) { bPoseChannel *pchan; @@ -1191,7 +1191,7 @@ void BKE_pose_free(bPose *pose) * and ID-Props, used when duplicating bones in editmode. * (unlike copy_pose_channel_data which only does posing-related stuff). * - * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify) + * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_ensure) */ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from) { @@ -1774,7 +1774,7 @@ void what_does_obaction(Object *ob, * allocation and also will make lookup slower. */ if (pose->chanbase.first != pose->chanbase.last) { - BKE_pose_channels_hash_make(pose); + BKE_pose_channels_hash_ensure(pose); } if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(pose); diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 447ed8fbe14..68de3e93a8e 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -354,7 +354,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag) } /* duplicate NLA data */ - BKE_nla_tracks_copy(bmain, &dadt->nla_tracks, &adt->nla_tracks, flag); + BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag); /* duplicate drivers (F-Curves) */ BKE_fcurves_copy(&dadt->drivers, &adt->drivers); diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 1075a46e72b..bcfd34ab42f 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version) { static char version_str[5]; BLI_assert(version < 1000); - BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100); + BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100); return version_str; } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index da8a3b49f3c..80992cff34d 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2444,7 +2444,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected) static int rebuild_pose_bone( bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p) { - bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */ + bPoseChannel *pchan = BKE_pose_channel_ensure(pose, bone->name); /* verify checks and/or adds */ pchan->bone = bone; pchan->parent = parchan; @@ -2562,7 +2562,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_ /* and a check for garbage */ BKE_pose_channels_clear_with_null_bone(pose, do_id_user); - BKE_pose_channels_hash_make(pose); + BKE_pose_channels_hash_ensure(pose); for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { /* Find the custom B-Bone handles. */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index ac582fc30e7..c24630c5666 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -44,196 +44,10 @@ using blender::float3; using blender::Set; using blender::StringRef; using blender::StringRefNull; -using blender::bke::ReadAttributePtr; -using blender::bke::WriteAttributePtr; using blender::fn::GMutableSpan; namespace blender::bke { -/* -------------------------------------------------------------------- */ -/** \name Attribute Accessor implementations - * \{ */ - -ReadAttribute::~ReadAttribute() -{ - if (array_is_temporary_ && array_buffer_ != nullptr) { - cpp_type_.destruct_n(array_buffer_, size_); - MEM_freeN(array_buffer_); - } -} - -fn::GSpan ReadAttribute::get_span() const -{ - if (size_ == 0) { - return fn::GSpan(cpp_type_); - } - if (array_buffer_ == nullptr) { - std::lock_guard lock{span_mutex_}; - if (array_buffer_ == nullptr) { - this->initialize_span(); - } - } - return fn::GSpan(cpp_type_, array_buffer_, size_); -} - -void ReadAttribute::initialize_span() const -{ - const int element_size = cpp_type_.size(); - array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); - array_is_temporary_ = true; - for (const int i : IndexRange(size_)) { - this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); - } -} - -WriteAttribute::~WriteAttribute() -{ - if (array_should_be_applied_) { - CLOG_ERROR(&LOG, "Forgot to call apply_span."); - } - if (array_is_temporary_ && array_buffer_ != nullptr) { - cpp_type_.destruct_n(array_buffer_, size_); - MEM_freeN(array_buffer_); - } -} - -/** - * Get a mutable span that can be modified. When all modifications to the attribute are done, - * #apply_span should be called. */ -fn::GMutableSpan WriteAttribute::get_span() -{ - if (size_ == 0) { - return fn::GMutableSpan(cpp_type_); - } - if (array_buffer_ == nullptr) { - this->initialize_span(false); - } - array_should_be_applied_ = true; - return fn::GMutableSpan(cpp_type_, array_buffer_, size_); -} - -fn::GMutableSpan WriteAttribute::get_span_for_write_only() -{ - if (size_ == 0) { - return fn::GMutableSpan(cpp_type_); - } - if (array_buffer_ == nullptr) { - this->initialize_span(true); - } - array_should_be_applied_ = true; - return fn::GMutableSpan(cpp_type_, array_buffer_, size_); -} - -void WriteAttribute::initialize_span(const bool write_only) -{ - const int element_size = cpp_type_.size(); - array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__); - array_is_temporary_ = true; - if (write_only) { - /* This does nothing for trivial types, but is necessary for general correctness. */ - cpp_type_.construct_default_n(array_buffer_, size_); - } - else { - for (const int i : IndexRange(size_)) { - this->get(i, POINTER_OFFSET(array_buffer_, i * element_size)); - } - } -} - -void WriteAttribute::apply_span() -{ - this->apply_span_if_necessary(); - array_should_be_applied_ = false; -} - -void WriteAttribute::apply_span_if_necessary() -{ - /* Only works when the span has been initialized beforehand. */ - BLI_assert(array_buffer_ != nullptr); - - const int element_size = cpp_type_.size(); - for (const int i : IndexRange(size_)) { - this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); - } -} - -/* This is used by the #OutputAttributePtr class. */ -class TemporaryWriteAttribute final : public WriteAttribute { - public: - GMutableSpan data; - GeometryComponent &component; - std::string final_name; - - TemporaryWriteAttribute(AttributeDomain domain, - GMutableSpan data, - GeometryComponent &component, - std::string final_name) - : WriteAttribute(domain, data.type(), data.size()), - data(data), - component(component), - final_name(std::move(final_name)) - { - } - - ~TemporaryWriteAttribute() override - { - if (data.data() != nullptr) { - cpp_type_.destruct_n(data.data(), data.size()); - MEM_freeN(data.data()); - } - } - - void get_internal(const int64_t index, void *r_value) const override - { - data.type().copy_to_uninitialized(data[index], r_value); - } - - void set_internal(const int64_t index, const void *value) override - { - data.type().copy_to_initialized(value, data[index]); - } - - void initialize_span(const bool UNUSED(write_only)) override - { - array_buffer_ = data.data(); - array_is_temporary_ = false; - } - - void apply_span_if_necessary() override - { - /* Do nothing, because the span contains the attribute itself already. */ - } -}; - -class ConvertedReadAttribute final : public ReadAttribute { - private: - const CPPType &from_type_; - const CPPType &to_type_; - ReadAttributePtr base_attribute_; - void (*convert_)(const void *src, void *dst); - - public: - ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type) - : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()), - from_type_(base_attribute->cpp_type()), - to_type_(to_type), - base_attribute_(std::move(base_attribute)) - { - const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - convert_ = conversions.get_conversion_functions(base_attribute_->cpp_type(), to_type) - ->convert_single_to_uninitialized; - } - - void get_internal(const int64_t index, void *r_value) const override - { - BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); - base_attribute_->get(index, buffer); - convert_(buffer, r_value); - } -}; - -/** \} */ - const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) { switch (type) { @@ -329,10 +143,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_ static int attribute_domain_priority(const AttributeDomain domain) { switch (domain) { -#if 0 case ATTR_DOMAIN_CURVE: return 0; -#endif case ATTR_DOMAIN_FACE: return 1; case ATTR_DOMAIN_EDGE: @@ -368,7 +180,27 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains) return highest_priority_domain; } -ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read( +void OutputAttribute::save() +{ + save_has_been_called_ = true; + if (optional_span_varray_.has_value()) { + optional_span_varray_->save(); + } + if (save_) { + save_(*this); + } +} + +OutputAttribute::~OutputAttribute() +{ + if (!save_has_been_called_) { + if (varray_) { + std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + } + } +} + +GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( const GeometryComponent &component) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -384,7 +216,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read( return as_read_attribute_(data, domain_size); } -WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write( +GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( GeometryComponent &component) const { if (writable_ != Writable) { @@ -430,7 +262,43 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co return delete_success; } -bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const +static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) +{ + switch (initializer.type) { + case AttributeInit::Type::Default: { + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + return data != nullptr; + } + case AttributeInit::Type::VArray: { + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + if (data == nullptr) { + return false; + } + const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), data); + return true; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast<const AttributeInitMove &>(initializer).data; + void *data = CustomData_add_layer( + &custom_data, data_type, CD_ASSIGN, source_data, domain_size); + if (data == nullptr) { + MEM_freeN(source_data); + return false; + } + return true; + } + } + + BLI_assert_unreachable(); + return false; +} + +bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, + const AttributeInit &initializer) const { if (createable_ != Creatable) { return false; @@ -443,10 +311,10 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) co /* Exists already. */ return false; } + const int domain_size = component.attribute_domain_size(domain_); - const void *data = CustomData_add_layer( - custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size); - const bool success = data != nullptr; + const bool success = add_custom_data_layer_from_attribute_init( + *custom_data, stored_type_, domain_size, initializer); if (success) { custom_data_access_.update_custom_data_pointers(component); } @@ -463,7 +331,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return data != nullptr; } -ReadAttributePtr CustomDataAttributeProvider::try_get_for_read( +ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( const GeometryComponent &component, const StringRef attribute_name) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -496,7 +364,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read( return {}; } -WriteAttributePtr CustomDataAttributeProvider::try_get_for_write( +WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( GeometryComponent &component, const StringRef attribute_name) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); @@ -548,10 +416,52 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, return false; } +static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name, + CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) +{ + char attribute_name_c[MAX_NAME]; + attribute_name.copy(attribute_name_c); + + switch (initializer.type) { + case AttributeInit::Type::Default: { + void *data = CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + return data != nullptr; + } + case AttributeInit::Type::VArray: { + void *data = CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + if (data == nullptr) { + return false; + } + const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), data); + return true; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast<const AttributeInitMove &>(initializer).data; + void *data = CustomData_add_layer_named( + &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c); + if (data == nullptr) { + MEM_freeN(source_data); + return false; + } + return true; + } + } + + BLI_assert_unreachable(); + return false; +} + bool CustomDataAttributeProvider::try_create(GeometryComponent &component, const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) const + const CustomDataType data_type, + const AttributeInit &initializer) const { if (domain_ != domain) { return false; @@ -569,10 +479,8 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, } } const int domain_size = component.attribute_domain_size(domain_); - char attribute_name_c[MAX_NAME]; - attribute_name.copy(attribute_name_c); - CustomData_add_layer_named( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + add_named_custom_data_layer_from_attribute_init( + attribute_name, *custom_data, data_type, domain_size, initializer); return true; } @@ -595,7 +503,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com return true; } -ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read( +ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( const GeometryComponent &component, const StringRef attribute_name) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -606,14 +514,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read( if (layer.type == stored_type_) { if (layer.name == attribute_name) { const int domain_size = component.attribute_domain_size(domain_); - return as_read_attribute_(layer.data, domain_size); + return {as_read_attribute_(layer.data, domain_size), domain_}; } } } return {}; } -WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write( +WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( GeometryComponent &component, const StringRef attribute_name) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); @@ -630,7 +538,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write( if (data_old != data_new) { custom_data_access_.update_custom_data_pointers(component); } - return as_write_attribute_(layer.data, domain_size); + return {as_write_attribute_(layer.data, domain_size), domain_}; } } } @@ -708,7 +616,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain return 0; } -ReadAttributePtr GeometryComponent::attribute_try_get_for_read( +bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const +{ + using namespace blender::bke; + const ComponentAttributeProviders *providers = this->get_attribute_providers(); + if (providers == nullptr) { + return false; + } + return providers->builtin_attribute_providers().contains_as(attribute_name); +} + +blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( const StringRef attribute_name) const { using namespace blender::bke; @@ -719,11 +637,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read( const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); if (builtin_provider != nullptr) { - return builtin_provider->try_get_for_read(*this); + return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name); + ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name); if (attribute) { return attribute; } @@ -731,16 +649,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read( return {}; } -ReadAttributePtr GeometryComponent::attribute_try_adapt_domain( - ReadAttributePtr attribute, const AttributeDomain new_domain) const +std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { - if (attribute && attribute->domain() == new_domain) { - return attribute; + if (from_domain == to_domain) { + return varray; } return {}; } -WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name) +blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( + const StringRef attribute_name) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); @@ -750,11 +671,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); if (builtin_provider != nullptr) { - return builtin_provider->try_get_for_write(*this); + return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name); + WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name); if (attribute) { return attribute; } @@ -784,7 +705,8 @@ bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) bool GeometryComponent::attribute_try_create(const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) + const CustomDataType data_type, + const AttributeInit &initializer) { using namespace blender::bke; if (attribute_name.is_empty()) { @@ -803,17 +725,36 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name, if (builtin_provider->data_type() != data_type) { return false; } - return builtin_provider->try_create(*this); + return builtin_provider->try_create(*this, initializer); } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) { + if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) { return true; } } return false; } +bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name, + const AttributeInit &initializer) +{ + using namespace blender::bke; + if (attribute_name.is_empty()) { + return false; + } + const ComponentAttributeProviders *providers = this->get_attribute_providers(); + if (providers == nullptr) { + return false; + } + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); + if (builtin_provider == nullptr) { + return false; + } + return builtin_provider->try_create(*this, initializer); +} + Set<std::string> GeometryComponent::attribute_names() const { Set<std::string> attributes; @@ -867,264 +808,283 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const { - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); if (attribute) { return true; } return false; } -static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute, - const blender::fn::CPPType &to_type) +std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( + const StringRef attribute_name) const { - const blender::fn::CPPType &from_type = attribute->cpp_type(); - if (from_type == to_type) { - return attribute; - } + std::optional<AttributeMetaData> result{std::nullopt}; + this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { + if (attribute_name == name) { + result = meta_data; + return false; + } + return true; + }); + return result; +} +static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type( + std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type) +{ const blender::nodes::DataTypeConversions &conversions = blender::nodes::get_implicit_type_conversions(); - if (!conversions.is_convertible(from_type, to_type)) { - return {}; - } - - return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type); + return conversions.try_convert(std::move(varray), to_type); } -ReadAttributePtr GeometryComponent::attribute_try_get_for_read( +std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read( const StringRef attribute_name, const AttributeDomain domain, const CustomDataType data_type) const { - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); if (!attribute) { return {}; } - if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) { - attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); - if (!attribute) { + std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray); + if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) { + varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); + if (!varray) { return {}; } } const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); BLI_assert(cpp_type != nullptr); - if (attribute->cpp_type() != *cpp_type) { - attribute = try_adapt_data_type(std::move(attribute), *cpp_type); - if (!attribute) { + if (varray->type() != *cpp_type) { + varray = try_adapt_data_type(std::move(varray), *cpp_type); + if (!varray) { return {}; } } - return attribute; + return varray; } -ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name, - const AttributeDomain domain) const +std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read( + const StringRef attribute_name, const AttributeDomain domain) const { if (!this->attribute_domain_supported(domain)) { return {}; } - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); if (!attribute) { return {}; } - if (attribute->domain() != domain) { - attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); - if (!attribute) { - return {}; - } + if (attribute.domain != domain) { + return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain); } - return attribute; + return std::move(attribute.varray); } -ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) const +blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( + const blender::StringRef attribute_name, const CustomDataType data_type) const { - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type); - if (attribute) { - return attribute; + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + if (!attribute) { + return {}; } - return this->attribute_get_constant_for_read(domain, data_type, default_value); -} - -blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read( - const AttributeDomain domain, const CustomDataType data_type, const void *value) const -{ - BLI_assert(this->attribute_domain_supported(domain)); - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - if (value == nullptr) { - value = cpp_type->default_value(); + const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); + BLI_assert(type != nullptr); + if (attribute.varray->type() == *type) { + return attribute; } - const int domain_size = this->attribute_domain_size(domain); - return std::make_unique<blender::bke::ConstantReadAttribute>( - domain, domain_size, *cpp_type, value); + const blender::nodes::DataTypeConversions &conversions = + blender::nodes::get_implicit_type_conversions(); + return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain}; } -blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted( +std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read( + const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType in_data_type, - const CustomDataType out_data_type, - const void *value) const + const CustomDataType data_type, + const void *default_value) const { - BLI_assert(this->attribute_domain_supported(domain)); - if (value == nullptr || in_data_type == out_data_type) { - return this->attribute_get_constant_for_read(domain, out_data_type, value); + std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read( + attribute_name, domain, data_type); + if (varray) { + return varray; + } + const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); + if (default_value == nullptr) { + default_value = type->default_value(); } - - const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type( - in_data_type); - const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type( - out_data_type); - BLI_assert(in_cpp_type != nullptr); - BLI_assert(out_cpp_type != nullptr); - - const blender::nodes::DataTypeConversions &conversions = - blender::nodes::get_implicit_type_conversions(); - BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type)); - - void *out_value = alloca(out_cpp_type->size()); - conversions.convert_to_uninitialized(*in_cpp_type, *out_cpp_type, value, out_value); - const int domain_size = this->attribute_domain_size(domain); - blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>( - domain, domain_size, *out_cpp_type, out_value); - - out_cpp_type->destruct(out_value); - return attribute; + return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value); } -OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) -{ - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - - WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name); +class GVMutableAttribute_For_OutputAttribute + : public blender::fn::GVMutableArray_For_GMutableSpan { + public: + GeometryComponent *component; + std::string final_name; - /* If the attribute doesn't exist, make a new one with the correct type. */ - if (!attribute) { - this->attribute_try_create(attribute_name, domain, data_type); - attribute = this->attribute_try_get_for_write(attribute_name); - if (attribute && default_value != nullptr) { - void *data = attribute->get_span_for_write_only().data(); - cpp_type->fill_initialized(default_value, data, attribute->size()); - attribute->apply_span(); - } - return OutputAttributePtr(std::move(attribute)); + GVMutableAttribute_For_OutputAttribute(GMutableSpan data, + GeometryComponent &component, + std::string final_name) + : blender::fn::GVMutableArray_For_GMutableSpan(data), + component(&component), + final_name(std::move(final_name)) + { } - /* If an existing attribute has a matching domain and type, just use that. */ - if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) { - return OutputAttributePtr(std::move(attribute)); + ~GVMutableAttribute_For_OutputAttribute() override + { + type_->destruct_n(data_, size_); + MEM_freeN(data_); } +}; - /* Otherwise create a temporary buffer to use before saving the new attribute. */ - return OutputAttributePtr(*this, domain, attribute_name, data_type); -} - -/* Construct from an attribute that already exists in the geometry component. */ -OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute) - : attribute_(std::move(attribute)) +static void save_output_attribute(blender::bke::OutputAttribute &output_attribute) { -} + using namespace blender; + using namespace blender::fn; + using namespace blender::bke; -/* Construct a temporary attribute that has to replace an existing one later on. */ -OutputAttributePtr::OutputAttributePtr(GeometryComponent &component, - AttributeDomain domain, - std::string final_name, - CustomDataType data_type) -{ - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); + GVMutableAttribute_For_OutputAttribute &varray = + dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray()); - const int domain_size = component.attribute_domain_size(domain); - void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__); - GMutableSpan new_span{*cpp_type, buffer, domain_size}; + GeometryComponent &component = *varray.component; + const StringRefNull name = varray.final_name; + const AttributeDomain domain = output_attribute.domain(); + const CustomDataType data_type = output_attribute.custom_data_type(); + const CPPType &cpp_type = output_attribute.cpp_type(); - /* Copy converted values from conflicting attribute, in case the value is read. - * TODO: An optimization could be to not do this, when the caller says that the attribute will - * only be written. */ - ReadAttributePtr src_attribute = component.attribute_get_for_read( - final_name, domain, data_type, nullptr); - for (const int i : blender::IndexRange(domain_size)) { - src_attribute->get(i, new_span[i]); + component.attribute_try_delete(name); + if (!component.attribute_try_create( + varray.final_name, domain, data_type, AttributeInitDefault())) { + CLOG_WARN(&LOG, + "Could not create the '%s' attribute with type '%s'.", + name.c_str(), + cpp_type.name().c_str()); + return; + } + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name); + BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer); + for (const int i : IndexRange(varray.size())) { + varray.get(i, buffer); + write_attribute.varray->set_by_relocate(i, buffer); } - - attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>( - domain, new_span, component, std::move(final_name)); } -/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This - * might delete another attribute with the same name. */ -void OutputAttributePtr::save() +static blender::bke::OutputAttribute create_output_attribute( + GeometryComponent &component, + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const bool ignore_old_values, + const void *default_value) { - if (!attribute_) { - CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore."); - return; + using namespace blender; + using namespace blender::fn; + using namespace blender::bke; + + if (attribute_name.is_empty()) { + return {}; } - blender::bke::TemporaryWriteAttribute *attribute = - dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get()); + const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type); + BLI_assert(cpp_type != nullptr); + const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - if (attribute == nullptr) { - /* The attribute is saved already. */ - attribute_.reset(); - return; + if (component.attribute_is_builtin(attribute_name)) { + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + if (default_value) { + const int64_t domain_size = component.attribute_domain_size(domain); + const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; + component.attribute_try_create_builtin(attribute_name, + AttributeInitVArray(&default_varray)); + } + else { + component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); + } + attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + /* Builtin attribute does not exist and can't be created. */ + return {}; + } + } + if (attribute.domain != domain) { + /* Builtin attribute is on different domain. */ + return {}; + } + GVMutableArrayPtr varray = std::move(attribute.varray); + if (varray->type() == *cpp_type) { + /* Builtin attribute matches exactly. */ + return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); + } + /* Builtin attribute is on the same domain but has a different data type. */ + varray = conversions.try_convert(std::move(varray), *cpp_type); + return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); } - StringRefNull name = attribute->final_name; - const blender::fn::CPPType &cpp_type = attribute->cpp_type(); + const int domain_size = component.attribute_domain_size(domain); - /* Delete an existing attribute with the same name if necessary. */ - attribute->component.attribute_try_delete(name); + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + if (default_value) { + const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; + component.attribute_try_create( + attribute_name, domain, data_type, AttributeInitVArray(&default_varray)); + } + else { + component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault()); + } - if (!attribute->component.attribute_try_create( - name, attribute_->domain(), attribute_->custom_data_type())) { - /* Cannot create the target attribute for some reason. */ - CLOG_WARN(&LOG, - "Creating the '%s' attribute with type '%s' failed.", - name.c_str(), - cpp_type.name().c_str()); - attribute_.reset(); - return; + attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + /* Can't create the attribute. */ + return {}; + } + } + if (attribute.domain == domain && attribute.varray->type() == *cpp_type) { + /* Existing generic attribute matches exactly. */ + return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values); } - WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name); - - GMutableSpan temp_span = attribute->data; - GMutableSpan new_span = new_attribute->get_span_for_write_only(); - BLI_assert(temp_span.size() == new_span.size()); - - /* Currently we copy over the attribute. In the future we want to reuse the buffer. */ - cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size()); - new_attribute->apply_span(); + /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing + * attribute after processing is done. */ + void *data = MEM_mallocN_aligned( + cpp_type->size() * domain_size, cpp_type->alignment(), __func__); + if (ignore_old_values) { + /* This does nothing for trivially constructible types, but is necessary for correctness. */ + cpp_type->construct_default_n(data, domain); + } + else { + /* Fill the temporary array with values from the existing attribute. */ + GVArrayPtr old_varray = component.attribute_get_for_read( + attribute_name, domain, data_type, default_value); + old_varray->materialize_to_uninitialized(IndexRange(domain_size), data); + } + GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>( + GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name); - attribute_.reset(); + return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } -OutputAttributePtr::~OutputAttributePtr() +blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output( + const StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) { - if (attribute_) { - CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save."); - } + return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value); } -/* Utility function to call #apply_span and #save in the right order. */ -void OutputAttributePtr::apply_span_and_save() +blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only( + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) { - BLI_assert(attribute_); - attribute_->apply_span(); - this->save(); + return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr); } - -/** \} */ diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 806d10e9e89..b3a795faa30 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -24,166 +24,8 @@ namespace blender::bke { -class ConstantReadAttribute final : public ReadAttribute { - private: - void *value_; - - public: - ConstantReadAttribute(AttributeDomain domain, - const int64_t size, - const CPPType &type, - const void *value) - : ReadAttribute(domain, type, size) - { - value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); - type.copy_to_uninitialized(value, value_); - } - - ~ConstantReadAttribute() override - { - this->cpp_type_.destruct(value_); - MEM_freeN(value_); - } - - void get_internal(const int64_t UNUSED(index), void *r_value) const override - { - this->cpp_type_.copy_to_uninitialized(value_, r_value); - } - - void initialize_span() const override - { - const int element_size = cpp_type_.size(); - array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); - array_is_temporary_ = true; - cpp_type_.fill_uninitialized(value_, array_buffer_, size_); - } -}; - -template<typename T> class ArrayReadAttribute final : public ReadAttribute { - private: - Span<T> data_; - - public: - ArrayReadAttribute(AttributeDomain domain, Span<T> data) - : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void initialize_span() const override - { - /* The data will not be modified, so this const_cast is fine. */ - array_buffer_ = const_cast<T *>(data_.data()); - array_is_temporary_ = false; - } -}; - -template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute { - private: - Array<T> data_; - - public: - OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data) - : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data)) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void initialize_span() const override - { - /* The data will not be modified, so this const_cast is fine. */ - array_buffer_ = const_cast<T *>(data_.data()); - array_is_temporary_ = false; - } -}; - -template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -class DerivedArrayReadAttribute final : public ReadAttribute { - private: - Span<StructT> data_; - - public: - DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data) - : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - const StructT &struct_value = data_[index]; - const ElemT value = GetFunc(struct_value); - new (r_value) ElemT(value); - } -}; - -template<typename T> class ArrayWriteAttribute final : public WriteAttribute { - private: - MutableSpan<T> data_; - - public: - ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data) - : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void set_internal(const int64_t index, const void *value) override - { - data_[index] = *reinterpret_cast<const T *>(value); - } - - void initialize_span(const bool UNUSED(write_only)) override - { - array_buffer_ = data_.data(); - array_is_temporary_ = false; - } - - void apply_span_if_necessary() override - { - /* Do nothing, because the span contains the attribute itself already. */ - } -}; - -template<typename StructT, - typename ElemT, - ElemT (*GetFunc)(const StructT &), - void (*SetFunc)(StructT &, const ElemT &)> -class DerivedArrayWriteAttribute final : public WriteAttribute { - private: - MutableSpan<StructT> data_; - - public: - DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data) - : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - const StructT &struct_value = data_[index]; - const ElemT value = GetFunc(struct_value); - new (r_value) ElemT(value); - } - - void set_internal(const int64_t index, const void *value) override - { - StructT &struct_value = data_[index]; - const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value); - SetFunc(struct_value, typed_value); - } -}; +using fn::GVArrayPtr; +using fn::GVMutableArrayPtr; /** * Utility to group together multiple functions that are used to access custom data on geometry @@ -244,10 +86,11 @@ class BuiltinAttributeProvider { { } - virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0; - virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0; + virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0; + virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0; virtual bool try_delete(GeometryComponent &component) const = 0; - virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0; + virtual bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const = 0; virtual bool exists(const GeometryComponent &component) const = 0; StringRefNull name() const @@ -272,15 +115,16 @@ class BuiltinAttributeProvider { */ class DynamicAttributesProvider { public: - virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const = 0; - virtual WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const = 0; + virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const = 0; + virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const = 0; virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), const StringRef UNUSED(attribute_name), const AttributeDomain UNUSED(domain), - const CustomDataType UNUSED(data_type)) const + const CustomDataType UNUSED(data_type), + const AttributeInit &UNUSED(initializer)) const { /* Some providers should not create new attributes. */ return false; @@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final; bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; bool try_create(GeometryComponent &component, const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) const final; + const CustomDataType data_type, + const AttributeInit &initializer) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; @@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { private: template<typename T> - ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer, - const int domain_size) const + ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer, + const int domain_size) const { - return std::make_unique<ArrayReadAttribute<T>>( - domain_, Span(static_cast<const T *>(layer.data), domain_size)); + return {std::make_unique<fn::GVArray_For_Span<T>>( + Span(static_cast<const T *>(layer.data), domain_size)), + domain_}; } template<typename T> - WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const + WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer, + const int domain_size) const { - return std::make_unique<ArrayWriteAttribute<T>>( - domain_, MutableSpan(static_cast<T *>(layer.data), domain_size)); + return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>( + MutableSpan(static_cast<T *>(layer.data), domain_size)), + domain_}; } bool type_is_supported(CustomDataType data_type) const @@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { */ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { private: - using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size); - using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size); + using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); + using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size); const AttributeDomain domain_; const CustomDataType attribute_type_; const CustomDataType stored_type_; @@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final; bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; @@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { * the #MVert struct, but is exposed as float3 attribute. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size); - using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size); + using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); + using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size); using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; @@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component) const final; + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final; + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final; bool try_delete(GeometryComponent &component) const final; - bool try_create(GeometryComponent &component) const final; + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; bool exists(const GeometryComponent &component) const final; }; diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index e8879cdda8f..e84b485c466 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -132,7 +132,7 @@ static void blender_version_init(void) BLI_snprintf(blender_version_string, ARRAY_SIZE(blender_version_string), - "%d.%02d.%d%s", + "%d.%01d.%d%s", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_VERSION_PATCH, diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index ef567044282..20c5af0efb6 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -989,6 +989,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_smoothfac = 0.1f; brush->gpencil_settings->draw_smoothlvl = 1; brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->dilate_pixels = 1; brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 2ee030ca83f..17f36bd0860 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2836,7 +2836,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph, * including rotation order, otherwise this fails. */ pchan = cob->pchan; - tchan = BKE_pose_channel_verify(&pose, pchan->name); + tchan = BKE_pose_channel_ensure(&pose, pchan->name); tchan->rotmode = pchan->rotmode; /* evaluate action using workob (it will only set the PoseChannel in question) */ diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index cbf7a4483c0..81830f5bb61 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -80,7 +80,17 @@ struct bContext { struct ARegion *menu; struct wmGizmoGroup *gizmo_group; struct bContextStore *store; - const char *operator_poll_msg; /* reason for poll failing */ + + /* Operator poll. */ + /** + * Store the reason the poll function fails (static string, not allocated). + * For more advanced formatting use `operator_poll_msg_dyn_params`. + */ + const char *operator_poll_msg; + /** + * Store values to dynamically to create the string (called when a tool-tip is shown). + */ + struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params; } wm; /* data context */ @@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C) { bContext *newC = MEM_dupallocN((void *)C); + memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params)); + return newC; } void CTX_free(bContext *C) { + /* This may contain a dynamically allocated message, free. */ + CTX_wm_operator_poll_msg_clear(C); + MEM_freeN(C); } @@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup) C->wm.gizmo_group = gzgroup; } +void CTX_wm_operator_poll_msg_clear(bContext *C) +{ + struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; + if (params->free_fn != NULL) { + params->free_fn(C, params->user_data); + } + params->get_fn = NULL; + params->free_fn = NULL; + params->user_data = NULL; + + C->wm.operator_poll_msg = NULL; +} void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg) { + CTX_wm_operator_poll_msg_clear(C); + C->wm.operator_poll_msg = msg; } -const char *CTX_wm_operator_poll_msg_get(bContext *C) +void CTX_wm_operator_poll_msg_set_dynamic(bContext *C, + const struct bContextPollMsgDyn_Params *params) { + CTX_wm_operator_poll_msg_clear(C); + + C->wm.operator_poll_msg_dyn_params = *params; +} + +const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free) +{ + struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; + if (params->get_fn != NULL) { + char *msg = params->get_fn(C, params->user_data); + if (msg != NULL) { + *r_free = true; + } + return msg; + } + + *r_free = false; return IFACE_(C->wm.operator_poll_msg); } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc new file mode 100644 index 00000000000..4c63c7f05ee --- /dev/null +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -0,0 +1,184 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_listbase.h" +#include "BLI_span.hh" + +#include "DNA_curve_types.h" + +#include "BKE_curve.h" +#include "BKE_spline.hh" + +using blender::float3; +using blender::float4x4; +using blender::Span; + +CurveEval *CurveEval::copy() +{ + CurveEval *new_curve = new CurveEval(); + + for (SplinePtr &spline : this->splines) { + new_curve->splines.append(spline->copy()); + } + + return new_curve; +} + +void CurveEval::translate(const float3 &translation) +{ + for (SplinePtr &spline : this->splines) { + spline->translate(translation); + spline->mark_cache_invalid(); + } +} + +void CurveEval::transform(const float4x4 &matrix) +{ + for (SplinePtr &spline : this->splines) { + spline->transform(matrix); + } +} + +void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const +{ + for (const SplinePtr &spline : this->splines) { + spline->bounds_min_max(min, max, use_evaluated); + } +} + +static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) +{ + switch (dna_handle_type) { + case HD_FREE: + return BezierSpline::HandleType::Free; + case HD_AUTO: + return BezierSpline::HandleType::Auto; + case HD_VECT: + return BezierSpline::HandleType::Vector; + case HD_ALIGN: + return BezierSpline::HandleType::Align; + case HD_AUTO_ANIM: + return BezierSpline::HandleType::Auto; + case HD_ALIGN_DOUBLESIDE: + return BezierSpline::HandleType::Align; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode) +{ + switch (twist_mode) { + case CU_TWIST_Z_UP: + return Spline::NormalCalculationMode::ZUp; + case CU_TWIST_MINIMUM: + return Spline::NormalCalculationMode::Minimum; + case CU_TWIST_TANGENT: + return Spline::NormalCalculationMode::Tangent; + } + BLI_assert_unreachable(); + return Spline::NormalCalculationMode::Minimum; +} + +static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag) +{ + switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) { + case CU_NURB_ENDPOINT: + return NURBSpline::KnotsMode::EndPoint; + case CU_NURB_BEZIER: + return NURBSpline::KnotsMode::Bezier; + default: + return NURBSpline::KnotsMode::Normal; + } + + BLI_assert_unreachable(); + return NURBSpline::KnotsMode::Normal; +} + +std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + + const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve)); + + curve->splines.reserve(BLI_listbase_count(nurbs)); + + /* TODO: Optimize by reserving the correct points size. */ + LISTBASE_FOREACH (const Nurb *, nurb, nurbs) { + switch (nurb->type) { + case CU_BEZIER: { + std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->set_resolution(nurb->resolu); + spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); + + for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) { + spline->add_point(bezt.vec[1], + handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1), + bezt.vec[0], + handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2), + bezt.vec[2], + bezt.radius, + bezt.tilt); + } + + curve->splines.append(std::move(spline)); + break; + } + case CU_NURBS: { + std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); + spline->set_resolution(nurb->resolu); + spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); + spline->set_order(nurb->orderu); + spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu); + + for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { + spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]); + } + + curve->splines.append(std::move(spline)); + break; + } + case CU_POLY: { + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); + + for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { + spline->add_point(bp.vec, bp.radius, bp.tilt); + } + + curve->splines.append(std::move(spline)); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + } + + /* Note: Normal mode is stored separately in each spline to facilitate combining splines + * from multiple curve objects, where the value may be different. */ + const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve( + dna_curve.twist_mode); + for (SplinePtr &spline : curve->splines) { + spline->normal_mode = normal_mode; + } + + return curve; +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c index 752e0d4dfcf..00cdc7b3031 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.c @@ -1015,7 +1015,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, { const float total_length = BKE_curveprofile_total_length(profile); const float segment_length = total_length / n_segments; - float length_travelled = 0.0f; float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0); float distance_to_previous_table_point = 0.0f; int i_table = 0; @@ -1029,7 +1028,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, for (int i = 1; i < n_segments; i++) { /* Travel over all of the points that fit inside this segment. */ while (distance_to_next_table_point < segment_left) { - length_travelled += distance_to_next_table_point; segment_left -= distance_to_next_table_point; i_table++; distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table); @@ -1057,7 +1055,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, /* We sampled in between this table point and the next, so the next travel step is smaller. */ distance_to_next_table_point -= segment_left; distance_to_previous_table_point += segment_left; - length_travelled += segment_left; segment_left = segment_length; } } diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.cc index ad8939fa5d1..20534ef933b 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.cc @@ -21,9 +21,9 @@ * \ingroup bke */ -#include <math.h> -#include <stdio.h> -#include <string.h> +#include <cmath> +#include <cstdio> +#include <cstring> #include "MEM_guardedalloc.h" @@ -82,7 +82,7 @@ void BKE_displist_free(ListBase *lb) { DispList *dl; - while ((dl = BLI_pophead(lb))) { + while ((dl = (DispList *)BLI_pophead(lb))) { BKE_displist_elem_free(dl); } } @@ -95,7 +95,7 @@ DispList *BKE_displist_find_or_create(ListBase *lb, int type) } } - DispList *dl = MEM_callocN(sizeof(DispList), "find_disp"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "find_disp"); dl->type = type; BLI_addtail(lb, dl); @@ -110,7 +110,7 @@ DispList *BKE_displist_find(ListBase *lb, int type) } } - return NULL; + return nullptr; } bool BKE_displist_has_faces(const ListBase *lb) @@ -129,11 +129,11 @@ void BKE_displist_copy(ListBase *lbn, const ListBase *lb) BKE_displist_free(lbn); LISTBASE_FOREACH (const DispList *, dl, lb) { - DispList *dln = MEM_dupallocN(dl); + DispList *dln = (DispList *)MEM_dupallocN(dl); BLI_addtail(lbn, dln); - dln->verts = MEM_dupallocN(dl->verts); - dln->nors = MEM_dupallocN(dl->nors); - dln->index = MEM_dupallocN(dl->index); + dln->verts = (float *)MEM_dupallocN(dl->verts); + dln->nors = (float *)MEM_dupallocN(dl->nors); + dln->index = (int *)MEM_dupallocN(dl->index); } } @@ -146,8 +146,8 @@ void BKE_displist_normals_add(ListBase *lb) LISTBASE_FOREACH (DispList *, dl, lb) { if (dl->type == DL_INDEX3) { - if (dl->nors == NULL) { - dl->nors = MEM_callocN(sizeof(float[3]), "dlnors"); + if (dl->nors == nullptr) { + dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors"); if (dl->flag & DL_BACK_CURVE) { dl->nors[2] = -1.0f; @@ -158,8 +158,8 @@ void BKE_displist_normals_add(ListBase *lb) } } else if (dl->type == DL_SURF) { - if (dl->nors == NULL) { - dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors"); + if (dl->nors == nullptr) { + dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors"); vdata = dl->verts; ndata = dl->nors; @@ -338,9 +338,9 @@ static void curve_to_displist(const Curve *cu, * and resolution > 1. */ const bool use_cyclic_sample = is_cyclic && (samples_len != 2); - DispList *dl = MEM_callocN(sizeof(DispList), __func__); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__); /* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */ - dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts"); + dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts"); BLI_addtail(r_dispbase, dl); dl->parts = 1; dl->nr = samples_len; @@ -393,8 +393,8 @@ static void curve_to_displist(const Curve *cu, } else if (nu->type == CU_NURBS) { const int len = (resolution * SEGMENTSU(nu)); - DispList *dl = MEM_callocN(sizeof(DispList), __func__); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(r_dispbase, dl); dl->parts = 1; dl->nr = len; @@ -402,12 +402,12 @@ static void curve_to_displist(const Curve *cu, dl->charidx = nu->charidx; dl->type = is_cyclic ? DL_POLY : DL_SEGM; - BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3])); + BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3])); } else if (nu->type == CU_POLY) { const int len = nu->pntsu; - DispList *dl = MEM_callocN(sizeof(DispList), __func__); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(r_dispbase, dl); dl->parts = 1; dl->nr = len; @@ -435,7 +435,7 @@ void BKE_displist_fill(const ListBase *dispbase, const float normal_proj[3], const bool flip_normal) { - if (dispbase == NULL) { + if (dispbase == nullptr) { return; } if (BLI_listbase_is_empty(dispbase)) { @@ -471,14 +471,14 @@ void BKE_displist_fill(const ListBase *dispbase, sf_ctx.poly_nr++; /* Make verts and edges. */ - ScanFillVert *sf_vert = NULL; - ScanFillVert *sf_vert_last = NULL; - ScanFillVert *sf_vert_new = NULL; + ScanFillVert *sf_vert = nullptr; + ScanFillVert *sf_vert_last = nullptr; + ScanFillVert *sf_vert_new = nullptr; for (int i = 0; i < dl->nr; i++) { sf_vert_last = sf_vert; sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]); totvert++; - if (sf_vert_last == NULL) { + if (sf_vert_last == nullptr) { sf_vert_new = sf_vert; } else { @@ -486,7 +486,7 @@ void BKE_displist_fill(const ListBase *dispbase, } } - if (sf_vert != NULL && sf_vert_new != NULL) { + if (sf_vert != nullptr && sf_vert_new != nullptr) { BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new); } } @@ -503,7 +503,7 @@ void BKE_displist_fill(const ListBase *dispbase, const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj); if (totvert != 0 && triangles_len != 0) { - DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist"); + DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist"); dlnew->type = DL_INDEX3; dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE)); dlnew->rt = (dl_rt_accum & CU_SMOOTH); @@ -511,8 +511,8 @@ void BKE_displist_fill(const ListBase *dispbase, dlnew->nr = totvert; dlnew->parts = triangles_len; - dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex"); - dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts"); + dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex"); + dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts"); /* vert data */ int i; @@ -551,16 +551,16 @@ void BKE_displist_fill(const ListBase *dispbase, static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase) { - ListBase front = {NULL, NULL}; - ListBase back = {NULL, NULL}; + ListBase front = {nullptr, nullptr}; + ListBase back = {nullptr, nullptr}; LISTBASE_FOREACH (const DispList *, dl, dispbase) { if (dl->type == DL_SURF) { if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) { if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) { - DispList *dlnew = MEM_callocN(sizeof(DispList), __func__); + DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__); BLI_addtail(&front, dlnew); - dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); + dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); dlnew->nr = dl->parts; dlnew->parts = 1; dlnew->type = DL_POLY; @@ -577,9 +577,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase) } } if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) { - DispList *dlnew = MEM_callocN(sizeof(DispList), __func__); + DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__); BLI_addtail(&back, dlnew); - dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); + dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); dlnew->nr = dl->parts; dlnew->parts = 1; dlnew->type = DL_POLY; @@ -634,16 +634,16 @@ static float displist_calc_taper(Depsgraph *depsgraph, Object *taperobj, float fac) { - DispList *dl; - - if (taperobj == NULL || taperobj->type != OB_CURVE) { + if (taperobj == nullptr || taperobj->type != OB_CURVE) { return 1.0; } - dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL; - if (dl == NULL) { + DispList *dl = taperobj->runtime.curve_cache ? + (DispList *)taperobj->runtime.curve_cache->disp.first : + nullptr; + if (dl == nullptr) { BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false); - dl = taperobj->runtime.curve_cache->disp.first; + dl = (DispList *)taperobj->runtime.curve_cache->disp.first; } if (dl) { float minx, dx, *fp; @@ -693,7 +693,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob) BKE_displist_free(&(ob->runtime.curve_cache->disp)); } else { - ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall"); + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for MBall"); } BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp); @@ -738,9 +739,9 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene, required_mode |= eModifierMode_Editmode; } - pretessellatePoint = NULL; + pretessellatePoint = nullptr; for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; @@ -777,22 +778,22 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, VirtualModifierData virtualModifierData; ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); ModifierData *pretessellatePoint; - Curve *cu = ob->data; + Curve *cu = (Curve *)ob->data; int numElems = 0, numVerts = 0; const bool editmode = (!for_render && (cu->editnurb || cu->editfont)); - ModifierApplyFlag apply_flag = 0; - float(*deformedVerts)[3] = NULL; - float *keyVerts = NULL; + ModifierApplyFlag apply_flag = (ModifierApplyFlag)0; + float(*deformedVerts)[3] = nullptr; + float *keyVerts = nullptr; int required_mode; bool modified = false; BKE_modifiers_clear_errors(ob); if (editmode) { - apply_flag |= MOD_APPLY_USECACHE; + apply_flag = MOD_APPLY_USECACHE; } if (for_render) { - apply_flag |= MOD_APPLY_RENDER; + apply_flag = MOD_APPLY_RENDER; required_mode = eModifierMode_Render; } else { @@ -823,7 +824,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, if (pretessellatePoint) { for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; @@ -836,7 +837,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts); } - mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts); + mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts); modified = true; if (md == pretessellatePoint) { @@ -869,7 +870,7 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[ *r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr; } - allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts"); + allverts = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__); fp = (float *)allverts; LISTBASE_FOREACH (DispList *, dl, dispbase) { int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr); @@ -903,16 +904,16 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, VirtualModifierData virtualModifierData; ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); ModifierData *pretessellatePoint; - const Curve *cu = ob->data; + const Curve *cu = (const Curve *)ob->data; int required_mode = 0, totvert = 0; const bool editmode = (!for_render && (cu->editnurb || cu->editfont)); - Mesh *modified = NULL, *mesh_applied; - float(*vertCos)[3] = NULL; + Mesh *modified = nullptr, *mesh_applied; + float(*vertCos)[3] = nullptr; int useCache = !for_render; - ModifierApplyFlag apply_flag = 0; + ModifierApplyFlag apply_flag = (ModifierApplyFlag)0; if (for_render) { - apply_flag |= MOD_APPLY_RENDER; + apply_flag = MOD_APPLY_RENDER; required_mode = eModifierMode_Render; } else { @@ -920,9 +921,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, } const ModifierEvalContext mectx_deform = { - depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag}; + depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag}; const ModifierEvalContext mectx_apply = { - depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag}; + depsgraph, ob, useCache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag}; pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode); @@ -935,22 +936,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, } if (r_final && *r_final) { - BKE_id_free(NULL, *r_final); + BKE_id_free(nullptr, *r_final); } for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; } /* If we need normals, no choice, have to convert to mesh now. */ - bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md); + bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md); /* XXX 2.8 : now that batch cache is stored inside the ob->data * we need to create a Mesh for each curve that uses modifiers. */ - if (modified == NULL /* && need_normal */) { - if (vertCos != NULL) { + if (modified == nullptr /* && need_normal */) { + if (vertCos != nullptr) { displist_vert_coords_apply(dispbase, vertCos); } @@ -976,7 +977,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (!vertCos) { vertCos = displist_vert_coords_alloc(dispbase, &totvert); } - mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert); + mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert); } } else { @@ -991,8 +992,8 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (modified) { if (vertCos) { Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex( - NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE); - BKE_id_free(NULL, modified); + nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE); + BKE_id_free(nullptr, modified); modified = temp_mesh; BKE_mesh_vert_coords_apply(modified, vertCos); @@ -1013,7 +1014,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (vertCos) { /* Vertex coordinates were applied to necessary data, could free it */ MEM_freeN(vertCos); - vertCos = NULL; + vertCos = nullptr; } if (need_normal) { @@ -1025,7 +1026,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, /* Modifier returned a new derived mesh */ if (modified && modified != mesh_applied) { /* Modifier */ - BKE_id_free(NULL, modified); + BKE_id_free(nullptr, modified); } modified = mesh_applied; } @@ -1034,8 +1035,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (vertCos) { if (modified) { - Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE); - BKE_id_free(NULL, modified); + Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex( + nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE); + BKE_id_free(nullptr, modified); modified = temp_mesh; BKE_mesh_vert_coords_apply(modified, vertCos); @@ -1046,7 +1048,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, else { displist_vert_coords_apply(dispbase, vertCos); MEM_freeN(vertCos); - vertCos = NULL; + vertCos = nullptr; } } @@ -1081,18 +1083,18 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, MEM_SAFE_FREE(modified->mat); /* Set flag which makes it easier to see what's going on in a debugger. */ modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT; - modified->mat = MEM_dupallocN(cu->mat); + modified->mat = (Material **)MEM_dupallocN(cu->mat); modified->totcol = cu->totcol; (*r_final) = modified; } else { - (*r_final) = NULL; + (*r_final) = nullptr; } } - else if (modified != NULL) { + else if (modified != nullptr) { /* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */ - BKE_id_free(NULL, modified); + BKE_id_free(nullptr, modified); } } @@ -1103,8 +1105,8 @@ static void displist_surf_indices(DispList *dl) dl->totindex = 0; - index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1), - "index array nurbs"); + index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1), + "index array nurbs"); for (a = 0; a < dl->parts; a++) { @@ -1136,8 +1138,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, const bool for_render, const bool for_orco) { - ListBase nubase = {NULL, NULL}; - Curve *cu = ob->data; + ListBase nubase = {nullptr, nullptr}; + Curve *cu = (Curve *)ob->data; DispList *dl; float *data; int len; @@ -1174,8 +1176,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, if (nu->pntsv == 1) { len = SEGMENTSU(nu) * resolu; - dl = MEM_callocN(sizeof(DispList), "makeDispListsurf"); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf"); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(dispbase, dl); dl->parts = 1; @@ -1195,13 +1197,13 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, dl->type = DL_SEGM; } - BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3])); + BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3])); } else { len = (nu->pntsu * resolu) * (nu->pntsv * resolv); - dl = MEM_callocN(sizeof(DispList), "makeDispListsurf"); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf"); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(dispbase, dl); dl->col = nu->mat_nr; @@ -1258,7 +1260,7 @@ static void rotateBevelPiece(const Curve *cu, vec[1] = fp[2]; vec[2] = 0.0; - if (nbevp == NULL) { + if (nbevp == nullptr) { copy_v3_v3(data, bevp->vec); copy_qt_qt(quat, bevp->quat); } @@ -1276,7 +1278,7 @@ static void rotateBevelPiece(const Curve *cu, else { float sina, cosa; - if (nbevp == NULL) { + if (nbevp == nullptr) { copy_v3_v3(data, bevp->vec); sina = bevp->sina; cosa = bevp->cosa; @@ -1307,8 +1309,8 @@ static void fillBevelCap(const Nurb *nu, { DispList *dl; - dl = MEM_callocN(sizeof(DispList), "makeDispListbev2"); - dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts"); + dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2"); + dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts"); memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr); dl->type = DL_POLY; @@ -1469,7 +1471,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, const bool for_orco, Mesh **r_final) { - Curve *cu = ob->data; + Curve *cu = (Curve *)ob->data; /* we do allow duplis... this is only displist on curve level */ if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) { @@ -1481,7 +1483,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, } else if (ELEM(ob->type, OB_CURVE, OB_FONT)) { ListBase dlbev; - ListBase nubase = {NULL, NULL}; + ListBase nubase = {nullptr, nullptr}; bool force_mesh_conversion = false; BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); @@ -1494,7 +1496,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, if (ob->runtime.curve_cache->anim_path_accum_length) { MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); } - ob->runtime.curve_cache->anim_path_accum_length = NULL; + ob->runtime.curve_cache->anim_path_accum_length = nullptr; } if (ob->type == OB_FONT) { @@ -1520,8 +1522,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, } else { const float widfac = cu->width - 1.0f; - BevList *bl = ob->runtime.curve_cache->bev.first; - Nurb *nu = nubase.first; + BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first; + Nurb *nu = (Nurb *)nubase.first; for (; bl && nu; bl = bl->next, nu = nu->next) { float *data; @@ -1532,8 +1534,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, /* exception handling; curve without bevel or extrude, with width correction */ if (BLI_listbase_is_empty(&dlbev)) { - DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev"); - dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev"); + dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts"); BLI_addtail(dispbase, dl); if (bl->poly != -1) { @@ -1565,8 +1567,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, } } else { - ListBase bottom_capbase = {NULL, NULL}; - ListBase top_capbase = {NULL, NULL}; + ListBase bottom_capbase = {nullptr, nullptr}; + ListBase top_capbase = {nullptr, nullptr}; float bottom_no[3] = {0.0f}; float top_no[3] = {0.0f}; float first_blend = 0.0f, last_blend = 0.0f; @@ -1585,8 +1587,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, LISTBASE_FOREACH (DispList *, dlb, &dlbev) { /* for each part of the bevel use a separate displblock */ - DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1"); - dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1"); + dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts"); BLI_addtail(dispbase, dl); dl->type = DL_SURF; @@ -1616,7 +1618,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, float radius_factor = 1.0; float *cur_data = data; - if (cu->taperobj == NULL) { + if (cu->taperobj == nullptr) { radius_factor = bevp->radius; } else { @@ -1666,7 +1668,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data); } else { - rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data); + rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data); } if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) { @@ -1737,15 +1739,16 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, BKE_object_free_derived_caches(ob); if (!ob->runtime.curve_cache) { - ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for curve types"); } dispbase = &(ob->runtime.curve_cache->disp); - Mesh *mesh_eval = NULL; + Mesh *mesh_eval = nullptr; do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval); - if (mesh_eval != NULL) { + if (mesh_eval != nullptr) { BKE_object_eval_assign_data(ob, &mesh_eval->id, true); } @@ -1759,8 +1762,9 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph, Mesh **r_final, const bool for_orco) { - if (ob->runtime.curve_cache == NULL) { - ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); + if (ob->runtime.curve_cache == nullptr) { + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for Curve"); } do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final); @@ -1797,8 +1801,8 @@ static void boundbox_displist_object(Object *ob) */ /* object's BB is calculated from final displist */ - if (ob->runtime.bb == NULL) { - ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox"); + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox"); } Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc new file mode 100644 index 00000000000..0490d577b88 --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -0,0 +1,299 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" + +#include "attribute_access_intern.hh" + +/* -------------------------------------------------------------------- */ +/** \name Geometry Component Implementation + * \{ */ + +CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) +{ +} + +CurveComponent::~CurveComponent() +{ + this->clear(); +} + +GeometryComponent *CurveComponent::copy() const +{ + CurveComponent *new_component = new CurveComponent(); + if (curve_ != nullptr) { + new_component->curve_ = curve_->copy(); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; +} + +void CurveComponent::clear() +{ + BLI_assert(this->is_mutable()); + if (curve_ != nullptr) { + if (ownership_ == GeometryOwnershipType::Owned) { + delete curve_; + } + curve_ = nullptr; + } +} + +bool CurveComponent::has_curve() const +{ + return curve_ != nullptr; +} + +/* Clear the component and replace it with the new curve. */ +void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership) +{ + BLI_assert(this->is_mutable()); + this->clear(); + curve_ = curve; + ownership_ = ownership; +} + +CurveEval *CurveComponent::release() +{ + BLI_assert(this->is_mutable()); + CurveEval *curve = curve_; + curve_ = nullptr; + return curve; +} + +const CurveEval *CurveComponent::get_for_read() const +{ + return curve_; +} + +CurveEval *CurveComponent::get_for_write() +{ + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + curve_ = curve_->copy(); + ownership_ = GeometryOwnershipType::Owned; + } + return curve_; +} + +bool CurveComponent::is_empty() const +{ + return curve_ == nullptr; +} + +bool CurveComponent::owns_direct_data() const +{ + return ownership_ == GeometryOwnershipType::Owned; +} + +void CurveComponent::ensure_owns_direct_data() +{ + BLI_assert(this->is_mutable()); + if (ownership_ != GeometryOwnershipType::Owned) { + curve_ = curve_->copy(); + ownership_ = GeometryOwnershipType::Owned; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attribute Access + * \{ */ + +int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +{ + if (curve_ == nullptr) { + return 0; + } + if (domain == ATTR_DOMAIN_POINT) { + int total = 0; + for (const SplinePtr &spline : curve_->splines) { + total += spline->size(); + } + return total; + } + if (domain == ATTR_DOMAIN_CURVE) { + return curve_->splines.size(); + } + return 0; +} + +namespace blender::bke { + +class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { + using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data); + using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data); + using UpdateOnWrite = void (*)(Spline &spline); + const AsReadAttribute as_read_attribute_; + const AsWriteAttribute as_write_attribute_; + + public: + BuiltinSplineAttributeProvider(std::string attribute_name, + const CustomDataType attribute_type, + const WritableEnum writable, + const AsReadAttribute as_read_attribute, + const AsWriteAttribute as_write_attribute) + : BuiltinAttributeProvider(std::move(attribute_name), + ATTR_DOMAIN_CURVE, + attribute_type, + BuiltinAttributeProvider::NonCreatable, + writable, + BuiltinAttributeProvider::NonDeletable), + as_read_attribute_(as_read_attribute), + as_write_attribute_(as_write_attribute) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final + { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve == nullptr) { + return {}; + } + + return as_read_attribute_(*curve); + } + + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + { + if (writable_ != Writable) { + return {}; + } + CurveComponent &curve_component = static_cast<CurveComponent &>(component); + CurveEval *curve = curve_component.get_for_write(); + if (curve == nullptr) { + return {}; + } + + return as_write_attribute_(*curve); + } + + bool try_delete(GeometryComponent &UNUSED(component)) const final + { + return false; + } + + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final + { + return false; + } + + bool exists(const GeometryComponent &component) const final + { + return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0; + } +}; + +static int get_spline_resolution(const SplinePtr &spline) +{ + if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) { + return bezier_spline->resolution(); + } + if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) { + return nurb_spline->resolution(); + } + return 1; +} + +static void set_spline_resolution(SplinePtr &spline, const int resolution) +{ + if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) { + bezier_spline->set_resolution(std::max(resolution, 1)); + } + if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) { + nurb_spline->set_resolution(std::max(resolution, 1)); + } +} + +static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve) +{ + return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>( + curve.splines.as_span()); +} + +static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve) +{ + return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr, + int, + get_spline_resolution, + set_spline_resolution>>( + curve.splines.as_mutable_span()); +} + +static bool get_cyclic_value(const SplinePtr &spline) +{ + return spline->is_cyclic(); +} + +static void set_cyclic_value(SplinePtr &spline, const bool value) +{ + if (spline->is_cyclic() != value) { + spline->set_cyclic(value); + spline->mark_cache_invalid(); + } +} + +static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve) +{ + return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>( + curve.splines.as_span()); +} + +static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) +{ + return std::make_unique< + fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>( + curve.splines.as_mutable_span()); +} + +/** + * In this function all the attribute providers for a curve component are created. Most data + * in this function is statically allocated, because it does not change over time. + */ +static ComponentAttributeProviders create_attribute_providers_for_curve() +{ + static BuiltinSplineAttributeProvider resolution("resolution", + CD_PROP_INT32, + BuiltinAttributeProvider::Writable, + make_resolution_read_attribute, + make_resolution_write_attribute); + + static BuiltinSplineAttributeProvider cyclic("cyclic", + CD_PROP_BOOL, + BuiltinAttributeProvider::Writable, + make_cyclic_read_attribute, + make_cyclic_write_attribute); + + return ComponentAttributeProviders({&resolution, &cyclic}, {}); +} + +} // namespace blender::bke + +const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +{ + static blender::bke::ComponentAttributeProviders providers = + blender::bke::create_attribute_providers_for_curve(); + return &providers; +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index feb30e8575a..55025ed5ac9 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -42,72 +42,86 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_ GeometryComponent *InstancesComponent::copy() const { InstancesComponent *new_component = new InstancesComponent(); - new_component->transforms_ = transforms_; - new_component->instanced_data_ = instanced_data_; - new_component->ids_ = ids_; + new_component->instance_reference_handles_ = instance_reference_handles_; + new_component->instance_transforms_ = instance_transforms_; + new_component->instance_ids_ = instance_ids_; + new_component->references_ = references_; return new_component; } -void InstancesComponent::clear() +void InstancesComponent::reserve(int min_capacity) { - instanced_data_.clear(); - transforms_.clear(); - ids_.clear(); + instance_reference_handles_.reserve(min_capacity); + instance_transforms_.reserve(min_capacity); + instance_ids_.reserve(min_capacity); } -void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id) +void InstancesComponent::clear() { - InstancedData data; - data.type = INSTANCE_DATA_TYPE_OBJECT; - data.data.object = object; - this->add_instance(data, transform, id); + instance_reference_handles_.clear(); + instance_transforms_.clear(); + instance_ids_.clear(); + + references_.clear(); } -void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id) +void InstancesComponent::add_instance(const int instance_handle, + const float4x4 &transform, + const int id) { - InstancedData data; - data.type = INSTANCE_DATA_TYPE_COLLECTION; - data.data.collection = collection; - this->add_instance(data, transform, id); + BLI_assert(instance_handle >= 0); + BLI_assert(instance_handle < references_.size()); + instance_reference_handles_.append(instance_handle); + instance_transforms_.append(transform); + instance_ids_.append(id); } -void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id) +blender::Span<int> InstancesComponent::instance_reference_handles() const { - instanced_data_.append(data); - transforms_.append(transform); - ids_.append(id); + return instance_reference_handles_; } -Span<InstancedData> InstancesComponent::instanced_data() const +blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms() +{ + return instance_transforms_; +} +blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const { - return instanced_data_; + return instance_transforms_; } -Span<float4x4> InstancesComponent::transforms() const +blender::MutableSpan<int> InstancesComponent::instance_ids() +{ + return instance_ids_; +} +blender::Span<int> InstancesComponent::instance_ids() const { - return transforms_; + return instance_ids_; } -Span<int> InstancesComponent::ids() const +/** + * Returns a handle for the given reference. + * If the reference exists already, the handle of the existing reference is returned. + * Otherwise a new handle is added. + */ +int InstancesComponent::add_reference(InstanceReference reference) { - return ids_; + return references_.index_of_or_add_as(reference); } -MutableSpan<float4x4> InstancesComponent::transforms() +blender::Span<InstanceReference> InstancesComponent::references() const { - return transforms_; + return references_; } int InstancesComponent::instances_amount() const { - const int size = instanced_data_.size(); - BLI_assert(transforms_.size() == size); - return size; + return instance_transforms_.size(); } bool InstancesComponent::is_empty() const { - return transforms_.size() == 0; + return this->instance_reference_handles_.size() == 0; } bool InstancesComponent::owns_direct_data() const @@ -178,8 +192,8 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) blender::Span<int> InstancesComponent::almost_unique_ids() const { std::lock_guard lock(almost_unique_ids_mutex_); - if (almost_unique_ids_.size() != ids_.size()) { - almost_unique_ids_ = generate_unique_instance_ids(ids_); + if (almost_unique_ids_.size() != instance_ids_.size()) { + almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); } return almost_unique_ids_; } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 5697fb2ccde..2ecd0e6bd85 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -32,7 +32,7 @@ /* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */ extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id); -using blender::bke::ReadAttributePtr; +using blender::fn::GVArray; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -201,14 +201,14 @@ namespace blender::bke { template<typename T> static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, - const TypedReadAttribute<T> &attribute, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); attribute_math::DefaultMixer<T> mixer(r_values); for (const int loop_index : IndexRange(mesh.totloop)) { - const T value = attribute[loop_index]; + const T value = old_values[loop_index]; const MLoop &loop = mesh.mloop[loop_index]; const int point_index = loop.v; mixer.mix_in(point_index, value); @@ -216,43 +216,40 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { /* We compute all interpolated values at once, because for this interpolation, one has to * iterate over all loops anyway. */ Array<T> values(mesh.totvert); - adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, - const TypedReadAttribute<T> &attribute, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); for (const int loop_index : IndexRange(mesh.totloop)) { const int vertex_index = mesh.mloop[loop_index].v; - r_values[loop_index] = attribute[vertex_index]; + r_values[loop_index] = old_values[vertex_index]; } } -static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); /* It is not strictly necessary to compute the value for all corners here. Instead one could @@ -260,11 +257,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, * when an algorithm only accesses very few of the corner values. However, for the algorithms * we currently have, precomputing the array is fine. Also, it is easier to implement. */ Array<T> values(mesh.totloop); - adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER, - std::move(values)); + adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); }); - return new_attribute; + return new_varray; } /** @@ -274,7 +270,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -291,26 +287,24 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -332,26 +326,24 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); @@ -370,26 +362,24 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totvert); - adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); @@ -401,26 +391,24 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, } } -static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totloop); - adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -437,21 +425,19 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -461,7 +447,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -478,21 +464,19 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -502,7 +486,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -517,26 +501,24 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); @@ -558,26 +540,24 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totloop); - adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); @@ -593,21 +573,19 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totvert); - adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -617,7 +595,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -634,87 +612,86 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } } // namespace blender::bke -ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute, - const AttributeDomain new_domain) const +blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain( + blender::fn::GVArrayPtr varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { - if (!attribute) { + if (!varray) { return {}; } - if (attribute->size() == 0) { + if (varray->size() == 0) { return {}; } - const AttributeDomain old_domain = attribute->domain(); - if (old_domain == new_domain) { - return attribute; + if (from_domain == to_domain) { + return varray; } - switch (old_domain) { + switch (from_domain) { case ATTR_DOMAIN_CORNER: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_POINT: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_FACE: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_EDGE: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray)); default: break; } @@ -743,25 +720,21 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com namespace blender::bke { -template<typename StructT, - typename ElemT, - ElemT (*GetFunc)(const StructT &), - AttributeDomain Domain> -static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size) +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size) { - return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>( - Domain, Span<StructT>((const StructT *)data, domain_size)); + return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>( + Span<StructT>((const StructT *)data, domain_size)); } template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &), - void (*SetFunc)(StructT &, const ElemT &), - AttributeDomain Domain> -static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size) + void (*SetFunc)(StructT &, ElemT)> +static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size) { - return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>( - Domain, MutableSpan<StructT>((StructT *)data, domain_size)); + return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>( + MutableSpan<StructT>((StructT *)data, domain_size)); } static float3 get_vertex_position(const MVert &vert) @@ -769,7 +742,7 @@ static float3 get_vertex_position(const MVert &vert) return float3(vert.co); } -static void set_vertex_position(MVert &vert, const float3 &position) +static void set_vertex_position(MVert &vert, float3 position) { copy_v3_v3(vert.co, position); } @@ -787,7 +760,7 @@ static int get_material_index(const MPoly &mpoly) return static_cast<int>(mpoly.mat_nr); } -static void set_material_index(MPoly &mpoly, const int &index) +static void set_material_index(MPoly &mpoly, int index) { mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX)); } @@ -797,7 +770,7 @@ static bool get_shade_smooth(const MPoly &mpoly) return mpoly.flag & ME_SMOOTH; } -static void set_shade_smooth(MPoly &mpoly, const bool &value) +static void set_shade_smooth(MPoly &mpoly, bool value) { SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH); } @@ -807,7 +780,7 @@ static float2 get_loop_uv(const MLoopUV &uv) return float2(uv.uv); } -static void set_loop_uv(MLoopUV &uv, const float2 &co) +static void set_loop_uv(MLoopUV &uv, float2 co) { copy_v2_v2(uv.uv, co); } @@ -821,7 +794,7 @@ static Color4f get_loop_color(const MLoopCol &col) return linear_color; } -static void set_loop_color(MLoopCol &col, const Color4f &linear_color) +static void set_loop_color(MLoopCol &col, Color4f linear_color) { linearrgb_to_srgb_uchar4(&col.r, linear_color); } @@ -831,71 +804,62 @@ static float get_crease(const MEdge &edge) return edge.crease / 255.0f; } -static void set_crease(MEdge &edge, const float &value) +static void set_crease(MEdge &edge, float value) { edge.crease = round_fl_to_uchar_clamp(value * 255.0f); } -class VertexWeightWriteAttribute final : public WriteAttribute { +class VMutableArray_For_VertexWeights final : public VMutableArray<float> { private: MDeformVert *dverts_; const int dvert_index_; public: - VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index) - : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), - dverts_(dverts), - dvert_index_(dvert_index) + VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index) + : VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index) { } - void get_internal(const int64_t index, void *r_value) const override + float get_impl(const int64_t index) const override { - get_internal(dverts_, dvert_index_, index, r_value); + return get_internal(dverts_, dvert_index_, index); } - void set_internal(const int64_t index, const void *value) override + void set_impl(const int64_t index, const float value) override { MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_); - weight->weight = *reinterpret_cast<const float *>(value); + weight->weight = value; } - static void get_internal(const MDeformVert *dverts, - const int dvert_index, - const int64_t index, - void *r_value) + static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index) { if (dverts == nullptr) { - *(float *)r_value = 0.0f; - return; + return 0.0f; } const MDeformVert &dvert = dverts[index]; for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) { if (weight.def_nr == dvert_index) { - *(float *)r_value = weight.weight; - return; + return weight.weight; } } - *(float *)r_value = 0.0f; + return 0.0f; } }; -class VertexWeightReadAttribute final : public ReadAttribute { +class VArray_For_VertexWeights final : public VArray<float> { private: const MDeformVert *dverts_; const int dvert_index_; public: - VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index) - : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), - dverts_(dverts), - dvert_index_(dvert_index) + VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index) + : VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index) { } - void get_internal(const int64_t index, void *r_value) const override + float get_impl(const int64_t index) const override { - VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value); + return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index); } }; @@ -904,8 +868,8 @@ class VertexWeightReadAttribute final : public ReadAttribute { */ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); @@ -917,15 +881,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } if (mesh == nullptr || mesh->dvert == nullptr) { static const float default_value = 0.0f; - return std::make_unique<ConstantReadAttribute>( - ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value); + return {std::make_unique<fn::GVArray_For_SingleValueRef>( + CPPType::get<float>(), mesh->totvert, &default_value), + ATTR_DOMAIN_POINT}; } - return std::make_unique<VertexWeightReadAttribute>( - mesh->dvert, mesh->totvert, vertex_group_index); + return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>( + mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index), + ATTR_DOMAIN_POINT}; } - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); MeshComponent &mesh_component = static_cast<MeshComponent &>(component); @@ -946,8 +912,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer( &mesh->vdata, CD_MDEFORMVERT, mesh->totvert); } - return std::make_unique<blender::bke::VertexWeightWriteAttribute>( - mesh->dvert, mesh->totvert, vertex_group_index); + return { + std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>( + mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index), + ATTR_DOMAIN_POINT}; } bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final @@ -1009,7 +978,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final { const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); const Mesh *mesh = mesh_component.get_for_read(); @@ -1022,8 +991,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { CustomData_has_layer(&mesh->pdata, CD_NORMAL)) { const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL); - return std::make_unique<ArrayReadAttribute<float3>>( - ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly)); + return std::make_unique<fn::GVArray_For_Span<float3>>( + Span<float3>((const float3 *)data, mesh->totpoly)); } Array<float3> normals(mesh->totpoly); @@ -1032,10 +1001,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]); } - return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals)); + return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals)); } - WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final + GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final { return {}; } @@ -1045,7 +1014,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { return false; } - bool try_create(GeometryComponent &UNUSED(component)) const final + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final { return false; } @@ -1105,12 +1075,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, point_access, - make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>, - make_derived_write_attribute<MVert, - float3, - get_vertex_position, - set_vertex_position, - ATTR_DOMAIN_POINT>, + make_derived_read_attribute<MVert, float3, get_vertex_position>, + make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>, tag_normals_dirty_when_writing_position); static NormalAttributeProvider normal; @@ -1124,12 +1090,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, face_access, - make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>, - make_derived_write_attribute<MPoly, - int, - get_material_index, - set_material_index, - ATTR_DOMAIN_FACE>, + make_derived_read_attribute<MPoly, int, get_material_index>, + make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>, nullptr); static BuiltinCustomDataLayerProvider shade_smooth( @@ -1141,12 +1103,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, face_access, - make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>, - make_derived_write_attribute<MPoly, - bool, - get_shade_smooth, - set_shade_smooth, - ATTR_DOMAIN_FACE>, + make_derived_read_attribute<MPoly, bool, get_shade_smooth>, + make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>, nullptr); static BuiltinCustomDataLayerProvider crease( @@ -1158,8 +1116,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, edge_access, - make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>, - make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>, + make_derived_read_attribute<MEdge, float, get_crease>, + make_derived_write_attribute<MEdge, float, get_crease, set_crease>, nullptr); static NamedLegacyCustomDataProvider uvs( @@ -1167,20 +1125,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_FLOAT2, CD_MLOOPUV, corner_access, - make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>, - make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>); + make_derived_read_attribute<MLoopUV, float2, get_loop_uv>, + make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>); static NamedLegacyCustomDataProvider vertex_colors( ATTR_DOMAIN_CORNER, CD_PROP_COLOR, CD_MLOOPCOL, corner_access, - make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>, - make_derived_write_attribute<MLoopCol, - Color4f, - get_loop_color, - set_loop_color, - ATTR_DOMAIN_CORNER>); + make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>, + make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>); static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 135de14b4f7..6c4af7a6d23 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -140,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con namespace blender::bke { -template<typename T, AttributeDomain Domain> -static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size) +template<typename T> +static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size) { - return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size)); + return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size)); } -template<typename T, AttributeDomain Domain> -static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size) +template<typename T> +static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size) { - return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size)); + return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>( + MutableSpan<T>((T *)data, domain_size)); } /** @@ -179,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() }, update_custom_data_pointers}; - static BuiltinCustomDataLayerProvider position( - "position", - ATTR_DOMAIN_POINT, - CD_PROP_FLOAT3, - CD_PROP_FLOAT3, - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::NonDeletable, - point_access, - make_array_read_attribute<float3, ATTR_DOMAIN_POINT>, - make_array_write_attribute<float3, ATTR_DOMAIN_POINT>, - nullptr); - static BuiltinCustomDataLayerProvider radius( - "radius", - ATTR_DOMAIN_POINT, - CD_PROP_FLOAT, - CD_PROP_FLOAT, - BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::Deletable, - point_access, - make_array_read_attribute<float, ATTR_DOMAIN_POINT>, - make_array_write_attribute<float, ATTR_DOMAIN_POINT>, - nullptr); + static BuiltinCustomDataLayerProvider position("position", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonDeletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + nullptr); + static BuiltinCustomDataLayerProvider radius("radius", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + nullptr); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); return ComponentAttributeProviders({&position, &radius}, {&point_custom_data}); } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 3e457e48076..3d85118deee 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -24,6 +24,7 @@ #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_pointcloud.h" +#include "BKE_spline.hh" #include "BKE_volume.h" #include "DNA_collection_types.h" @@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return new InstancesComponent(); case GEO_COMPONENT_TYPE_VOLUME: return new VolumeComponent(); + case GEO_COMPONENT_TYPE_CURVE: + return new CurveComponent(); } BLI_assert_unreachable(); return nullptr; @@ -182,6 +185,11 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma if (volume != nullptr) { BKE_volume_min_max(volume, *r_min, *r_max); } + const CurveEval *curve = this->get_curve_for_read(); + if (curve != nullptr) { + /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */ + curve->bounds_min_max(*r_min, *r_max, true); + } } std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set) @@ -252,6 +260,13 @@ const Volume *GeometrySet::get_volume_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } +/* Returns a read-only curve or null. */ +const CurveEval *GeometrySet::get_curve_for_read() const +{ + const CurveComponent *component = this->get_component_for_read<CurveComponent>(); + return (component == nullptr) ? nullptr : component->get_for_read(); +} + /* Returns true when the geometry set has a point cloud component that has a point cloud. */ bool GeometrySet::has_pointcloud() const { @@ -273,6 +288,13 @@ bool GeometrySet::has_volume() const return component != nullptr && component->has_volume(); } +/* Returns true when the geometry set has a curve component that has a curve. */ +bool GeometrySet::has_curve() const +{ + const CurveComponent *component = this->get_component_for_read<CurveComponent>(); + return component != nullptr && component->has_curve(); +} + /* Create a new geometry set that only contains the given mesh. */ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) { @@ -292,6 +314,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, return geometry_set; } +/* Create a new geometry set that only contains the given curve. */ +GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership) +{ + GeometrySet geometry_set; + CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); + component.replace(curve, ownership); + return geometry_set; +} + /* Clear the existing mesh and replace it with the given one. */ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) { @@ -299,6 +330,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) component.replace(mesh, ownership); } +/* Clear the existing curve and replace it with the given one. */ +void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership) +{ + CurveComponent &component = this->get_component_for_write<CurveComponent>(); + component.replace(curve, ownership); +} + /* Clear the existing point cloud and replace with the given one. */ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { @@ -306,6 +344,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy pointcloud_component.replace(pointcloud, ownership); } +/* Clear the existing volume and replace with the given one. */ +void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership) +{ + VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>(); + volume_component.replace(volume, ownership); +} + /* Returns a mutable mesh or null. No ownership is transferred. */ Mesh *GeometrySet::get_mesh_for_write() { @@ -327,6 +372,13 @@ Volume *GeometrySet::get_volume_for_write() return component.get_for_write(); } +/* Returns a mutable curve or null. No ownership is transferred. */ +CurveEval *GeometrySet::get_curve_for_write() +{ + CurveComponent &component = this->get_component_for_write<CurveComponent>(); + return component.get_for_write(); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 07d0e520c93..a792e268d5c 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -19,6 +19,7 @@ #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_pointcloud.h" +#include "BKE_spline.hh" #include "DNA_collection_types.h" #include "DNA_mesh_types.h" @@ -50,6 +51,16 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS } } +static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set) +{ + BLI_assert(object.type == OB_CURVE); + if (object.data != nullptr) { + std::unique_ptr<CurveEval> curve = curve_eval_from_dna_curve(*(Curve *)object.data); + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + curve_component.replace(curve.release(), GeometryOwnershipType::Owned); + } +} + /** * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. */ @@ -73,6 +84,9 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object) if (object.type == OB_MESH) { add_final_mesh_as_geometry_component(object, geometry_set); } + else if (object.type == OB_CURVE) { + add_curve_data_as_geometry_component(object, geometry_set); + } /* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the * #geometry_set_eval case above. */ @@ -135,21 +149,28 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set, const InstancesComponent &instances_component = *geometry_set.get_component_for_read<InstancesComponent>(); - Span<float4x4> transforms = instances_component.transforms(); - Span<InstancedData> instances = instances_component.instanced_data(); - for (const int i : instances.index_range()) { - const InstancedData &data = instances[i]; + Span<float4x4> transforms = instances_component.instance_transforms(); + Span<int> handles = instances_component.instance_reference_handles(); + Span<InstanceReference> references = instances_component.references(); + for (const int i : transforms.index_range()) { + const InstanceReference &reference = references[handles[i]]; const float4x4 instance_transform = transform * transforms[i]; - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - BLI_assert(data.data.object != nullptr); - const Object &object = *data.data.object; - geometry_set_collect_recursive_object(object, instance_transform, r_sets); - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - BLI_assert(data.data.collection != nullptr); - const Collection &collection = *data.data.collection; - geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets); + switch (reference.type()) { + case InstanceReference::Type::Object: { + Object &object = reference.object(); + geometry_set_collect_recursive_object(object, instance_transform, r_sets); + break; + } + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); + geometry_set_collect_recursive_collection_instance( + collection, instance_transform, r_sets); + break; + } + case InstanceReference::Type::None: { + break; + } } } } @@ -253,19 +274,24 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se return true; } - for (const InstancedData &data : instances_component->instanced_data()) { - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - BLI_assert(data.data.object != nullptr); - const Object &object = *data.data.object; - if (!object_instance_attribute_foreach(object, callback, limit, count)) { - return false; + for (const InstanceReference &reference : instances_component->references()) { + switch (reference.type()) { + case InstanceReference::Type::Object: { + const Object &object = reference.object(); + if (!object_instance_attribute_foreach(object, callback, limit, count)) { + return false; + } + break; } - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - BLI_assert(data.data.collection != nullptr); - const Collection &collection = *data.data.collection; - if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { - return false; + case InstanceReference::Type::Collection: { + const Collection &collection = reference.collection(); + if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { + return false; + } + break; + } + case InstanceReference::Type::None: { + break; } } } @@ -449,13 +475,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); BLI_assert(cpp_type != nullptr); - result.attribute_try_create(entry.key, domain_output, data_type_output); - WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); - if (!write_attribute || &write_attribute->cpp_type() != cpp_type || - write_attribute->domain() != domain_output) { + result.attribute_try_create( + entry.key, domain_output, data_type_output, AttributeInitDefault()); + WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name); + if (!write_attribute || &write_attribute.varray->type() != cpp_type || + write_attribute.domain != domain_output) { continue; } - fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); + + fn::GVMutableArray_GSpan dst_span{*write_attribute.varray}; int offset = 0; for (const GeometryInstanceGroup &set_group : set_groups) { @@ -467,11 +495,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, if (domain_size == 0) { continue; /* Domain size is 0, so no need to increment the offset. */ } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + GVArrayPtr source_attribute = component.attribute_try_get_for_read( name, domain_output, data_type_output); if (source_attribute) { - fn::GSpan src_span = source_attribute->get_span(); + fn::GVArray_GSpan src_span{*source_attribute}; const void *src_buffer = src_span.data(); for (const int UNUSED(i) : set_group.transforms.index_range()) { void *dst_buffer = dst_span[offset]; @@ -486,10 +514,32 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, } } - write_attribute->apply_span(); + dst_span.save(); } } +static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result) +{ + CurveEval *new_curve = new CurveEval(); + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (!set.has_curve()) { + continue; + } + + const CurveEval &source_curve = *set.get_curve_for_read(); + for (const SplinePtr &source_spline : source_curve.splines) { + for (const float4x4 &transform : set_group.transforms) { + SplinePtr new_spline = source_spline->copy(); + new_spline->transform(transform); + new_curve->splines.append(std::move(new_spline)); + } + } + } + + result.replace(new_curve); +} + static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, bool convert_points_to_vertices, GeometrySet &result) @@ -556,6 +606,12 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, UNUSED_VARS(set_groups, dst_component); } +static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) +{ + CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); + join_curve_splines(set_groups, dst_component); +} + GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) { if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) { @@ -587,6 +643,7 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) join_instance_groups_mesh(set_groups, false, new_geometry_set); join_instance_groups_pointcloud(set_groups, new_geometry_set); join_instance_groups_volume(set_groups, new_geometry_set); + join_instance_groups_curve(set_groups, new_geometry_set); return new_geometry_set; } diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index fb6500cfe1b..04403e264a4 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -1050,7 +1050,7 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, normalize_v3(locx); normalize_v3(locy); - /* Calculcate last point first. */ + /* Calculate last point first. */ const bGPDspoint *pt_last = &points[totpoints - 1]; float tmp[3]; sub_v3_v3v3(tmp, &pt_last->x, &pt0->x); diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index cbbe07f99d8..3281783d81a 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -56,7 +56,7 @@ typedef struct LibraryForeachIDData { */ ID *self_id; - /** Flags controlling the bahaviour of the 'foreach id' looping code. */ + /** Flags controlling the behavior of the 'foreach id' looping code. */ int flag; /** Generic flags to be passed to all callback calls for current processed data. */ int cb_flag; diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 729c38ae7cf..098d9c420aa 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -1027,11 +1027,15 @@ static Object *object_for_curve_to_mesh_create(Object *object) * * Note that there are extra fields in there like bevel and path, but those are not needed during * conversion, so they are not copied to save unnecessary allocations. */ - if (object->runtime.curve_cache != NULL) { + if (temp_object->runtime.curve_cache == NULL) { temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); + } + + if (object->runtime.curve_cache != NULL) { BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp); } + /* Constructive modifiers will use mesh to store result. */ if (object->runtime.data_eval != NULL) { BKE_id_copy_ex( @@ -1057,17 +1061,25 @@ static Object *object_for_curve_to_mesh_create(Object *object) return temp_object; } +/** + * Populate `object->runtime.curve_cache` which is then used to create the mesh. + */ static void curve_to_mesh_eval_ensure(Object *object) { - if (object->runtime.curve_cache == NULL) { - object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); - } Curve *curve = (Curve *)object->data; Curve remapped_curve = *curve; Object remapped_object = *object; - remapped_object.runtime.bb = NULL; + BKE_object_runtime_reset(&remapped_object); + remapped_object.data = &remapped_curve; + if (object->runtime.curve_cache == NULL) { + object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); + } + + /* Temporarily share the curve-cache with the temporary object, owned by `object`. */ + remapped_object.runtime.curve_cache = object->runtime.curve_cache; + /* Clear all modifiers for the bevel object. * * This is because they can not be reliably evaluated for an original object (at least because @@ -1078,8 +1090,8 @@ static void curve_to_mesh_eval_ensure(Object *object) Object bevel_object = {{NULL}}; if (remapped_curve.bevobj != NULL) { bevel_object = *remapped_curve.bevobj; - bevel_object.runtime.bb = NULL; BLI_listbase_clear(&bevel_object.modifiers); + BKE_object_runtime_reset(&bevel_object); remapped_curve.bevobj = &bevel_object; } @@ -1087,8 +1099,8 @@ static void curve_to_mesh_eval_ensure(Object *object) Object taper_object = {{NULL}}; if (remapped_curve.taperobj != NULL) { taper_object = *remapped_curve.taperobj; - taper_object.runtime.bb = NULL; BLI_listbase_clear(&taper_object.modifiers); + BKE_object_runtime_reset(&taper_object); remapped_curve.taperobj = &taper_object; } @@ -1110,12 +1122,12 @@ static void curve_to_mesh_eval_ensure(Object *object) BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true); } - MEM_SAFE_FREE(remapped_object.runtime.bb); - MEM_SAFE_FREE(taper_object.runtime.bb); - MEM_SAFE_FREE(bevel_object.runtime.bb); + /* Owned by `object` & needed by the caller to create the mesh. */ + remapped_object.runtime.curve_cache = NULL; - BKE_object_free_curve_cache(&bevel_object); - BKE_object_free_curve_cache(&taper_object); + BKE_object_runtime_free_data(&remapped_object); + BKE_object_runtime_free_data(&taper_object); + BKE_object_runtime_free_data(&taper_object); } static Mesh *mesh_new_from_curve_type_object(Object *object) @@ -1542,7 +1554,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, * check whether it is still true with Mesh */ Mesh tmp = *mesh_dst; int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; - int did_shapekeys = 0; + bool did_shapekeys = false; eCDAllocType alloctype = CD_DUPLICATE; if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) { @@ -1597,7 +1609,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, } shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid); - did_shapekeys = 1; + did_shapekeys = true; } /* copy texture space */ @@ -1626,13 +1638,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, totedge); } if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) { - /* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the - * BKE_mesh_update_customdata_pointers() call below. */ - tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop); - tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly); - - CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop); - CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly); + CustomData_add_layer(&tmp.ldata, + CD_MLOOP, + CD_ASSIGN, + (alloctype == CD_ASSIGN) ? mesh_src->mloop : + MEM_dupallocN(mesh_src->mloop), + tmp.totloop); + CustomData_add_layer(&tmp.pdata, + CD_MPOLY, + CD_ASSIGN, + (alloctype == CD_ASSIGN) ? mesh_src->mpoly : + MEM_dupallocN(mesh_src->mpoly), + tmp.totpoly); } /* object had got displacement layer, should copy this layer to save sculpted data */ @@ -1651,9 +1668,6 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* yes, must be before _and_ after tessellate */ BKE_mesh_update_customdata_pointers(&tmp, false); - /* since 2.65 caller must do! */ - // BKE_mesh_tessface_calc(&tmp); - CustomData_free(&mesh_dst->vdata, mesh_dst->totvert); CustomData_free(&mesh_dst->edata, mesh_dst->totedge); CustomData_free(&mesh_dst->fdata, mesh_dst->totface); diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc new file mode 100644 index 00000000000..91c9951ae89 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_attribute_math.hh" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +namespace blender::bke::mesh_surface_sample { + +static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) +{ + /* This only updates a cache and can be considered to be logically const. */ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); + const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); + return {looptris, looptris_len}; +} + +template<typename T> +BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + const Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + + const T v0 = data_in[v0_index]; + const T v1 = data_in[v1_index]; + const T v2 = data_in[v2_index]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +void sample_point_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_out.size() == bary_coords.size()); + BLI_assert(data_in.size() == mesh.totvert); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_point_attribute<T>( + mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +template<typename T> +BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int loop_index_0 = looptri.tri[0]; + const int loop_index_1 = looptri.tri[1]; + const int loop_index_2 = looptri.tri[2]; + + const T v0 = data_in[loop_index_0]; + const T v1 = data_in[loop_index_1]; + const T v2 = data_in[loop_index_2]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +void sample_corner_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_out.size() == bary_coords.size()); + BLI_assert(data_in.size() == mesh.totloop); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_corner_attribute<T>( + mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +template<typename T> +void sample_face_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : data_out.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const int poly_index = looptri.poly; + data_out[i] = data_in[poly_index]; + } +} + +void sample_face_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_in.size() == mesh.totpoly); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +} // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 9c2cd03dbc2..0f2f56f4f2b 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -950,7 +950,7 @@ static void movieclip_load_get_size(MovieClip *clip) int width, height; MovieClipUser user = {0}; - user.framenr = 1; + user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, 1); BKE_movieclip_get_size(clip, &user, &width, &height); if (width && height) { diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 97aa6b07ab0..92e21183acb 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -254,7 +254,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain, * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... * flags in BKE_lib_id.h */ -void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag) +void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag) { NlaTrack *nlt, *nlt_d; @@ -275,6 +275,54 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl } } +/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */ +static void update_active_strip(AnimData *adt_dest, + NlaTrack *track_dest, + const AnimData *adt_source, + NlaTrack *track_source) +{ + BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips)); + + NlaStrip *strip_dest = track_dest->strips.first; + LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) { + if (strip_source == adt_source->actstrip) { + adt_dest->actstrip = strip_dest; + } + + strip_dest = strip_dest->next; + } +} + +/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */ +static void update_active_track(AnimData *adt_dest, const AnimData *adt_source) +{ + BLI_assert(BLI_listbase_count(&adt_source->nla_tracks) == + BLI_listbase_count(&adt_dest->nla_tracks)); + + NlaTrack *track_dest = adt_dest->nla_tracks.first; + LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) { + if (track_source == adt_source->act_track) { + adt_dest->act_track = track_dest; + /* Assumption: the active strip is on the active track. */ + update_active_strip(adt_dest, track_dest, adt_source, track_source); + } + + track_dest = track_dest->next; + } +} + +void BKE_nla_tracks_copy_from_adt(Main *bmain, + AnimData *adt_dest, + const AnimData *adt_source, + const int flag) +{ + adt_dest->act_track = NULL; + adt_dest->actstrip = NULL; + + BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag); + update_active_track(adt_dest, adt_source); +} + /* Adding ------------------------------------------- */ /* Add a NLA Track to the given AnimData diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 02195e0d60f..6634c42a274 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4941,11 +4941,13 @@ static void registerGeometryNodes() register_node_type_geo_attribute_proximity(); register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_separate_xyz(); + register_node_type_geo_attribute_transfer(); register_node_type_geo_attribute_vector_math(); register_node_type_geo_attribute_remove(); register_node_type_geo_boolean(); register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); + register_node_type_geo_curve_to_mesh(); register_node_type_geo_edge_split(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); @@ -4968,6 +4970,7 @@ static void registerGeometryNodes() register_node_type_geo_sample_texture(); register_node_type_geo_subdivide(); register_node_type_geo_subdivision_surface(); + register_node_type_geo_switch(); register_node_type_geo_transform(); register_node_type_geo_triangulate(); register_node_type_geo_volume_to_mesh(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 160fb93b835..02be16d1d28 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) if (ob->type == OB_HAIR) { return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); } - if (ob->type == OB_POINTCLOUD) { + if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) { return (mti->modifyGeometrySet != NULL); } - if (ob->type == OB_VOLUME) { - return (mti->modifyVolume != NULL); - } if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) { return false; @@ -4346,7 +4343,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, } /* Speed optimization for animation lookups. */ if (ob->pose != NULL) { - BKE_pose_channels_hash_make(ob->pose); + BKE_pose_channels_hash_ensure(ob->pose); if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(ob->pose); } @@ -5117,6 +5114,20 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) } /** + * The function frees memory used by the runtime data, but not the runtime field itself. + * + * All runtime data is cleared to ensure it's not used again, + * in keeping with other `_free_data(..)` functions. + */ +void BKE_object_runtime_free_data(Object *object) +{ + /* Currently this is all that's needed. */ + BKE_object_free_derived_caches(object); + + BKE_object_runtime_reset(object); +} + +/** * Find an associated armature object. */ static Object *obrel_armature_find(Object *ob) diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 835b62c06bf..768fa9373c1 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -842,38 +842,38 @@ static void make_duplis_instances_component(const DupliContext *ctx) return; } - Span<float4x4> instance_offset_matrices = component->transforms(); + Span<float4x4> instance_offset_matrices = component->instance_transforms(); + Span<int> instance_reference_handles = component->instance_reference_handles(); Span<int> almost_unique_ids = component->almost_unique_ids(); - Span<InstancedData> instanced_data = component->instanced_data(); + Span<InstanceReference> references = component->references(); - for (int i = 0; i < component->instances_amount(); i++) { - const InstancedData &data = instanced_data[i]; + for (int64_t i : instance_offset_matrices.index_range()) { + const InstanceReference &reference = references[instance_reference_handles[i]]; const int id = almost_unique_ids[i]; - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - Object *object = data.data.object; - if (object != nullptr) { + switch (reference.type()) { + case InstanceReference::Type::Object: { + Object &object = reference.object(); float matrix[4][4]; mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values); - make_dupli(ctx, object, matrix, id); + make_dupli(ctx, &object, matrix, id); float space_matrix[4][4]; - mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object->imat); + mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat); mul_m4_m4_pre(space_matrix, ctx->object->obmat); - make_recursive_duplis(ctx, object, space_matrix, id); + make_recursive_duplis(ctx, &object, space_matrix, id); + break; } - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - Collection *collection = data.data.collection; - if (collection != nullptr) { + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); float collection_matrix[4][4]; unit_m4(collection_matrix); - sub_v3_v3(collection_matrix[3], collection->instance_offset); + sub_v3_v3(collection_matrix[3], collection.instance_offset); mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values); mul_m4_m4_pre(collection_matrix, ctx->object->obmat); eEvaluationMode mode = DEG_get_mode(ctx->depsgraph); - FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) { + FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) { if (object == ctx->object) { continue; } @@ -885,6 +885,10 @@ static void make_duplis_instances_component(const DupliContext *ctx) make_recursive_duplis(ctx, object, collection_matrix, id); } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; + break; + } + case InstanceReference::Type::None: { + break; } } } diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 63bc8753fc7..948b57578dc 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -187,31 +187,31 @@ int BB_widest_axis(const BB *bb); void pbvh_grow_nodes(PBVH *bvh, int totnode); bool ray_face_intersection_quad(const float ray_start[3], struct IsectRayPrecalc *isect_precalc, - const float *t0, - const float *t1, - const float *t2, - const float *t3, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], float *depth); bool ray_face_intersection_tri(const float ray_start[3], struct IsectRayPrecalc *isect_precalc, - const float *t0, - const float *t1, - const float *t2, + const float t0[3], + const float t1[3], + const float t2[3], float *depth); bool ray_face_nearest_quad(const float ray_start[3], const float ray_normal[3], - const float *t0, - const float *t1, - const float *t2, - const float *t3, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], float *r_depth, float *r_dist_sq); bool ray_face_nearest_tri(const float ray_start[3], const float ray_normal[3], - const float *t0, - const float *t1, - const float *t2, + const float t0[3], + const float t1[3], + const float t2[3], float *r_depth, float *r_dist_sq); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 17434ee8023..be206f8a642 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1805,7 +1805,7 @@ int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm, void *cur[BPH } for (i = 0; i < BPHYS_TOT_DATA; i++) { - cur[i] = data_types & (1 << i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL; + cur[i] = (data_types & (1 << i)) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL; } return 1; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index d52e4443ac1..3b1230ce3b6 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm) } MEM_freeN(ccdm->mima); MEM_freeN(ccdm); - ccdm = NULL; } } diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc new file mode 100644 index 00000000000..31c2178fa3f --- /dev/null +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -0,0 +1,274 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_span.hh" + +#include "FN_generic_virtual_array.hh" + +#include "BKE_spline.hh" + +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + +Spline::Type Spline::type() const +{ + return type_; +} + +void Spline::translate(const blender::float3 &translation) +{ + for (float3 &position : this->positions()) { + position += translation; + } + this->mark_cache_invalid(); +} + +void Spline::transform(const blender::float4x4 &matrix) +{ + for (float3 &position : this->positions()) { + position = matrix * position; + } + this->mark_cache_invalid(); +} + +int Spline::evaluated_edges_size() const +{ + const int eval_size = this->evaluated_points_size(); + if (eval_size == 1) { + return 0; + } + + return this->is_cyclic_ ? eval_size : eval_size - 1; +} + +float Spline::length() const +{ + return this->evaluated_lengths().last(); +} + +int Spline::segments_size() const +{ + const int points_len = this->size(); + + return is_cyclic_ ? points_len : points_len - 1; +} + +bool Spline::is_cyclic() const +{ + return is_cyclic_; +} + +void Spline::set_cyclic(const bool value) +{ + is_cyclic_ = value; +} + +static void accumulate_lengths(Span<float3> positions, + const bool is_cyclic, + MutableSpan<float> lengths) +{ + float length = 0.0f; + for (const int i : IndexRange(positions.size() - 1)) { + length += float3::distance(positions[i], positions[i + 1]); + lengths[i] = length; + } + if (is_cyclic) { + lengths.last() = length + float3::distance(positions.last(), positions.first()); + } +} + +/** + * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the + * length of the subsequent segment, i.e. the first value is the length of the first segment rather + * than 0. This calculation is rather trivial, and only depends on the evaluated positions. + * However, the results are used often, so it makes sense to cache it. + */ +Span<float> Spline::evaluated_lengths() const +{ + if (!length_cache_dirty_) { + return evaluated_lengths_cache_; + } + + std::lock_guard lock{length_cache_mutex_}; + if (!length_cache_dirty_) { + return evaluated_lengths_cache_; + } + + const int total = evaluated_edges_size(); + evaluated_lengths_cache_.resize(total); + + Span<float3> positions = this->evaluated_positions(); + accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_); + + length_cache_dirty_ = false; + return evaluated_lengths_cache_; +} + +static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next) +{ + const float3 dir_prev = (middle - prev).normalized(); + const float3 dir_next = (next - middle).normalized(); + + return (dir_prev + dir_next).normalized(); +} + +static void calculate_tangents(Span<float3> positions, + const bool is_cyclic, + MutableSpan<float3> tangents) +{ + if (positions.size() == 1) { + return; + } + + for (const int i : IndexRange(1, positions.size() - 2)) { + tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]); + } + + if (is_cyclic) { + const float3 &second_to_last = positions[positions.size() - 2]; + const float3 &last = positions.last(); + const float3 &first = positions.first(); + const float3 &second = positions[1]; + tangents.first() = direction_bisect(last, first, second); + tangents.last() = direction_bisect(second_to_last, last, first); + } + else { + tangents.first() = (positions[1] - positions[0]).normalized(); + tangents.last() = (positions.last() - positions[positions.size() - 2]).normalized(); + } +} + +/** + * Return non-owning access to the direction of the curve at each evaluated point. + */ +Span<float3> Spline::evaluated_tangents() const +{ + if (!tangent_cache_dirty_) { + return evaluated_tangents_cache_; + } + + std::lock_guard lock{tangent_cache_mutex_}; + if (!tangent_cache_dirty_) { + return evaluated_tangents_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_tangents_cache_.resize(eval_size); + + Span<float3> positions = this->evaluated_positions(); + + if (eval_size == 1) { + evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f); + } + else { + calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_); + this->correct_end_tangents(); + } + + tangent_cache_dirty_ = false; + return evaluated_tangents_cache_; +} + +static float3 rotate_direction_around_axis(const float3 &direction, + const float3 &axis, + const float angle) +{ + BLI_ASSERT_UNIT_V3(direction); + BLI_ASSERT_UNIT_V3(axis); + + const float3 axis_scaled = axis * float3::dot(direction, axis); + const float3 diff = direction - axis_scaled; + const float3 cross = float3::cross(axis, diff); + + return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); +} + +static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals) +{ + for (const int i : normals.index_range()) { + normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized(); + } +} + +/** + * Return non-owning access to the direction vectors perpendicular to the tangents at every + * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode. + */ +Span<float3> Spline::evaluated_normals() const +{ + if (!normal_cache_dirty_) { + return evaluated_normals_cache_; + } + + std::lock_guard lock{normal_cache_mutex_}; + if (!normal_cache_dirty_) { + return evaluated_normals_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_normals_cache_.resize(eval_size); + + Span<float3> tangents = evaluated_tangents(); + MutableSpan<float3> normals = evaluated_normals_cache_; + + /* Only Z up normals are supported at the moment. */ + calculate_normals_z_up(tangents, normals); + + /* Rotate the generated normals with the interpolated tilt data. */ + blender::fn::GVArray_Typed<float> tilts{ + this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(this->tilts()))}; + for (const int i : normals.index_range()) { + normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]); + } + + normal_cache_dirty_ = false; + return evaluated_normals_cache_; +} + +Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const +{ + return this->lookup_evaluated_length(this->length() * factor); +} + +/** + * \note This does not support extrapolation currently. + */ +Spline::LookupResult Spline::lookup_evaluated_length(const float length) const +{ + BLI_assert(length >= 0.0f && length <= this->length()); + + Span<float> lengths = this->evaluated_lengths(); + + const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); + const int index = offset - lengths.begin(); + const int next_index = (index == this->size() - 1) ? 0 : index + 1; + + const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; + const float factor = (length - previous_length) / (lengths[index] - previous_length); + + return LookupResult{index, next_index, factor}; +} + +void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const +{ + Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions(); + for (const float3 &position : positions) { + minmax_v3v3_v3(min, max, position); + } +} diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc new file mode 100644 index 00000000000..f62718011da --- /dev/null +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -0,0 +1,478 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +using blender::Array; +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + +SplinePtr BezierSpline::copy() const +{ + return std::make_unique<BezierSpline>(*this); +} + +int BezierSpline::size() const +{ + const int size = positions_.size(); + BLI_assert(size == handle_types_left_.size()); + BLI_assert(size == handle_positions_left_.size()); + BLI_assert(size == handle_types_right_.size()); + BLI_assert(size == handle_positions_right_.size()); + BLI_assert(size == radii_.size()); + BLI_assert(size == tilts_.size()); + return size; +} + +int BezierSpline::resolution() const +{ + return resolution_; +} + +void BezierSpline::set_resolution(const int value) +{ + BLI_assert(value > 0); + resolution_ = value; + this->mark_cache_invalid(); +} + +void BezierSpline::add_point(const float3 position, + const HandleType handle_type_start, + const float3 handle_position_start, + const HandleType handle_type_end, + const float3 handle_position_end, + const float radius, + const float tilt) +{ + handle_types_left_.append(handle_type_start); + handle_positions_left_.append(handle_position_start); + positions_.append(position); + handle_types_right_.append(handle_type_end); + handle_positions_right_.append(handle_position_end); + radii_.append(radius); + tilts_.append(tilt); + this->mark_cache_invalid(); +} + +MutableSpan<float3> BezierSpline::positions() +{ + return positions_; +} +Span<float3> BezierSpline::positions() const +{ + return positions_; +} +MutableSpan<float> BezierSpline::radii() +{ + return radii_; +} +Span<float> BezierSpline::radii() const +{ + return radii_; +} +MutableSpan<float> BezierSpline::tilts() +{ + return tilts_; +} +Span<float> BezierSpline::tilts() const +{ + return tilts_; +} +Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const +{ + return handle_types_left_; +} +MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left() +{ + return handle_types_left_; +} +Span<float3> BezierSpline::handle_positions_left() const +{ + return handle_positions_left_; +} +MutableSpan<float3> BezierSpline::handle_positions_left() +{ + return handle_positions_left_; +} +Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const +{ + return handle_types_right_; +} +MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right() +{ + return handle_types_right_; +} +Span<float3> BezierSpline::handle_positions_right() const +{ + return handle_positions_right_; +} +MutableSpan<float3> BezierSpline::handle_positions_right() +{ + return handle_positions_right_; +} + +void BezierSpline::translate(const blender::float3 &translation) +{ + for (float3 &position : this->positions()) { + position += translation; + } + for (float3 &handle_position : this->handle_positions_left()) { + handle_position += translation; + } + for (float3 &handle_position : this->handle_positions_right()) { + handle_position += translation; + } + this->mark_cache_invalid(); +} + +void BezierSpline::transform(const blender::float4x4 &matrix) +{ + for (float3 &position : this->positions()) { + position = matrix * position; + } + for (float3 &handle_position : this->handle_positions_left()) { + handle_position = matrix * handle_position; + } + for (float3 &handle_position : this->handle_positions_right()) { + handle_position = matrix * handle_position; + } + this->mark_cache_invalid(); +} + +bool BezierSpline::point_is_sharp(const int index) const +{ + return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) || + ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free); +} + +bool BezierSpline::segment_is_vector(const int index) const +{ + if (index == this->size() - 1) { + BLI_assert(is_cyclic_); + return handle_types_right_.last() == HandleType::Vector && + handle_types_left_.first() == HandleType::Vector; + } + return handle_types_right_[index] == HandleType::Vector && + handle_types_left_[index + 1] == HandleType::Vector; +} + +void BezierSpline::mark_cache_invalid() +{ + offset_cache_dirty_ = true; + position_cache_dirty_ = true; + mapping_cache_dirty_ = true; + tangent_cache_dirty_ = true; + normal_cache_dirty_ = true; + length_cache_dirty_ = true; +} + +int BezierSpline::evaluated_points_size() const +{ + const int points_len = this->size(); + BLI_assert(points_len > 0); + + const int last_offset = this->control_point_offsets().last(); + if (is_cyclic_ && points_len > 1) { + return last_offset + (this->segment_is_vector(points_len - 1) ? 0 : resolution_); + } + + return last_offset + 1; +} + +/** + * If the spline is not cyclic, the direction for the first and last points is just the + * direction formed by the corresponding handles and control points. In the unlikely situation + * that the handles define a zero direction, fallback to using the direction defined by the + * first and last evaluated segments already calculated in #Spline::evaluated_tangents(). + */ +void BezierSpline::correct_end_tangents() const +{ + if (is_cyclic_) { + return; + } + + MutableSpan<float3> tangents(evaluated_tangents_cache_); + + if (handle_positions_left_.first() != positions_.first()) { + tangents.first() = (positions_.first() - handle_positions_left_.first()).normalized(); + } + if (handle_positions_right_.last() != positions_.last()) { + tangents.last() = (handle_positions_right_.last() - positions_.last()).normalized(); + } +} + +static void bezier_forward_difference_3d(const float3 &point_0, + const float3 &point_1, + const float3 &point_2, + const float3 &point_3, + MutableSpan<float3> result) +{ + BLI_assert(result.size() > 0); + const float inv_len = 1.0f / static_cast<float>(result.size()); + const float inv_len_squared = inv_len * inv_len; + const float inv_len_cubed = inv_len_squared * inv_len; + + const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len; + const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared; + const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed; + + float3 q0 = point_0; + float3 q1 = rt1 + rt2 + rt3; + float3 q2 = 2.0f * rt2 + 6.0f * rt3; + float3 q3 = 6.0f * rt3; + for (const int i : result.index_range()) { + result[i] = q0; + q0 += q1; + q1 += q2; + q2 += q3; + } +} + +void BezierSpline::evaluate_bezier_segment(const int index, + const int next_index, + MutableSpan<float3> positions) const +{ + if (this->segment_is_vector(index)) { + BLI_assert(positions.size() == 1); + positions.first() = positions_[index]; + } + else { + bezier_forward_difference_3d(positions_[index], + handle_positions_right_[index], + handle_positions_left_[next_index], + positions_[next_index], + positions); + } +} + +/** + * Returns access to a cache of offsets into the evaluated point array for each control point. + * This is important because while most control point edges generate the number of edges specified + * by the resolution, vector segments only generate one edge. + */ +Span<int> BezierSpline::control_point_offsets() const +{ + if (!offset_cache_dirty_) { + return offset_cache_; + } + + std::lock_guard lock{offset_cache_mutex_}; + if (!offset_cache_dirty_) { + return offset_cache_; + } + + const int points_len = this->size(); + offset_cache_.resize(points_len); + + MutableSpan<int> offsets = offset_cache_; + + int offset = 0; + for (const int i : IndexRange(points_len - 1)) { + offsets[i] = offset; + offset += this->segment_is_vector(i) ? 1 : resolution_; + } + offsets.last() = offset; + + offset_cache_dirty_ = false; + return offsets; +} + +static void calculate_mappings_linear_resolution(Span<int> offsets, + const int size, + const int resolution, + const bool is_cyclic, + MutableSpan<float> r_mappings) +{ + const float first_segment_len_inv = 1.0f / offsets[1]; + for (const int i : IndexRange(0, offsets[1])) { + r_mappings[i] = i * first_segment_len_inv; + } + + const int grain_size = std::max(2048 / resolution, 1); + blender::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) { + for (const int i_control_point : range) { + const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point]; + const float segment_len_inv = 1.0f / segment_len; + for (const int i : IndexRange(segment_len)) { + r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv; + } + } + }); + + if (is_cyclic) { + const int last_segment_len = offsets[size - 1] - offsets[size - 2]; + const float last_segment_len_inv = 1.0f / last_segment_len; + for (const int i : IndexRange(last_segment_len)) { + r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv; + } + } + else { + r_mappings.last() = size - 1; + } +} + +/** + * Returns non-owning access to an array of values containing the information necessary to + * interpolate values from the original control points to evaluated points. The control point + * index is the integer part of each value, and the factor used for interpolating to the next + * control point is the remaining factional part. + */ +Span<float> BezierSpline::evaluated_mappings() const +{ + if (!mapping_cache_dirty_) { + return evaluated_mapping_cache_; + } + + std::lock_guard lock{mapping_cache_mutex_}; + if (!mapping_cache_dirty_) { + return evaluated_mapping_cache_; + } + + const int size = this->size(); + const int eval_size = this->evaluated_points_size(); + evaluated_mapping_cache_.resize(eval_size); + MutableSpan<float> mappings = evaluated_mapping_cache_; + + if (eval_size == 1) { + mappings.first() = 0.0f; + mapping_cache_dirty_ = false; + return mappings; + } + + Span<int> offsets = this->control_point_offsets(); + + calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings); + + mapping_cache_dirty_ = false; + return mappings; +} + +Span<float3> BezierSpline::evaluated_positions() const +{ + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + std::lock_guard lock{position_cache_mutex_}; + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_position_cache_.resize(eval_size); + + MutableSpan<float3> positions = evaluated_position_cache_; + + Span<int> offsets = this->control_point_offsets(); + BLI_assert(offsets.last() <= eval_size); + + const int grain_size = std::max(512 / resolution_, 1); + blender::parallel_for(IndexRange(this->size() - 1), grain_size, [&](IndexRange range) { + for (const int i : range) { + this->evaluate_bezier_segment( + i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); + } + }); + + const int i_last = this->size() - 1; + if (is_cyclic_) { + this->evaluate_bezier_segment(i_last, 0, positions.slice(offsets.last(), resolution_)); + } + else { + /* Since evaluating the bezier segment doesn't add the final point, + * it must be added manually in the non-cyclic case. */ + positions.last() = positions_.last(); + } + + position_cache_dirty_ = false; + return positions; +} + +/** + * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary + * to interpolate data from control points to evaluated points between them. The next control + * point index result will not overflow the size of the vector. + */ +BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( + const float index_factor) const +{ + const int points_len = this->size(); + + if (is_cyclic_) { + if (index_factor < points_len) { + const int index = std::floor(index_factor); + const int next_index = (index < points_len - 1) ? index + 1 : 0; + return InterpolationData{index, next_index, index_factor - index}; + } + return InterpolationData{points_len - 1, 0, 1.0f}; + } + + if (index_factor < points_len - 1) { + const int index = std::floor(index_factor); + const int next_index = index + 1; + return InterpolationData{index, next_index, index_factor - index}; + } + return InterpolationData{points_len - 2, points_len - 1, 1.0f}; +} + +/* Use a spline argument to avoid adding this to the header. */ +template<typename T> +static void interpolate_to_evaluated_points_impl(const BezierSpline &spline, + const blender::VArray<T> &source_data, + MutableSpan<T> result_data) +{ + Span<float> mappings = spline.evaluated_mappings(); + + for (const int i : result_data.index_range()) { + BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor( + mappings[i]); + + const T &value = source_data[interp.control_point_index]; + const T &next_value = source_data[interp.next_control_point_index]; + + result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value); + } +} + +blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const +{ + BLI_assert(source_data.size() == this->size()); + + const int eval_size = this->evaluated_points_size(); + if (eval_size == 1) { + return source_data.shallow_copy(); + } + + blender::fn::GVArrayPtr new_varray; + blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { + Array<T> values(eval_size); + interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values); + new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>( + std::move(values)); + } + }); + + return new_varray; +} diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc new file mode 100644 index 00000000000..37d1232cfeb --- /dev/null +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -0,0 +1,417 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_span.hh" +#include "BLI_virtual_array.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +using blender::Array; +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + +SplinePtr NURBSpline::copy() const +{ + return std::make_unique<NURBSpline>(*this); +} + +int NURBSpline::size() const +{ + const int size = positions_.size(); + BLI_assert(size == radii_.size()); + BLI_assert(size == tilts_.size()); + BLI_assert(size == weights_.size()); + return size; +} + +int NURBSpline::resolution() const +{ + return resolution_; +} + +void NURBSpline::set_resolution(const int value) +{ + BLI_assert(value > 0); + resolution_ = value; + this->mark_cache_invalid(); +} + +uint8_t NURBSpline::order() const +{ + return order_; +} + +void NURBSpline::set_order(const uint8_t value) +{ + BLI_assert(value >= 2 && value <= 6); + order_ = value; + this->mark_cache_invalid(); +} + +void NURBSpline::add_point(const float3 position, + const float radius, + const float tilt, + const float weight) +{ + positions_.append(position); + radii_.append(radius); + tilts_.append(tilt); + weights_.append(weight); + knots_dirty_ = true; + this->mark_cache_invalid(); +} + +MutableSpan<float3> NURBSpline::positions() +{ + return positions_; +} +Span<float3> NURBSpline::positions() const +{ + return positions_; +} +MutableSpan<float> NURBSpline::radii() +{ + return radii_; +} +Span<float> NURBSpline::radii() const +{ + return radii_; +} +MutableSpan<float> NURBSpline::tilts() +{ + return tilts_; +} +Span<float> NURBSpline::tilts() const +{ + return tilts_; +} +MutableSpan<float> NURBSpline::weights() +{ + return weights_; +} +Span<float> NURBSpline::weights() const +{ + return weights_; +} + +void NURBSpline::mark_cache_invalid() +{ + basis_cache_dirty_ = true; + position_cache_dirty_ = true; + tangent_cache_dirty_ = true; + normal_cache_dirty_ = true; + length_cache_dirty_ = true; +} + +int NURBSpline::evaluated_points_size() const +{ + if (!this->check_valid_size_and_order()) { + return 0; + } + return resolution_ * this->segments_size(); +} + +void NURBSpline::correct_end_tangents() const +{ +} + +bool NURBSpline::check_valid_size_and_order() const +{ + if (this->size() < order_) { + return false; + } + + if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) { + if (order_ == 4) { + if (this->size() < 5) { + return false; + } + } + else if (order_ != 3) { + return false; + } + } + + return true; +} + +int NURBSpline::knots_size() const +{ + const int size = this->size() + order_; + return is_cyclic_ ? size + order_ - 1 : size; +} + +void NURBSpline::calculate_knots() const +{ + const KnotsMode mode = this->knots_mode; + const int length = this->size(); + const int order = order_; + + knots_.resize(this->knots_size()); + + MutableSpan<float> knots = knots_; + + if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) { + for (const int i : knots.index_range()) { + knots[i] = static_cast<float>(i); + } + } + else if (mode == NURBSpline::KnotsMode::EndPoint) { + float k = 0.0f; + for (const int i : IndexRange(1, knots.size())) { + knots[i - 1] = k; + if (i >= order && i <= length) { + k += 1.0f; + } + } + } + else if (mode == NURBSpline::KnotsMode::Bezier) { + BLI_assert(ELEM(order, 3, 4)); + if (order == 3) { + float k = 0.6f; + for (const int i : knots.index_range()) { + if (i >= order && i <= length) { + k += 0.5f; + } + knots[i] = std::floor(k); + } + } + else { + float k = 0.34f; + for (const int i : knots.index_range()) { + knots[i] = std::floor(k); + k += 1.0f / 3.0f; + } + } + } + + if (is_cyclic_) { + const int b = length + order - 1; + if (order > 2) { + for (const int i : IndexRange(1, order - 2)) { + if (knots[b] != knots[b - i]) { + if (i == order - 1) { + knots[length + order - 2] += 1.0f; + break; + } + } + } + } + + int c = order; + for (int i = b; i < this->knots_size(); i++) { + knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]); + c--; + } + } +} + +Span<float> NURBSpline::knots() const +{ + if (!knots_dirty_) { + BLI_assert(knots_.size() == this->size() + order_); + return knots_; + } + + std::lock_guard lock{knots_mutex_}; + if (!knots_dirty_) { + BLI_assert(knots_.size() == this->size() + order_); + return knots_; + } + + this->calculate_knots(); + + knots_dirty_ = false; + + return knots_; +} + +static void calculate_basis_for_point(const float parameter, + const int points_len, + const int order, + Span<float> knots, + MutableSpan<float> basis_buffer, + NURBSpline::BasisCache &basis_cache) +{ + /* Clamp parameter due to floating point inaccuracy. TODO: Look into using doubles. */ + const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]); + + int start = 0; + int end = 0; + for (const int i : IndexRange(points_len + order - 1)) { + const bool knots_equal = knots[i] == knots[i + 1]; + if (knots_equal || t < knots[i] || t > knots[i + 1]) { + basis_buffer[i] = 0.0f; + continue; + } + + basis_buffer[i] = 1.0f; + start = std::max(i - order - 1, 0); + end = i; + basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f); + break; + } + basis_buffer[points_len + order - 1] = 0.0f; + + for (const int i_order : IndexRange(2, order - 1)) { + if (end + i_order >= points_len + order) { + end = points_len + order - 1 - i_order; + } + for (const int i : IndexRange(start, end - start + 1)) { + float new_basis = 0.0f; + if (basis_buffer[i] != 0.0f) { + new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]); + } + + if (basis_buffer[i + 1] != 0.0f) { + new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) / + (knots[i + i_order] - knots[i + 1]); + } + + basis_buffer[i] = new_basis; + } + } + + /* Shrink the range of calculated values to avoid storing unnecessary zeros. */ + while (basis_buffer[start] == 0.0f && start < end) { + start++; + } + while (basis_buffer[end] == 0.0f && end > start) { + end--; + } + + basis_cache.weights.clear(); + basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1)); + basis_cache.start_index = start; +} + +void NURBSpline::calculate_basis_cache() const +{ + if (!basis_cache_dirty_) { + return; + } + + std::lock_guard lock{basis_cache_mutex_}; + if (!basis_cache_dirty_) { + return; + } + + const int points_len = this->size(); + const int eval_size = this->evaluated_points_size(); + BLI_assert(this->evaluated_edges_size() > 0); + basis_cache_.resize(eval_size); + + const int order = this->order(); + Span<float> control_weights = this->weights(); + Span<float> knots = this->knots(); + + MutableSpan<BasisCache> basis_cache(basis_cache_); + + /* This buffer is reused by each basis calculation to store temporary values. + * Theoretically it could be optimized away in the future. */ + Array<float> basis_buffer(this->knots_size()); + + const float start = knots[order - 1]; + const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len]; + const float step = (end - start) / this->evaluated_edges_size(); + float parameter = start; + for (const int i : IndexRange(eval_size)) { + BasisCache &basis = basis_cache[i]; + calculate_basis_for_point( + parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis); + BLI_assert(basis.weights.size() <= order); + + for (const int j : basis.weights.index_range()) { + const int point_index = (basis.start_index + j) % points_len; + basis.weights[j] *= control_weights[point_index]; + } + + parameter += step; + } + + basis_cache_dirty_ = false; +} + +template<typename T> +void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights, + const blender::VArray<T> &source_data, + MutableSpan<T> result_data) +{ + const int points_len = source_data.size(); + BLI_assert(result_data.size() == weights.size()); + blender::attribute_math::DefaultMixer<T> mixer(result_data); + + for (const int i : result_data.index_range()) { + Span<float> point_weights = weights[i].weights; + const int start_index = weights[i].start_index; + + for (const int j : point_weights.index_range()) { + const int point_index = (start_index + j) % points_len; + mixer.mix_in(i, source_data[point_index], point_weights[j]); + } + } + + mixer.finalize(); +} + +blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const +{ + BLI_assert(source_data.size() == this->size()); + + this->calculate_basis_cache(); + Span<BasisCache> weights(basis_cache_); + + blender::fn::GVArrayPtr new_varray; + blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { + Array<T> values(this->evaluated_points_size()); + interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values); + new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>( + std::move(values)); + } + }); + + return new_varray; +} + +Span<float3> NURBSpline::evaluated_positions() const +{ + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + std::lock_guard lock{position_cache_mutex_}; + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_position_cache_.resize(eval_size); + + blender::fn::GVArray_Typed<float3> evaluated_positions{ + this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span<float3>(positions_))}; + + evaluated_positions->materialize(evaluated_position_cache_); + + position_cache_dirty_ = false; + return evaluated_position_cache_; +} diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc new file mode 100644 index 00000000000..09c578c0503 --- /dev/null +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -0,0 +1,105 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_span.hh" +#include "BLI_virtual_array.hh" + +#include "BKE_spline.hh" + +using blender::float3; +using blender::MutableSpan; +using blender::Span; + +SplinePtr PolySpline::copy() const +{ + return std::make_unique<PolySpline>(*this); +} + +int PolySpline::size() const +{ + const int size = positions_.size(); + BLI_assert(size == radii_.size()); + BLI_assert(size == tilts_.size()); + return size; +} + +void PolySpline::add_point(const float3 position, const float radius, const float tilt) +{ + positions_.append(position); + radii_.append(radius); + tilts_.append(tilt); + this->mark_cache_invalid(); +} + +MutableSpan<float3> PolySpline::positions() +{ + return positions_; +} +Span<float3> PolySpline::positions() const +{ + return positions_; +} +MutableSpan<float> PolySpline::radii() +{ + return radii_; +} +Span<float> PolySpline::radii() const +{ + return radii_; +} +MutableSpan<float> PolySpline::tilts() +{ + return tilts_; +} +Span<float> PolySpline::tilts() const +{ + return tilts_; +} + +void PolySpline::mark_cache_invalid() +{ + tangent_cache_dirty_ = true; + normal_cache_dirty_ = true; + length_cache_dirty_ = true; +} + +int PolySpline::evaluated_points_size() const +{ + return this->size(); +} + +void PolySpline::correct_end_tangents() const +{ +} + +Span<float3> PolySpline::evaluated_positions() const +{ + return this->positions(); +} + +/** + * Poly spline interpolation from control points to evaluated points is a special case, since + * the result data is the same as the input data. This function returns a GVArray that points to + * the original data. Therefore the lifetime of the returned virtual array must not be longer than + * the source data. + */ +blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const +{ + BLI_assert(source_data.size() == this->size()); + + return source_data.shallow_copy(); +} diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 7b03839f659..c0ce57818d1 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -39,6 +39,7 @@ #include "BLI_utildefines.h" #include "BKE_anim_data.h" +#include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -1003,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr #endif } -static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, - struct Scene *scene, - Object *object, - Volume *volume_input) +static void volume_evaluate_modifiers(struct Depsgraph *depsgraph, + struct Scene *scene, + Object *object, + GeometrySet &geometry_set) { - Volume *volume = volume_input; - /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; @@ -1030,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, continue; } - if (mti->modifyVolume) { - /* Ensure we are not modifying the input. */ - if (volume == volume_input) { - volume = BKE_volume_copy_for_eval(volume, true); - } - - Volume *volume_next = mti->modifyVolume(md, &mectx, volume); - - if (volume_next && volume_next != volume) { - /* If the modifier returned a new volume, release the old one. */ - if (volume != volume_input) { - BKE_id_free(nullptr, volume); - } - volume = volume_next; - } + if (mti->modifyGeometrySet) { + mti->modifyGeometrySet(md, &mectx, &geometry_set); } } - - return volume; } void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) @@ -1072,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) } } +static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set) +{ + if (!geometry_set.has<VolumeComponent>()) { + return nullptr; + } + VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); + Volume *volume = volume_component.release(); + if (volume != nullptr) { + /* Add back, but only as read-only non-owning component. */ + volume_component.replace(volume, GeometryOwnershipType::ReadOnly); + } + else { + /* The component was empty, we can remove it. */ + geometry_set.remove<VolumeComponent>(); + } + return volume; +} + void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) { /* Free any evaluated data and restore original data. */ @@ -1079,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob /* Evaluate modifiers. */ Volume *volume = (Volume *)object->data; - Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume); + GeometrySet geometry_set; + geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly); + volume_evaluate_modifiers(depsgraph, scene, object, geometry_set); + + Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set); + + /* If the geometry set did not contain a volume, we still create an empty one. */ + if (volume_eval == nullptr) { + volume_eval = BKE_volume_new_for_eval(volume); + } /* Assign evaluated object. */ - const bool is_owned = (volume != volume_eval); - BKE_object_eval_assign_data(object, &volume_eval->id, is_owned); + const bool eval_is_owned = (volume != volume_eval); + BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned); + object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set)); } void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath) diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h index 680c4bc78da..4b5a7d671f2 100644 --- a/source/blender/blenlib/BLI_compiler_attrs.h +++ b/source/blender/blenlib/BLI_compiler_attrs.h @@ -98,3 +98,10 @@ #else # define ATTR_ALIGN(x) __attribute__((aligned(x))) #endif + +/* Alignment directive */ +#ifdef _WIN64 +# define ALIGN_STRUCT __declspec(align(64)) +#else +# define ALIGN_STRUCT +#endif diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index cbc4d4ed366..04aae375889 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -245,6 +245,13 @@ struct float3 { return result; } + static float3 cross(const float3 &a, const float3 &b) + { + float3 result; + cross_v3_v3v3(result, a, b); + return result; + } + static float3 project(const float3 &a, const float3 &b) { float3 result; diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index f5ba91bc12c..396b0b1bd21 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -45,6 +45,37 @@ struct float4x4 { return mat; } + static float4x4 from_normalized_axis_data(const float3 location, + const float3 forward, + const float3 up) + { + BLI_ASSERT_UNIT_V3(forward); + BLI_ASSERT_UNIT_V3(up); + float4x4 matrix; + const float3 cross = float3::cross(forward, up); + matrix.values[0][0] = forward.x; + matrix.values[1][0] = cross.x; + matrix.values[2][0] = up.x; + matrix.values[3][0] = location.x; + + matrix.values[0][1] = forward.y; + matrix.values[1][1] = cross.y; + matrix.values[2][1] = up.y; + matrix.values[3][1] = location.y; + + matrix.values[0][2] = forward.z; + matrix.values[1][2] = cross.z; + matrix.values[2][2] = up.z; + matrix.values[3][2] = location.z; + + matrix.values[0][3] = 0.0f; + matrix.values[1][3] = 0.0f; + matrix.values[2][3] = 0.0f; + matrix.values[3][3] = 1.0f; + + return matrix; + } + static float4x4 identity() { float4x4 mat; @@ -116,6 +147,19 @@ struct float4x4 { return scale; } + void apply_scale(const float scale) + { + values[0][0] *= scale; + values[0][1] *= scale; + values[0][2] *= scale; + values[1][0] *= scale; + values[1][1] *= scale; + values[1][2] *= scale; + values[2][0] *= scale; + values[2][1] *= scale; + values[2][2] *= scale; + } + float4x4 inverted() const { float4x4 result; diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 9fa69853e44..95afbfc2ec6 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -247,11 +247,11 @@ class Map { { this->add_new_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - void add_new_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + void add_new_as(ForwardKey &&key, ForwardValue &&... value) { this->add_new__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -277,11 +277,11 @@ class Map { { return this->add_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - bool add_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + bool add_as(ForwardKey &&key, ForwardValue &&... value) { return this->add__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -307,11 +307,11 @@ class Map { { return this->add_overwrite_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value) { return this->add_overwrite__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -413,12 +413,12 @@ class Map { { return this->pop_default_as(key, std::move(default_value)); } - template<typename ForwardKey, typename ForwardValue> - Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value) + template<typename ForwardKey, typename... ForwardValue> + Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value) { Slot *slot = this->lookup_slot_ptr(key, hash_(key)); if (slot == nullptr) { - return std::forward<ForwardValue>(default_value); + return Value(std::forward<ForwardValue>(default_value)...); } Value value = std::move(*slot->value()); slot->remove(); @@ -525,15 +525,15 @@ class Map { { return this->lookup_default_as(key, default_value); } - template<typename ForwardKey, typename ForwardValue> - Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const + template<typename ForwardKey, typename... ForwardValue> + Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const { const Value *ptr = this->lookup_ptr_as(key); if (ptr != nullptr) { return *ptr; } else { - return std::forward<ForwardValue>(default_value); + return Value(std::forward<ForwardValue>(default_value)...); } } @@ -557,11 +557,11 @@ class Map { { return this->lookup_or_add_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value) { return this->lookup_or_add__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -621,19 +621,23 @@ class Map { } } - /** - * A utility iterator that reduces the amount of code when implementing the actual iterators. - * This uses the "curiously recurring template pattern" (CRTP). - */ - template<typename SubIterator> struct BaseIterator { + /* Common base class for all iterators below. */ + struct BaseIterator { + public: using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; + protected: + /* We could have separate base iterators for const and non-const iterators, but that would add + * more complexity than benefits right now. */ Slot *slots_; int64_t total_slots_; int64_t current_slot_; - BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) + friend Map; + + public: + BaseIterator(const Slot *slots, const int64_t total_slots, const int64_t current_slot) : slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot) { } @@ -667,11 +671,29 @@ class Map { return !(a != b); } + protected: + Slot ¤t_slot() const + { + return slots_[current_slot_]; + } + }; + + /** + * A utility iterator that reduces the amount of code when implementing the actual iterators. + * This uses the "curiously recurring template pattern" (CRTP). + */ + template<typename SubIterator> class BaseIteratorRange : public BaseIterator { + public: + BaseIteratorRange(const Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIterator(slots, total_slots, current_slot) + { + } + SubIterator begin() const { - for (int64_t i = 0; i < total_slots_; i++) { - if (slots_[i].is_occupied()) { - return SubIterator(slots_, total_slots_, i); + for (int64_t i = 0; i < this->total_slots_; i++) { + if (this->slots_[i].is_occupied()) { + return SubIterator(this->slots_, this->total_slots_, i); } } return this->end(); @@ -679,23 +701,18 @@ class Map { SubIterator end() const { - return SubIterator(slots_, total_slots_, total_slots_); - } - - Slot ¤t_slot() const - { - return slots_[current_slot_]; + return SubIterator(this->slots_, this->total_slots_, this->total_slots_); } }; - class KeyIterator final : public BaseIterator<KeyIterator> { + class KeyIterator final : public BaseIteratorRange<KeyIterator> { public: using value_type = Key; using pointer = const Key *; using reference = const Key &; KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<KeyIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<KeyIterator>(slots, total_slots, current_slot) { } @@ -705,14 +722,14 @@ class Map { } }; - class ValueIterator final : public BaseIterator<ValueIterator> { + class ValueIterator final : public BaseIteratorRange<ValueIterator> { public: using value_type = Value; using pointer = const Value *; using reference = const Value &; ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<ValueIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<ValueIterator>(slots, total_slots, current_slot) { } @@ -722,14 +739,14 @@ class Map { } }; - class MutableValueIterator final : public BaseIterator<MutableValueIterator> { + class MutableValueIterator final : public BaseIteratorRange<MutableValueIterator> { public: using value_type = Value; using pointer = Value *; using reference = Value &; - MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<MutableValueIterator>(slots, total_slots, current_slot) + MutableValueIterator(Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIteratorRange<MutableValueIterator>(slots, total_slots, current_slot) { } @@ -754,14 +771,14 @@ class Map { } }; - class ItemIterator final : public BaseIterator<ItemIterator> { + class ItemIterator final : public BaseIteratorRange<ItemIterator> { public: using value_type = Item; using pointer = Item *; using reference = Item &; ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<ItemIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<ItemIterator>(slots, total_slots, current_slot) { } @@ -772,14 +789,14 @@ class Map { } }; - class MutableItemIterator final : public BaseIterator<MutableItemIterator> { + class MutableItemIterator final : public BaseIteratorRange<MutableItemIterator> { public: using value_type = MutableItem; using pointer = MutableItem *; using reference = MutableItem &; - MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<MutableItemIterator>(slots, total_slots, current_slot) + MutableItemIterator(Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIteratorRange<MutableItemIterator>(slots, total_slots, current_slot) { } @@ -840,6 +857,19 @@ class Map { } /** + * Remove the key-value-pair that the iterator is currently pointing at. + * It is valid to call this method while iterating over the map. However, after this method has + * been called, the removed element must not be accessed anymore. + */ + void remove(const BaseIterator &iterator) + { + Slot &slot = iterator.current_slot(); + BLI_assert(slot.is_occupied()); + slot.remove(); + removed_slots_++; + } + + /** * Print common statistics like size and collision count. This is useful for debugging purposes. */ void print_stats(StringRef name = "") const @@ -982,7 +1012,7 @@ class Map { SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) { Slot &slot = new_slots[slot_index]; if (slot.is_empty()) { - slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash); + slot.occupy(std::move(*old_slot.key()), hash, std::move(*old_slot.value())); return; } } @@ -996,8 +1026,8 @@ class Map { new (this) Map(NoExceptConstructor(), allocator); } - template<typename ForwardKey, typename ForwardValue> - void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { BLI_assert(!this->contains_as(key)); @@ -1005,7 +1035,7 @@ class Map { MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...); occupied_and_removed_slots_++; return; } @@ -1013,14 +1043,14 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey, typename ForwardValue> - bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { this->ensure_can_add(); MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...); occupied_and_removed_slots_++; return true; } @@ -1075,7 +1105,7 @@ class Map { MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), create_value(), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, create_value()); occupied_and_removed_slots_++; return *slot.value(); } @@ -1086,14 +1116,14 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey, typename ForwardValue> - Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { this->ensure_can_add(); MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...); occupied_and_removed_slots_++; return *slot.value(); } @@ -1104,15 +1134,15 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey, typename ForwardValue> - bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { auto create_func = [&](Value *ptr) { - new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)); + new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...); return true; }; auto modify_func = [&](Value *ptr) { - *ptr = std::forward<ForwardValue>(value); + *ptr = Value(std::forward<ForwardValue>(value)...); return false; }; return this->add_or_modify__impl( @@ -1221,16 +1251,18 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper { map_.reserve(n); } - template<typename ForwardKey, typename ForwardValue> - void add_new(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + void add_new(ForwardKey &&key, ForwardValue &&... value) { - map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}); + map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)}); } - template<typename ForwardKey, typename ForwardValue> - bool add(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + bool add(ForwardKey &&key, ForwardValue &&... value) { - return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second; + return map_ + .insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)}) + .second; } bool contains(const Key &key) const diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh index c0cb3091a8e..1b4ca11af41 100644 --- a/source/blender/blenlib/BLI_map_slots.hh +++ b/source/blender/blenlib/BLI_map_slots.hh @@ -195,11 +195,11 @@ template<typename Key, typename Value> class SimpleMapSlot { * Change the state of this slot from empty/removed to occupied. The key/value has to be * constructed by calling the constructor with the given key/value as parameter. */ - template<typename ForwardKey, typename ForwardValue> - void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { BLI_assert(!this->is_occupied()); - new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + new (&value_buffer_) Value(std::forward<ForwardValue>(value)...); this->occupy_no_value(std::forward<ForwardKey>(key), hash); state_ = Occupied; } @@ -315,12 +315,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot return is_equal(key, key_); } - template<typename ForwardKey, typename ForwardValue> - void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + new (&value_buffer_) Value(std::forward<ForwardValue>(value)...); this->occupy_no_value(std::forward<ForwardKey>(key), hash); } diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 26d2f1fcb29..a113f3a370c 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -105,8 +105,8 @@ int constrain_rgb(float *r, float *g, float *b); void minmax_rgb(short c[3]); void hsv_clamp_v(float hsv[3], float v_max); -void rgb_float_set_hue_float_offset(float *rgb, float hue_offset); -void rgb_byte_set_hue_float_offset(unsigned char *rgb, float hue_offset); +void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset); +void rgb_byte_set_hue_float_offset(unsigned char rgb[3], float hue_offset); void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3]); void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4]); diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index d767c2924d1..c744c5d13d3 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -119,10 +119,10 @@ float dist_signed_to_plane_v3(const float p[3], const float plane[4]); float dist_to_plane_v3(const float p[3], const float plane[4]); /* plane3 versions */ -float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[4]); -float dist_squared_to_plane3_v3(const float p[3], const float plane[4]); -float dist_signed_to_plane3_v3(const float p[3], const float plane[4]); -float dist_to_plane3_v3(const float p[3], const float plane[4]); +float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[3]); +float dist_squared_to_plane3_v3(const float p[3], const float plane[3]); +float dist_signed_to_plane3_v3(const float p[3], const float plane[3]); +float dist_to_plane3_v3(const float p[3], const float plane[3]); float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]); float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]); @@ -778,7 +778,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]); MINLINE float eval_shv3(float r[9], const float v[3]); MINLINE float diffuse_shv3(float r[9], const float v[3]); MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f); -MINLINE void madd_sh_shfl(float r[9], const float sh[3], const float f); +MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f); /********************************* Form Factor *******************************/ diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 378095589e8..54df88ca541 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -362,7 +362,7 @@ void loc_quat_size_to_mat4(float R[4][4], const float size[3]); void loc_axisangle_size_to_mat4(float R[4][4], const float loc[3], - const float axis[4], + const float axis[3], const float angle, const float size[3]); diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h index 13481e27e2a..39a79efc7e2 100644 --- a/source/blender/blenlib/BLI_math_solvers.h +++ b/source/blender/blenlib/BLI_math_solvers.h @@ -41,7 +41,7 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3], float r_eigen_values[3], float r_eigen_vectors[3][3]); -void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]); +void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3][3]); /***************************** Simple Solvers ************************************/ diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index bb1e1a1c38d..b43f86af670 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -146,7 +146,7 @@ MINLINE void mul_v3_v3(float r[3], const float a[3]); MINLINE void mul_v3_v3v3(float r[3], const float a[3], const float b[3]); MINLINE void mul_v4_fl(float r[4], float f); MINLINE void mul_v4_v4(float r[4], const float a[4]); -MINLINE void mul_v4_v4fl(float r[3], const float a[4], float f); +MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f); MINLINE void mul_v2_v2_cw(float r[2], const float mat[2], const float vec[2]); MINLINE void mul_v2_v2_ccw(float r[2], const float mat[2], const float vec[2]); MINLINE float mul_project_m4_v3_zfac(const float mat[4][4], @@ -177,7 +177,7 @@ MINLINE void negate_v2_v2(float r[2], const float a[2]); MINLINE void negate_v3(float r[3]); MINLINE void negate_v3_v3(float r[3], const float a[3]); MINLINE void negate_v4(float r[4]); -MINLINE void negate_v4_v4(float r[4], const float a[3]); +MINLINE void negate_v4_v4(float r[4], const float a[4]); MINLINE void negate_v3_short(short r[3]); MINLINE void negate_v3_db(double r[3]); @@ -323,11 +323,11 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]); /********************************* Comparison ********************************/ -MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; -bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; +bool is_finite_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index fe511793c46..c3876d4eaf8 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -94,7 +94,7 @@ template<typename T> class Span { using iterator = const T *; using size_type = int64_t; - private: + protected: const T *data_ = nullptr; int64_t size_ = 0; @@ -477,7 +477,7 @@ template<typename T> class MutableSpan { using iterator = T *; using size_type = int64_t; - private: + protected: T *data_; int64_t size_; @@ -662,6 +662,16 @@ template<typename T> class MutableSpan { } /** + * Return a reference to the first element in the array. This invokes undefined behavior when the + * array is empty. + */ + constexpr T &first() const + { + BLI_assert(size_ > 0); + return data_[0]; + } + + /** * Returns a reference to the last element. This invokes undefined behavior when the array is * empty. */ diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh index 19f77078c5b..d66316a95d9 100644 --- a/source/blender/blenlib/BLI_stack.hh +++ b/source/blender/blenlib/BLI_stack.hh @@ -232,13 +232,14 @@ class Stack { { this->push_as(std::move(value)); } - template<typename ForwardT> void push_as(ForwardT &&value) + /* This is similar to `std::stack::emplace`. */ + template<typename... ForwardT> void push_as(ForwardT &&... value) { if (top_ == top_chunk_->capacity_end) { this->activate_next_chunk(1); } try { - new (top_) T(std::forward<ForwardT>(value)); + new (top_) T(std::forward<ForwardT>(value)...); top_++; size_++; } diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 328d623787b..8bea2584ca7 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -437,13 +437,17 @@ class Vector { */ void append(const T &value) { - this->ensure_space_for_one(); - this->append_unchecked(value); + this->append_as(value); } void append(T &&value) { + this->append_as(std::move(value)); + } + /* This is similar to `std::vector::emplace_back`. */ + template<typename... ForwardValue> void append_as(ForwardValue &&... value) + { this->ensure_space_for_one(); - this->append_unchecked(std::move(value)); + this->append_unchecked_as(std::forward<ForwardValue>(value)...); } /** @@ -474,10 +478,18 @@ class Vector { * behavior when not enough capacity has been reserved beforehand. Only use this in performance * critical code. */ - template<typename ForwardT> void append_unchecked(ForwardT &&value) + void append_unchecked(const T &value) + { + this->append_unchecked_as(value); + } + void append_unchecked(T &&value) + { + this->append_unchecked_as(std::move(value)); + } + template<typename... ForwardT> void append_unchecked_as(ForwardT &&... value) { BLI_assert(end_ < capacity_end_); - new (end_) T(std::forward<ForwardT>(value)); + new (end_) T(std::forward<ForwardT>(value)...); end_++; UPDATE_VECTOR_SIZE(this); } @@ -657,6 +669,21 @@ class Vector { } /** + * Return a reference to the first element in the vector. + * This invokes undefined behavior when the vector is empty. + */ + const T &first() const + { + BLI_assert(this->size() > 0); + return *begin_; + } + T &first() + { + BLI_assert(this->size() > 0); + return *begin_; + } + + /** * Return how many values are currently stored in the vector. */ int64_t size() const @@ -713,11 +740,12 @@ class Vector { BLI_assert(index >= 0); BLI_assert(index < this->size()); T *element_to_remove = begin_ + index; - if (element_to_remove < end_) { - *element_to_remove = std::move(*(end_ - 1)); + T *last_element = end_ - 1; + if (element_to_remove < last_element) { + *element_to_remove = std::move(*last_element); } - end_--; - end_->~T(); + end_ = last_element; + last_element->~T(); UPDATE_VECTOR_SIZE(this); } diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index 14b38d564cb..2f5d04aefa2 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -398,6 +398,23 @@ class VectorSet { } /** + * Return the index of the key in the vector. If the key is not in the set, add it and return its + * index. + */ + int64_t index_of_or_add(const Key &key) + { + return this->index_of_or_add_as(key); + } + int64_t index_of_or_add(Key &&key) + { + return this->index_of_or_add_as(std::move(key)); + } + template<typename ForwardKey> int64_t index_of_or_add_as(ForwardKey &&key) + { + return this->index_of_or_add__impl(std::forward<ForwardKey>(key), hash_(key)); + } + + /** * Get a pointer to the beginning of the array containing all keys. */ const Key *data() const @@ -484,6 +501,14 @@ class VectorSet { } /** + * Remove all keys from the vector set. + */ + void clear() + { + this->noexcept_reset(); + } + + /** * Get the number of collisions that the probing strategy has to go through to find the key or * determine that it is not in the set. */ @@ -652,6 +677,26 @@ class VectorSet { VECTOR_SET_SLOT_PROBING_END(); } + template<typename ForwardKey> + int64_t index_of_or_add__impl(ForwardKey &&key, const uint64_t hash) + { + this->ensure_can_add(); + + VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) { + if (slot.contains(key, is_equal_, hash, keys_)) { + return slot.index(); + } + if (slot.is_empty()) { + const int64_t index = this->size(); + new (keys_ + index) Key(std::forward<ForwardKey>(key)); + slot.occupy(index, hash); + occupied_and_removed_slots_++; + return index; + } + } + VECTOR_SET_SLOT_PROBING_END(); + } + Key pop__impl() { BLI_assert(this->size() > 0); diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index f9b0aaa7de6..1c02bce8411 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -37,6 +37,8 @@ * see of the increased compile time and binary size is worth it. */ +#include "BLI_array.hh" +#include "BLI_index_mask.hh" #include "BLI_span.hh" namespace blender { @@ -71,6 +73,11 @@ template<typename T> class VArray { return size_ == 0; } + IndexRange index_range() const + { + return IndexRange(size_); + } + /* Returns true when the virtual array is stored as a span internally. */ bool is_span() const { @@ -82,13 +89,13 @@ template<typename T> class VArray { /* Returns the internally used span of the virtual array. This invokes undefined behavior is the * virtual array is not stored as a span internally. */ - Span<T> get_span() const + Span<T> get_internal_span() const { BLI_assert(this->is_span()); if (size_ == 0) { return {}; } - return this->get_span_impl(); + return this->get_internal_span_impl(); } /* Returns true when the virtual array returns the same value for every index. */ @@ -102,20 +109,46 @@ template<typename T> class VArray { /* Returns the value that is returned for every index. This invokes undefined behavior if the * virtual array would not return the same value for every index. */ - T get_single() const + T get_internal_single() const { BLI_assert(this->is_single()); if (size_ == 1) { return this->get(0); } - return this->get_single_impl(); + return this->get_internal_single_impl(); } + /* Get the element at a specific index. Note that this operator cannot be used to assign values + * to an index, because the return value is not a reference. */ T operator[](const int64_t index) const { return this->get(index); } + /* Copy the entire virtual array into a span. */ + void materialize(MutableSpan<T> r_span) const + { + this->materialize(IndexMask(size_), r_span); + } + + /* Copy some indices of the virtual array into a span. */ + void materialize(IndexMask mask, MutableSpan<T> r_span) const + { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_impl(mask, r_span); + } + + void materialize_to_uninitialized(MutableSpan<T> r_span) const + { + this->materialize_to_uninitialized(IndexMask(size_), r_span); + } + + void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const + { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_to_uninitialized_impl(mask, r_span); + } + protected: virtual T get_impl(const int64_t index) const = 0; @@ -124,7 +157,7 @@ template<typename T> class VArray { return false; } - virtual Span<T> get_span_impl() const + virtual Span<T> get_internal_span_impl() const { BLI_assert_unreachable(); return {}; @@ -135,56 +168,196 @@ template<typename T> class VArray { return false; } - virtual T get_single_impl() const + virtual T get_internal_single_impl() const { /* Provide a default implementation, so that subclasses don't have to provide it. This method * should never be called because `is_single_impl` returns false by default. */ BLI_assert_unreachable(); return T(); } + + virtual void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const + { + T *dst = r_span.data(); + if (this->is_span()) { + const T *src = this->get_internal_span().data(); + mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; }); + } + else if (this->is_single()) { + const T single = this->get_internal_single(); + mask.foreach_index([&](const int64_t i) { dst[i] = single; }); + } + else { + mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); }); + } + } + + virtual void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const + { + T *dst = r_span.data(); + if (this->is_span()) { + const T *src = this->get_internal_span().data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); }); + } + else if (this->is_single()) { + const T single = this->get_internal_single(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); }); + } + else { + mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); }); + } + } +}; + +/* Similar to VArray, but the elements are mutable. */ +template<typename T> class VMutableArray : public VArray<T> { + public: + VMutableArray(const int64_t size) : VArray<T>(size) + { + } + + void set(const int64_t index, T value) + { + BLI_assert(index >= 0); + BLI_assert(index < this->size_); + this->set_impl(index, std::move(value)); + } + + /* Copy the values from the source span to all elements in the virtual array. */ + void set_all(Span<T> src) + { + BLI_assert(src.size() == this->size_); + this->set_all_impl(src); + } + + MutableSpan<T> get_internal_span() + { + BLI_assert(this->is_span()); + Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span(); + return MutableSpan<T>(const_cast<T *>(span.data()), span.size()); + } + + protected: + virtual void set_impl(const int64_t index, T value) = 0; + + virtual void set_all_impl(Span<T> src) + { + if (this->is_span()) { + const MutableSpan<T> span = this->get_internal_span(); + initialized_copy_n(src.data(), this->size_, span.data()); + } + else { + const int64_t size = this->size_; + for (int64_t i = 0; i < size; i++) { + this->set(i, src[i]); + } + } + } }; +template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>; +template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>; + /** - * A virtual array implementation for a span. This class is final so that it can be devirtualized - * by the compiler in some cases (e.g. when #devirtualize_varray is used). + * A virtual array implementation for a span. Methods in this class are final so that it can be + * devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used). */ -template<typename T> class VArrayForSpan final : public VArray<T> { - private: - const T *data_; +template<typename T> class VArray_For_Span : public VArray<T> { + protected: + const T *data_ = nullptr; public: - VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data()) + VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data()) { } protected: - T get_impl(const int64_t index) const override + VArray_For_Span(const int64_t size) : VArray<T>(size) + { + } + + T get_impl(const int64_t index) const final + { + return data_[index]; + } + + bool is_span_impl() const final + { + return true; + } + + Span<T> get_internal_span_impl() const final + { + return Span<T>(data_, this->size_); + } +}; + +template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> { + protected: + T *data_ = nullptr; + + public: + VMutableArray_For_MutableSpan(const MutableSpan<T> data) + : VMutableArray<T>(data.size()), data_(data.data()) + { + } + + protected: + VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size) + { + } + + T get_impl(const int64_t index) const final { return data_[index]; } + void set_impl(const int64_t index, T value) final + { + data_[index] = value; + } + bool is_span_impl() const override { return true; } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { return Span<T>(data_, this->size_); } }; /** + * A variant of `VArray_For_Span` that owns the underlying data. + * The `Container` type has to implement a `size()` and `data()` method. + * The `data()` method has to return a pointer to the first element in the continuous array of + * elements. + */ +template<typename Container, typename T = typename Container::value_type> +class VArray_For_ArrayContainer : public VArray_For_Span<T> { + private: + Container container_; + + public: + VArray_For_ArrayContainer(Container container) + : VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container)) + { + this->data_ = container_.data(); + } +}; + +/** * A virtual array implementation that returns the same value for every index. This class is final * so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is * used). */ -template<typename T> class VArrayForSingle final : public VArray<T> { +template<typename T> class VArray_For_Single final : public VArray<T> { private: T value_; public: - VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value)) + VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value)) { } @@ -199,7 +372,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> { return this->size_ == 1; } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { return Span<T>(&value_, 1); } @@ -209,13 +382,207 @@ template<typename T> class VArrayForSingle final : public VArray<T> { return true; } - T get_single_impl() const override + T get_internal_single_impl() const override { return value_; } }; /** + * In many cases a virtual array is a span internally. In those cases, access to individual could + * be much more efficient than calling a virtual method. When the underlying virtual array is not a + * span, this class allocates a new array and copies the values over. + * + * This should be used in those cases: + * - All elements in the virtual array are accessed multiple times. + * - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit + * from faster access. + * - An API is called, that does not accept virtual arrays, but only spans. + */ +template<typename T> class VArray_Span final : public Span<T> { + private: + const VArray<T> &varray_; + Array<T> owned_data_; + + public: + VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray) + { + this->size_ = varray_.size(); + if (varray_.is_span()) { + this->data_ = varray_.get_internal_span().data(); + } + else { + owned_data_.~Array(); + new (&owned_data_) Array<T>(varray_.size(), NoInitialization{}); + varray_.materialize_to_uninitialized(owned_data_); + this->data_ = owned_data_.data(); + } + } +}; + +/** + * Same as VArray_Span, but for a mutable span. + * The important thing to note is that when changing this span, the results might not be + * immediately reflected in the underlying virtual array (only when the virtual array is a span + * internally). The #save method can be used to write all changes to the underlying virtual array, + * if necessary. + */ +template<typename T> class VMutableArray_Span final : public MutableSpan<T> { + private: + VMutableArray<T> &varray_; + Array<T> owned_data_; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If + * not, a new array has to be allocated as a wrapper for the underlying virtual array. */ + VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true) + : MutableSpan<T>(), varray_(varray) + { + this->size_ = varray_.size(); + if (varray_.is_span()) { + this->data_ = varray_.get_internal_span().data(); + } + else { + if (copy_values_to_span) { + owned_data_.~Array(); + new (&owned_data_) Array<T>(varray_.size(), NoInitialization{}); + varray_.materialize_to_uninitialized(owned_data_); + } + else { + owned_data_.reinitialize(varray_.size()); + } + this->data_ = owned_data_.data(); + } + } + + ~VMutableArray_Span() + { + if (show_not_saved_warning_) { + if (!save_has_been_called_) { + std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + } + } + } + + /* Write back all values from a temporary allocated array to the underlying virtual array. */ + void save() + { + save_has_been_called_ = true; + if (this->data_ != owned_data_.data()) { + return; + } + varray_.set_all(owned_data_); + } + + void disable_not_applied_warning() + { + show_not_saved_warning_ = false; + } +}; + +/** + * This class makes it easy to create a virtual array for an existing function or lambda. The + * `GetFunc` should take a single `index` argument and return the value at that index. + */ +template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> { + private: + GetFunc get_func_; + + public: + VArray_For_Func(const int64_t size, GetFunc get_func) + : VArray<T>(size), get_func_(std::move(get_func)) + { + } + + private: + T get_impl(const int64_t index) const override + { + return get_func_(index); + } + + void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const override + { + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = get_func_(i); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const override + { + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(get_func_(i)); }); + } +}; + +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +class VArray_For_DerivedSpan : public VArray<ElemT> { + private: + const StructT *data_; + + public: + VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data()) + { + } + + private: + ElemT get_impl(const int64_t index) const override + { + return GetFunc(data_[index]); + } + + void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); }); + } +}; + +template<typename StructT, + typename ElemT, + ElemT (*GetFunc)(const StructT &), + void (*SetFunc)(StructT &, ElemT)> +class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> { + private: + StructT *data_; + + public: + VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data) + : VMutableArray<ElemT>(data.size()), data_(data.data()) + { + } + + private: + ElemT get_impl(const int64_t index) const override + { + return GetFunc(data_[index]); + } + + void set_impl(const int64_t index, ElemT value) override + { + SetFunc(data_[index], std::move(value)); + } + + void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); }); + } +}; + +/** * Generate multiple versions of the given function optimized for different virtual arrays. * One has to be careful with nesting multiple devirtualizations, because that results in an * exponential number of function instantiations (increasing compile time and binary size). @@ -229,14 +596,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool /* Support disabling the devirtualization to simplify benchmarking. */ if (enable) { if (varray.is_single()) { - /* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */ - const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()}; + /* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */ + const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()}; func(varray_single); return; } if (varray.is_span()) { - /* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */ - const VArrayForSpan<T> varray_span{varray.get_span()}; + /* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */ + const VArray_For_Span<T> varray_span{varray.get_internal_span()}; func(varray_span); return; } @@ -262,26 +629,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1, const bool is_single1 = varray1.is_single(); const bool is_single2 = varray2.is_single(); if (is_span1 && is_span2) { - const VArrayForSpan<T1> varray1_span{varray1.get_span()}; - const VArrayForSpan<T2> varray2_span{varray2.get_span()}; + const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()}; + const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()}; func(varray1_span, varray2_span); return; } if (is_span1 && is_single2) { - const VArrayForSpan<T1> varray1_span{varray1.get_span()}; - const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()}; + const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()}; + const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()}; func(varray1_span, varray2_single); return; } if (is_single1 && is_span2) { - const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()}; - const VArrayForSpan<T2> varray2_span{varray2.get_span()}; + const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()}; + const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()}; func(varray1_single, varray2_span); return; } if (is_single1 && is_single2) { - const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()}; - const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()}; + const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()}; + const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()}; func(varray1_single, varray2_single); return; } diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c index b5f1d7818cf..786fdd219a3 100644 --- a/source/blender/blenlib/intern/BLI_dial_2d.c +++ b/source/blender/blenlib/intern/BLI_dial_2d.c @@ -78,7 +78,7 @@ float BLI_dial_angle(Dial *dial, const float current_position[2]) cosval = dot_v2v2(current_direction, dial->initial_direction); sinval = cross_v2v2(current_direction, dial->initial_direction); - /* clamp to avoid nans in #acos */ + /* Clamp to avoid NAN's in #acos */ angle = atan2f(sinval, cosval); /* change of sign, we passed the 180 degree threshold. This means we need to add a turn. diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 01cda6c9e4a..bd1fa0fbb8b 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -5393,7 +5393,7 @@ void accumulate_vertex_normals_poly_v3(float **vertnos, void tangent_from_uv_v3(const float uv1[2], const float uv2[2], - const float uv3[3], + const float uv3[2], const float co1[3], const float co2[3], const float co3[3], diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 3558dfad81d..636209883c3 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1744,9 +1744,7 @@ static inline std::pair<int, int> sorted_int_pair(std::pair<int, int> pair) if (pair.first <= pair.second) { return pair; } - else { - return std::pair<int, int>(pair.second, pair.first); - } + return std::pair<int, int>(pair.second, pair.first); } /** diff --git a/source/blender/blenlib/intern/uvproject.c b/source/blender/blenlib/intern/uvproject.c index 329c4d48fe8..093d08e643d 100644 --- a/source/blender/blenlib/intern/uvproject.c +++ b/source/blender/blenlib/intern/uvproject.c @@ -134,7 +134,7 @@ void BLI_uvproject_from_view(float target[2], /* 'rotmat' can be `obedit->obmat` when uv project is used. * 'winx' and 'winy' can be from `scene->r.xsch/ysch` */ -ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float (*rotmat)[4], float winx, float winy) +ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float rotmat[4][4], float winx, float winy) { ProjCameraInfo uci; Camera *camera = ob->data; diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc index f1ae8fb3921..18be456bd20 100644 --- a/source/blender/blenlib/tests/BLI_map_test.cc +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -604,6 +604,42 @@ TEST(map, GenericAlgorithms) EXPECT_EQ(std::count(map.keys().begin(), map.keys().end(), 7), 1); } +TEST(map, AddAsVariadic) +{ + Map<int, StringRef> map; + map.add_as(3, "hello", 2); + map.add_as(2, "test", 1); + EXPECT_EQ(map.lookup(3), "he"); + EXPECT_EQ(map.lookup(2), "t"); +} + +TEST(map, RemoveDuringIteration) +{ + Map<int, int> map; + map.add(2, 1); + map.add(5, 2); + map.add(1, 2); + map.add(6, 0); + map.add(3, 3); + + EXPECT_EQ(map.size(), 5); + + using Iter = Map<int, int>::MutableItemIterator; + Iter begin = map.items().begin(); + Iter end = map.items().end(); + for (Iter iter = begin; iter != end; ++iter) { + Map<int, int>::MutableItem item = *iter; + if (item.value == 2) { + map.remove(iter); + } + } + + EXPECT_EQ(map.size(), 3); + EXPECT_EQ(map.lookup(2), 1); + EXPECT_EQ(map.lookup(6), 0); + EXPECT_EQ(map.lookup(3), 3); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc index f1fcdae3a52..b3108381d78 100644 --- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc +++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc @@ -93,6 +93,15 @@ TEST(stack, Push) EXPECT_EQ(stack.size(), 2); } +TEST(stack, PushAs) +{ + Stack<StringRef> stack; + stack.push_as("hello", 3); + stack.push_as("world", 1); + EXPECT_EQ(stack.pop(), "w"); + EXPECT_EQ(stack.pop(), "hel"); +} + TEST(stack, PushMultiple) { Stack<int> stack; diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc index bbbe96f9b7e..c535ac57774 100644 --- a/source/blender/blenlib/tests/BLI_vector_set_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc @@ -232,4 +232,30 @@ TEST(vector_set, PopExceptions) EXPECT_EQ(set.size(), 4); } +TEST(vector_set, IndexOfOrAdd) +{ + VectorSet<int> set; + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(2), 1); + EXPECT_EQ(set.index_of_or_add(0), 2); + EXPECT_EQ(set.index_of_or_add(2), 1); + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(5), 3); + EXPECT_EQ(set.index_of_or_add(8), 4); + EXPECT_EQ(set.index_of_or_add(5), 3); +} + +TEST(vector_set, Clear) +{ + VectorSet<int> set = {4, 6, 2, 4}; + EXPECT_EQ(set.size(), 3); + set.clear(); + EXPECT_EQ(set.size(), 0); + set.add_multiple({4, 1, 6, 8, 3, 6, 9, 3}); + EXPECT_EQ(set.size(), 6); + set.clear(); + EXPECT_EQ(set.size(), 0); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index 462f13c15ab..e8636168308 100644 --- a/source/blender/blenlib/tests/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -248,6 +248,15 @@ TEST(vector, Append) EXPECT_EQ(vec[2], 7); } +TEST(vector, AppendAs) +{ + Vector<StringRef> vec; + vec.append_as("hello", 2); + vec.append_as("world", 3); + EXPECT_EQ(vec[0], "he"); + EXPECT_EQ(vec[1], "wor"); +} + TEST(vector, AppendAndGetIndex) { Vector<int> vec; diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc index ac25229cd69..a6d2ca10315 100644 --- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc +++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc @@ -1,26 +1,29 @@ /* Apache License, Version 2.0 */ +#include "BLI_array.hh" #include "BLI_strict_flags.h" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" #include "BLI_virtual_array.hh" #include "testing/testing.h" namespace blender::tests { -TEST(virtual_array, ForSpan) +TEST(virtual_array, Span) { std::array<int, 5> data = {3, 4, 5, 6, 7}; - VArrayForSpan<int> varray{data}; + VArray_For_Span<int> varray{data}; EXPECT_EQ(varray.size(), 5); EXPECT_EQ(varray.get(0), 3); EXPECT_EQ(varray.get(4), 7); EXPECT_TRUE(varray.is_span()); EXPECT_FALSE(varray.is_single()); - EXPECT_EQ(varray.get_span().data(), data.data()); + EXPECT_EQ(varray.get_internal_span().data(), data.data()); } -TEST(virtual_array, ForSingle) +TEST(virtual_array, Single) { - VArrayForSingle<int> varray{10, 4}; + VArray_For_Single<int> varray{10, 4}; EXPECT_EQ(varray.size(), 4); EXPECT_EQ(varray.get(0), 10); EXPECT_EQ(varray.get(3), 10); @@ -28,4 +31,124 @@ TEST(virtual_array, ForSingle) EXPECT_TRUE(varray.is_single()); } +TEST(virtual_array, Array) +{ + Array<int> array = {1, 2, 3, 5, 8}; + { + VArray_For_ArrayContainer varray{array}; + EXPECT_EQ(varray.size(), 5); + EXPECT_EQ(varray[0], 1); + EXPECT_EQ(varray[2], 3); + EXPECT_EQ(varray[3], 5); + EXPECT_TRUE(varray.is_span()); + } + { + VArray_For_ArrayContainer varray{std::move(array)}; + EXPECT_EQ(varray.size(), 5); + EXPECT_EQ(varray[0], 1); + EXPECT_EQ(varray[2], 3); + EXPECT_EQ(varray[3], 5); + EXPECT_TRUE(varray.is_span()); + } + { + VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */ + EXPECT_TRUE(varray.is_empty()); + } +} + +TEST(virtual_array, Vector) +{ + Vector<int> vector = {9, 8, 7, 6}; + VArray_For_ArrayContainer varray{std::move(vector)}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 9); + EXPECT_EQ(varray[3], 6); +} + +TEST(virtual_array, StdVector) +{ + std::vector<int> vector = {5, 6, 7, 8}; + VArray_For_ArrayContainer varray{std::move(vector)}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 5); + EXPECT_EQ(varray[1], 6); +} + +TEST(virtual_array, StdArray) +{ + std::array<int, 4> array = {2, 3, 4, 5}; + VArray_For_ArrayContainer varray{array}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 2); + EXPECT_EQ(varray[1], 3); +} + +TEST(virtual_array, VectorSet) +{ + VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1}; + VArray_For_ArrayContainer varray{std::move(vector_set)}; + EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */ + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 5); + EXPECT_EQ(varray[1], 3); + EXPECT_EQ(varray[2], 7); + EXPECT_EQ(varray[3], 1); +} + +TEST(virtual_array, Func) +{ + auto func = [](int64_t index) { return (int)(index * index); }; + VArray_For_Func<int, decltype(func)> varray{10, func}; + EXPECT_EQ(varray.size(), 10); + EXPECT_EQ(varray[0], 0); + EXPECT_EQ(varray[3], 9); + EXPECT_EQ(varray[9], 81); +} + +TEST(virtual_array, AsSpan) +{ + auto func = [](int64_t index) { return (int)(10 * index); }; + VArray_For_Func<int, decltype(func)> func_varray{10, func}; + VArray_Span span_varray{func_varray}; + EXPECT_EQ(span_varray.size(), 10); + Span<int> span = span_varray; + EXPECT_EQ(span.size(), 10); + EXPECT_EQ(span[0], 0); + EXPECT_EQ(span[3], 30); + EXPECT_EQ(span[6], 60); +} + +static int get_x(const std::array<int, 3> &item) +{ + return item[0]; +} + +static void set_x(std::array<int, 3> &item, int value) +{ + item[0] = value; +} + +TEST(virtual_array, DerivedSpan) +{ + Vector<std::array<int, 3>> vector; + vector.append({3, 4, 5}); + vector.append({1, 1, 1}); + { + VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector}; + EXPECT_EQ(varray.size(), 2); + EXPECT_EQ(varray[0], 3); + EXPECT_EQ(varray[1], 1); + } + { + VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector}; + EXPECT_EQ(varray.size(), 2); + EXPECT_EQ(varray[0], 3); + EXPECT_EQ(varray[1], 1); + varray.set(0, 10); + varray.set(1, 20); + EXPECT_EQ(vector[0][0], 10); + EXPECT_EQ(vector[1][0], 20); + } +} + } // namespace blender::tests diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index ee9b9a49768..36802fc8842 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -57,6 +57,7 @@ set(SRC intern/versioning_270.c intern/versioning_280.c intern/versioning_290.c + intern/versioning_300.c intern/versioning_cycles.c intern/versioning_defaults.c intern/versioning_dna.c diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 19ae0014bb8..24501adcb90 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3868,6 +3868,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) blo_do_versions_270(fd, lib, main); blo_do_versions_280(fd, lib, main); blo_do_versions_290(fd, lib, main); + blo_do_versions_300(fd, lib, main); blo_do_versions_cycles(fd, lib, main); /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ @@ -3891,6 +3892,7 @@ static void do_versions_after_linking(Main *main, ReportList *reports) do_versions_after_linking_270(main); do_versions_after_linking_280(main, reports); do_versions_after_linking_290(main, reports); + do_versions_after_linking_300(main, reports); do_versions_after_linking_cycles(main); main->is_locked_for_linking = false; diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 9682b5456d2..d1d4e0b3256 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -210,6 +210,7 @@ void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main * void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *bmain); void blo_do_versions_280(struct FileData *fd, struct Library *lib, struct Main *bmain); void blo_do_versions_290(struct FileData *fd, struct Library *lib, struct Main *bmain); +void blo_do_versions_300(struct FileData *fd, struct Library *lib, struct Main *bmain); void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Main *bmain); void do_versions_after_linking_250(struct Main *bmain); @@ -217,6 +218,7 @@ void do_versions_after_linking_260(struct Main *bmain); void do_versions_after_linking_270(struct Main *bmain); void do_versions_after_linking_280(struct Main *bmain, struct ReportList *reports); void do_versions_after_linking_290(struct Main *bmain, struct ReportList *reports); +void do_versions_after_linking_300(struct Main *bmain, struct ReportList *reports); void do_versions_after_linking_cycles(struct Main *bmain); /* This is rather unfortunate to have to expose this here, but better use that nasty hack in diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 97fe9fb04c4..2b6f44c694b 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -2069,19 +2069,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Set default value for the new bisect_threshold parameter in the mirror modifier. */ - if (!MAIN_VERSION_ATLEAST(bmain, 293, 19)) { - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->type == eModifierType_Mirror) { - MirrorModifierData *mmd = (MirrorModifierData *)md; - /* This was the previous hard-coded value. */ - mmd->bisect_threshold = 0.001f; - } - } - } - } - /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c new file mode 100644 index 00000000000..182231b5878 --- /dev/null +++ b/source/blender/blenloader/intern/versioning_300.c @@ -0,0 +1,99 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup blenloader + */ +/* allow readfile to use deprecated functionality */ +#define DNA_DEPRECATED_ALLOW + +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "DNA_brush_types.h" +#include "DNA_genfile.h" +#include "DNA_modifier_types.h" +#include "DNA_text_types.h" + +#include "BKE_lib_id.h" +#include "BKE_main.h" + +#include "BLO_readfile.h" +#include "readfile.h" + +void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) +{ + if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { + /* Set zero user text objects to have a fake user. */ + LISTBASE_FOREACH (Text *, text, &bmain->texts) { + if (text->id.us == 0) { + id_fake_user_set(&text->id); + } + } + } + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_300 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } +} + +/* NOLINTNEXTLINE: readability-function-size */ +void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) +{ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) { + /* Set default value for the new bisect_threshold parameter in the mirror modifier. */ + if (!DNA_struct_elem_find(fd->filesdna, "MirrorModifierData", "float", "bisect_threshold")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + /* This was the previous hard-coded value. */ + mmd->bisect_threshold = 0.001f; + } + } + } + } + /* Grease Pencil: Set default value for dilate pixels. */ + if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "int", "dilate_pixels")) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->gpencil_settings) { + brush->gpencil_settings->dilate_pixels = 1; + } + } + } + } + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } +} diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index dcf9717465c..0754564fa47 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -106,7 +106,7 @@ static void normalize_v2_m3_v3v3(float out[2], /** * \note Be sure to update #bm_face_split_edgenet_find_loop_pair_exists - * when making changed to edge picking logic. + * when making changes to edge picking logic. */ static bool bm_face_split_edgenet_find_loop_pair(BMVert *v_init, const float face_normal[3], @@ -456,8 +456,8 @@ static bool bm_face_split_edgenet_find_loop(BMVert *v_init, * Splits a face into many smaller faces defined by an edge-net. * handle customdata and degenerate cases. * - * - isolated holes or unsupported face configurations, will be ignored. - * - customdata calculations aren't efficient + * - Isolated holes or unsupported face configurations, will be ignored. + * - Customdata calculations aren't efficient * (need to calculate weights for each vert). */ bool BM_face_split_edgenet(BMesh *bm, @@ -593,7 +593,7 @@ bool BM_face_split_edgenet(BMesh *bm, BMIter iter; BMLoop *l_other; - /* see: #BM_loop_interp_from_face for similar logic */ + /* See: #BM_loop_interp_from_face for similar logic. */ void **blocks = BLI_array_alloca(blocks, f->len); float(*cos_2d)[2] = BLI_array_alloca(cos_2d, f->len); float *w = BLI_array_alloca(w, f->len); @@ -1064,7 +1064,7 @@ static int bm_face_split_edgenet_find_connection(const struct EdgeGroup_FindConn #ifdef USE_PARTIAL_CONNECT /** - * Used to identify edges that get split off when making island from partial connection. + * Used to identify edges that get split off when making island from partial connection. * fptr should be a BMFace*, but is a void* for general interface to BM_vert_separate_tested_edges */ static bool test_tagged_and_notface(BMEdge *e, void *fptr) @@ -1211,7 +1211,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap, const int v_a_index, const int v_b_index) { - /* connected to eachother */ + /* Connected to each other. */ if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) { return true; } @@ -1226,7 +1226,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap, * \param use_partial_connect: Support for handling islands connected by only a single edge, * \note that this is quite slow so avoid using where possible. * \param mem_arena: Avoids many small allocs & should be cleared after each use. - * take care since \a r_edge_net_new is stored in \a r_edge_net_new. + * take care since \a edge_net_new is stored in \a r_edge_net_new. */ bool BM_face_split_edgenet_connect_islands(BMesh *bm, BMFace *f, @@ -1246,7 +1246,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm, * * Keep the first part fast since it will run very often for edge-nets that have no holes. * - * \note Don't use the mem_arena unless he have holes to fill. + * \note Don't use the mem_arena unless we have holes to fill. * (avoid thrashing the area when the initial check isn't so intensive on the stack). */ @@ -1572,7 +1572,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm, if (g->has_prev_edge == false) { BMVert *v_origin = g->vert_span.min; - + /* Index of BMVert for the edge group connection with `v_origin`. */ const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, false); // BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len); @@ -1598,7 +1598,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm, { BMVert *v_origin = g->vert_span.max; - + /* Index of BMVert for the edge group connection with `v_origin`. */ const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, true); // BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len); @@ -1660,7 +1660,7 @@ finally: struct TempVertPair *tvp = temp_vert_pairs.list; do { /* its _very_ unlikely the edge exists, - * however splicing may case this. see: T48012 */ + * however splicing may cause this. see: T48012 */ if (!BM_edge_exists(tvp->v_orig, tvp->v_temp)) { BM_vert_splice(bm, tvp->v_orig, tvp->v_temp); } diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c index f840a3770ff..e7d0fe6a0c6 100644 --- a/source/blender/bmesh/tools/bmesh_bisect_plane.c +++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c @@ -39,12 +39,13 @@ #include "BLI_utildefines_stack.h" #include "bmesh.h" -#include "bmesh_bisect_plane.h" /* own include */ +#include "bmesh_bisect_plane.h" /* Own include. */ -#include "BLI_strict_flags.h" /* keep last */ +#include "BLI_strict_flags.h" /* Keep last. */ /* -------------------------------------------------------------------- */ -/* Math utils */ +/** \name Math Functions + * \{ */ static short plane_point_test_v3(const float plane[4], const float co[3], @@ -63,10 +64,15 @@ static short plane_point_test_v3(const float plane[4], return 0; } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Wrappers to hide internal data-structure abuse, +/** \name BMesh Element Accessors + * + * Wrappers to hide internal data-structure abuse, * later we may want to move this into some hash lookup - * to a separate struct, but for now we can store in BMesh data */ + * to a separate struct, but for now we can store in #BMesh data. + * \{ */ #define BM_VERT_DIR(v) ((short *)(&(v)->head.index))[0] /* Direction -1/0/1 */ #define BM_VERT_SKIP(v) ((short *)(&(v)->head.index))[1] /* Skip Vert 0/1 */ @@ -75,12 +81,16 @@ static short plane_point_test_v3(const float plane[4], #define BM_VERT_LOOPINDEX(v) /* The verts index within a face (temp var) */ \ (*((uint *)(&(v)->no[2]))) -/** +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name BMesh Flag Accessors + * * Hide flag access - * (for more readable code since same flag is used differently for vert/edgeface)... + * (for more readable code since same flag is used differently for vert/edge-face). */ -/* enable when vertex is in the center and its faces have been added to the stack */ +/** Enable when vertex is in the center and its faces have been added to the stack. */ BLI_INLINE void vert_is_center_enable(BMVert *v) { BM_elem_flag_enable(v, BM_ELEM_TAG); @@ -100,7 +110,7 @@ BLI_INLINE bool vert_pair_adjacent_in_orig_face(BMVert *v_a, BMVert *v_b, const return ELEM(delta, 1, (uint)(f_len_orig - 1)); } -/* enable when the edge can be cut */ +/** Enable when the edge can be cut. */ BLI_INLINE void edge_is_cut_enable(BMEdge *e) { BM_elem_flag_enable(e, BM_ELEM_TAG); @@ -114,7 +124,7 @@ BLI_INLINE bool edge_is_cut_test(BMEdge *e) return (BM_elem_flag_test(e, BM_ELEM_TAG) != 0); } -/* enable when the faces are added to the stack */ +/** Enable when the faces are added to the stack. */ BLI_INLINE void face_in_stack_enable(BMFace *f) { BM_elem_flag_disable(f, BM_ELEM_TAG); @@ -128,8 +138,11 @@ BLI_INLINE bool face_in_stack_test(BMFace *f) return (BM_elem_flag_test(f, BM_ELEM_TAG) == 0); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* BMesh utils */ +/** \name BMesh Face Bisect + * \{ */ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v) { @@ -148,7 +161,7 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v) static void bm_face_bisect_verts( BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new) { - /* unlikely more than 2 verts are needed */ + /* Unlikely more than 2 verts are needed. */ const uint f_len_orig = (uint)f->len; BMVert **vert_split_arr = BLI_array_alloca(vert_split_arr, f_len_orig); STACK_DECLARE(vert_split_arr); @@ -163,16 +176,14 @@ static void bm_face_bisect_verts( l_first = BM_FACE_FIRST_LOOP(f); - /* add plane-aligned verts to the stack - * and check we have verts from both sides in this face, - * ... that the face doesn't only have boundary verts on the plane for eg. */ + /* Add plane-aligned verts to the stack and check we have verts from both sides in this face + * (that the face doesn't only have boundary verts on the plane for eg). */ l_iter = l_first; do { if (vert_is_center_test(l_iter->v)) { BLI_assert(BM_VERT_DIR(l_iter->v) == 0); - /* if both are -1 or 1, or both are zero: - * don't flip 'inside' var while walking */ + /* If both are -1 or 1, or both are zero: don't flip 'inside' var while walking. */ BM_VERT_SKIP(l_iter->v) = (((BM_VERT_DIR(l_iter->prev->v) ^ BM_VERT_DIR(l_iter->next->v))) == 0); @@ -195,7 +206,7 @@ static void bm_face_bisect_verts( l_a = BM_face_vert_share_loop(f, vert_split_arr[0]); l_b = BM_face_vert_share_loop(f, vert_split_arr[1]); - /* common case, just cut the face once */ + /* Common case, just cut the face once. */ BM_face_split(bm, f, l_a, l_b, &l_new, NULL, true); if (l_new) { if (oflag_center | oflag_new) { @@ -207,6 +218,7 @@ static void bm_face_bisect_verts( } } else { + /* Less common case, _complicated_ we need to calculate how to do multiple cuts. */ uint i = 0; @@ -263,7 +275,6 @@ static void bm_face_bisect_verts( } while ((l_iter = l_iter->next) != l_first_non_center); } - /* less common case, _complicated_ we need to calculate how to do multiple cuts */ float(*face_verts_proj_2d)[2] = BLI_array_alloca(face_verts_proj_2d, f_len_orig); float axis_mat[3][3]; @@ -275,10 +286,8 @@ static void bm_face_bisect_verts( /* ---- */ /* Calculate the direction to sort verts in the face intersecting the plane */ - /* exact dir isn't so important, - * just need a dir for sorting verts across face, - * 'sort_dir' could be flipped either way, it not important, we only need to order the array - */ + /* The exact direction isn't important, vertices just need to be sorted across the face. + * (`sort_dir` could be flipped either way). */ cross_v3_v3v3(sort_dir, f->no, plane); if (UNLIKELY(normalize_v3(sort_dir) == 0.0f)) { /* find any 2 verts and get their direction */ @@ -289,8 +298,8 @@ static void bm_face_bisect_verts( } } if (UNLIKELY(i == STACK_SIZE(vert_split_arr))) { - /* ok, we can't do anything useful here, - * face has no area or so, bail out, this is highly unlikely but not impossible */ + /* Ok, we can't do anything useful here, + * face has no area or so, bail out, this is highly unlikely but not impossible. */ goto finally; } } @@ -298,7 +307,7 @@ static void bm_face_bisect_verts( /* ---- */ /* Calculate 2d coords to use for intersection checks */ - /* get the faces 2d coords */ + /* Get the faces 2d coords. */ BLI_assert(BM_face_is_normal_valid(f)); axis_dominant_v3_to_m3(axis_mat, f->no); @@ -310,7 +319,7 @@ static void bm_face_bisect_verts( } while ((l_iter = l_iter->next) != l_first); /* ---- */ - /* Sort the verts across the face from one side to another */ + /* Sort the verts across the face from one side to another. */ for (i = 0; i < STACK_SIZE(vert_split_arr); i++) { BMVert *v = vert_split_arr[i]; BM_VERT_SORTVAL(v) = dot_v3v3(sort_dir, v->co); @@ -320,9 +329,9 @@ static void bm_face_bisect_verts( vert_split_arr, STACK_SIZE(vert_split_arr), sizeof(*vert_split_arr), bm_vert_sortval_cb); /* ---- */ - /* Split the face across sorted splits */ + /* Split the face across sorted splits. */ - /* note: we don't know which face gets which splits, + /* NOTE: we don't know which face gets which splits, * so at the moment we have to search all faces for the vert pair, * while not all that nice, typically there are < 5 resulting faces, * so its not _that_ bad. */ @@ -350,8 +359,8 @@ static void bm_face_bisect_verts( uint j; for (j = 0; j < STACK_SIZE(face_split_arr); j++) { - /* would be nice to avoid loop lookup here, - * but we need to know which face the verts are in */ + /* It would be nice to avoid loop lookup here, + * but we need to know which face the verts are in. */ if ((l_a = BM_face_vert_share_loop(face_split_arr[j], v_a)) && (l_b = BM_face_vert_share_loop(face_split_arr[j], v_b))) { found = true; @@ -359,11 +368,10 @@ static void bm_face_bisect_verts( } } - /* ideally wont happen, but it can for self intersecting faces */ + /* Ideally wont happen, but it can for self intersecting faces. */ // BLI_assert(found == true); - /* in fact this simple test is good enough, - * test if the loops are adjacent */ + /* In fact this simple test is good enough, test if the loops are adjacent. */ if (found && !BM_loop_is_adjacent(l_a, l_b)) { BMLoop *l_new; BMFace *f_tmp; @@ -397,8 +405,11 @@ finally: (void)vert_split_arr; } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Main logic */ +/** \name Public BMesh Bisect Function + * \{ */ /** * \param use_snap_center: Snap verts onto the plane. @@ -425,25 +436,25 @@ void BM_mesh_bisect_plane(BMesh *bm, BMIter iter; if (use_tag) { - /* build tagged edge array */ + /* Build tagged edge array. */ BMEdge *e; einput_len = 0; - /* flush edge tags to verts */ + /* Flush edge tags to verts. */ BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - /* keep face tags as is */ + /* Keep face tags as is. */ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { if (edge_is_cut_test(e)) { edges_arr[einput_len++] = e; - /* flush edge tags to verts */ + /* Flush edge tags to verts. */ BM_elem_flag_enable(e->v1, BM_ELEM_TAG); BM_elem_flag_enable(e->v2, BM_ELEM_TAG); } } - /* face tags are set by caller */ + /* Face tags are set by caller. */ } else { BMEdge *e; @@ -463,7 +474,7 @@ void BM_mesh_bisect_plane(BMesh *bm, if (use_tag && !BM_elem_flag_test(v, BM_ELEM_TAG)) { vert_is_center_disable(v); - /* these should never be accessed */ + /* These should never be accessed. */ BM_VERT_DIR(v) = 0; BM_VERT_DIST(v) = 0.0f; @@ -483,11 +494,11 @@ void BM_mesh_bisect_plane(BMesh *bm, } } - /* store a stack of faces to be evaluated for splitting */ + /* Store a stack of faces to be evaluated for splitting. */ BLI_LINKSTACK_INIT(face_stack); for (i = 0; i < einput_len; i++) { - /* we could check edge_is_cut_test(e) but there is no point */ + /* We could check `edge_is_cut_test(e)` but there is no point. */ BMEdge *e = edges_arr[i]; const int side[2] = {BM_VERT_DIR(e->v1), BM_VERT_DIR(e->v2)}; const float dist[2] = {BM_VERT_DIST(e->v1), BM_VERT_DIST(e->v2)}; @@ -524,8 +535,8 @@ void BM_mesh_bisect_plane(BMesh *bm, BM_VERT_DIST(v_new) = 0.0f; } else if (side[0] == 0 || side[1] == 0) { - /* check if either edge verts are aligned, - * if so - tag and push all faces that use it into the stack */ + /* Check if either edge verts are aligned, + * if so - tag and push all faces that use it into the stack. */ uint j; BM_ITER_ELEM_INDEX (v, &iter, e, BM_VERTS_OF_EDGE, j) { if (side[j] == 0) { @@ -545,7 +556,7 @@ void BM_mesh_bisect_plane(BMesh *bm, } } - /* if both verts are on the center - tag it */ + /* If both verts are on the center - tag it. */ if (oflag_center) { if (side[0] == 0 && side[1] == 0) { BMO_edge_flag_enable(bm, e, oflag_center); @@ -560,9 +571,11 @@ void BM_mesh_bisect_plane(BMesh *bm, bm_face_bisect_verts(bm, f, plane, oflag_center, oflag_new); } - /* Caused by access macros: BM_VERT_DIR, BM_VERT_SKIP. */ + /* Caused by access macros: #BM_VERT_DIR, #BM_VERT_SKIP. */ bm->elem_index_dirty |= BM_VERT; - /* now we have all faces to split in the stack */ + /* Now we have all faces to split in the stack. */ BLI_LINKSTACK_FREE(face_stack); } + +/** \} */ diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index c5ff2b3adc6..ef889807030 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -55,7 +55,6 @@ constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType // configurable items // chunk size determination -// #define COM_DEBUG // chunk order /** diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 648ef7519d1..dfb4f53fee5 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -42,8 +42,6 @@ extern "C" { namespace blender::compositor { -#ifdef COM_DEBUG - int DebugInfo::m_file_index = 0; DebugInfo::NodeNameMap DebugInfo::m_node_names; DebugInfo::OpNameMap DebugInfo::m_op_names; @@ -69,50 +67,6 @@ std::string DebugInfo::operation_name(const NodeOperation *op) return ""; } -void DebugInfo::convert_started() -{ - m_op_names.clear(); -} - -void DebugInfo::execute_started(const ExecutionSystem *system) -{ - m_file_index = 1; - m_group_states.clear(); - for (ExecutionGroup *execution_group : system->m_groups) { - m_group_states[execution_group] = EG_WAIT; - } -} - -void DebugInfo::node_added(const Node *node) -{ - m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : ""); -} - -void DebugInfo::node_to_operations(const Node *node) -{ - m_current_node_name = m_node_names[node]; -} - -void DebugInfo::operation_added(const NodeOperation *operation) -{ - m_op_names[operation] = m_current_node_name; -} - -void DebugInfo::operation_read_write_buffer(const NodeOperation *operation) -{ - m_current_op_name = m_op_names[operation]; -} - -void DebugInfo::execution_group_started(const ExecutionGroup *group) -{ - m_group_states[group] = EG_RUNNING; -} - -void DebugInfo::execution_group_finished(const ExecutionGroup *group) -{ - m_group_states[group] = EG_FINISHED; -} - int DebugInfo::graphviz_operation(const ExecutionSystem *system, NodeOperation *operation, const ExecutionGroup *group, @@ -442,6 +396,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma void DebugInfo::graphviz(const ExecutionSystem *system) { + if (!COM_EXPORT_GRAPHVIZ) { + return; + } char str[1000000]; if (graphviz_system(system, str, sizeof(str) - 1)) { char basename[FILE_MAX]; @@ -459,44 +416,4 @@ void DebugInfo::graphviz(const ExecutionSystem *system) } } -#else - -std::string DebugInfo::node_name(const Node * /*node*/) -{ - return ""; -} -std::string DebugInfo::operation_name(const NodeOperation * /*op*/) -{ - return ""; -} -void DebugInfo::convert_started() -{ -} -void DebugInfo::execute_started(const ExecutionSystem * /*system*/) -{ -} -void DebugInfo::node_added(const Node * /*node*/) -{ -} -void DebugInfo::node_to_operations(const Node * /*node*/) -{ -} -void DebugInfo::operation_added(const NodeOperation * /*operation*/) -{ -} -void DebugInfo::operation_read_write_buffer(const NodeOperation * /*operation*/) -{ -} -void DebugInfo::execution_group_started(const ExecutionGroup * /*group*/) -{ -} -void DebugInfo::execution_group_finished(const ExecutionGroup * /*group*/) -{ -} -void DebugInfo::graphviz(const ExecutionSystem * /*system*/) -{ -} - -#endif - } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index bf7b981fbd5..e1aea69e481 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -21,11 +21,13 @@ #include <map> #include <string> +#include "COM_ExecutionSystem.h" #include "COM_NodeOperation.h" #include "COM_defines.h" namespace blender::compositor { +static constexpr bool COM_EXPORT_GRAPHVIZ = false; class Node; class ExecutionSystem; class ExecutionGroup; @@ -41,20 +43,81 @@ class DebugInfo { static std::string node_name(const Node *node); static std::string operation_name(const NodeOperation *op); - static void convert_started(); - static void execute_started(const ExecutionSystem *system); + private: + static int m_file_index; + /** Map nodes to usable names for debug output. */ + static NodeNameMap m_node_names; + /** Map operations to usable names for debug output. */ + static OpNameMap m_op_names; + /** Base name for all operations added by a node. */ + static std::string m_current_node_name; + /** Base name for automatic sub-operations. */ + static std::string m_current_op_name; + /** For visualizing group states. */ + static GroupStateMap m_group_states; + + public: + static void convert_started() + { + if (COM_EXPORT_GRAPHVIZ) { + m_op_names.clear(); + } + } + + static void execute_started(const ExecutionSystem *system) + { + if (COM_EXPORT_GRAPHVIZ) { + m_file_index = 1; + m_group_states.clear(); + for (ExecutionGroup *execution_group : system->m_groups) { + m_group_states[execution_group] = EG_WAIT; + } + } + }; + + static void node_added(const Node *node) + { + if (COM_EXPORT_GRAPHVIZ) { + m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : ""); + } + } - static void node_added(const Node *node); - static void node_to_operations(const Node *node); - static void operation_added(const NodeOperation *operation); - static void operation_read_write_buffer(const NodeOperation *operation); + static void node_to_operations(const Node *node) + { + if (COM_EXPORT_GRAPHVIZ) { + m_current_node_name = m_node_names[node]; + } + } - static void execution_group_started(const ExecutionGroup *group); - static void execution_group_finished(const ExecutionGroup *group); + static void operation_added(const NodeOperation *operation) + { + if (COM_EXPORT_GRAPHVIZ) { + m_op_names[operation] = m_current_node_name; + } + }; + + static void operation_read_write_buffer(const NodeOperation *operation) + { + if (COM_EXPORT_GRAPHVIZ) { + m_current_op_name = m_op_names[operation]; + } + }; + + static void execution_group_started(const ExecutionGroup *group) + { + if (COM_EXPORT_GRAPHVIZ) { + m_group_states[group] = EG_RUNNING; + } + }; + static void execution_group_finished(const ExecutionGroup *group) + { + if (COM_EXPORT_GRAPHVIZ) { + m_group_states[group] = EG_FINISHED; + } + }; static void graphviz(const ExecutionSystem *system); -#ifdef COM_DEBUG protected: static int graphviz_operation(const ExecutionSystem *system, NodeOperation *operation, @@ -68,20 +131,6 @@ class DebugInfo { const char *name, const char *color, const char *style, char *str, int maxlen); static int graphviz_legend(char *str, int maxlen); static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen); - - private: - static int m_file_index; - /** Map nodes to usable names for debug output. */ - static NodeNameMap m_node_names; - /** Map operations to usable names for debug output. */ - static OpNameMap m_op_names; - /** Base name for all operations added by a node. */ - static std::string m_current_node_name; - /** Base name for automatic sub-operations. */ - static std::string m_current_op_name; - /** For visualizing group states. */ - static GroupStateMap m_group_states; -#endif }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index ee3a6dedd44..d578ac24a4a 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -263,10 +263,10 @@ static void opencl_initialize(const bool use_opencl) if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2)); } - g_work_scheduler.opencl.devices.append(OpenCLDevice(g_work_scheduler.opencl.context, - device, - g_work_scheduler.opencl.program, - vendorID)); + g_work_scheduler.opencl.devices.append_as(g_work_scheduler.opencl.context, + device, + g_work_scheduler.opencl.program, + vendorID); } } MEM_freeN(cldevices); @@ -368,7 +368,7 @@ static void threading_model_queue_initialize(const int num_cpu_threads) /* Initialize CPU threads. */ if (!g_work_scheduler.queue.initialized) { for (int index = 0; index < num_cpu_threads; index++) { - g_work_scheduler.queue.devices.append(CPUDevice(index)); + g_work_scheduler.queue.devices.append_as(index); } BLI_thread_local_create(g_thread_device); g_work_scheduler.queue.initialized = true; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index 13caba67713..00c78b8edce 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -169,7 +169,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible) } /* Speed optimization for animation lookups. */ if (object->pose != nullptr) { - BKE_pose_channels_hash_make(object->pose); + BKE_pose_channels_hash_ensure(object->pose); if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(object->pose); } @@ -318,7 +318,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object, bool is_object_visibl /* Armature. */ build_armature(armature); /* speed optimization for animation lookups */ - BKE_pose_channels_hash_make(object->pose); + BKE_pose_channels_hash_ensure(object->pose); if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(object->pose); } diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index 0b66141e51a..21d55357a2a 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -424,22 +424,26 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx GPUShader *sh = GPENCIL_shader_fx_glow_get(); - float ref_col[3]; + float ref_col[4]; if (fx->mode == eShaderFxGlowMode_Luminance) { + /* Only pass in the first value for luminance. */ ref_col[0] = fx->threshold; ref_col[1] = -1.0f; ref_col[2] = -1.0f; + ref_col[3] = -1.0f; } else { + /* First three values are the RGB for the selected color, last value the threshold. */ copy_v3_v3(ref_col, fx->select_color); + ref_col[3] = fx->threshold; } DRWState state = DRW_STATE_WRITE_COLOR; grp = gpencil_vfx_pass_create("Fx Glow H", state, iter, sh); DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){fx->blur[0] * c, fx->blur[0] * s}); DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); - DRW_shgroup_uniform_vec3_copy(grp, "threshold", ref_col); + DRW_shgroup_uniform_vec4_copy(grp, "threshold", ref_col); DRW_shgroup_uniform_vec4_copy(grp, "glowColor", fx->glow_color); DRW_shgroup_uniform_bool_copy(grp, "glowUnder", use_glow_under); DRW_shgroup_uniform_bool_copy(grp, "firstPass", true); @@ -473,7 +477,7 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx grp = gpencil_vfx_pass_create("Fx Glow V", state, iter, sh); DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-fx->blur[1] * s, fx->blur[1] * c}); DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); - DRW_shgroup_uniform_vec3_copy(grp, "threshold", (float[3]){-1.0f, -1.0f, -1.0f}); + DRW_shgroup_uniform_vec4_copy(grp, "threshold", (float[4]){-1.0f, -1.0f, -1.0f, -1.0}); DRW_shgroup_uniform_vec4_copy(grp, "glowColor", (float[4]){1.0f, 1.0f, 1.0f, fx->glow_color[3]}); DRW_shgroup_uniform_bool_copy(grp, "firstPass", false); DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->blend_mode); diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl index bb905f8694b..269ed49c4d0 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl @@ -145,7 +145,7 @@ void main() uniform vec4 glowColor; uniform vec2 offset; uniform int sampCount; -uniform vec3 threshold; +uniform vec4 threshold; uniform bool firstPass; uniform bool glowUnder; uniform int blendMode; @@ -168,7 +168,7 @@ void main() vec3 rev = texture(revealBuf, uv).rgb; if (threshold.x > -1.0) { if (threshold.y > -1.0) { - if (any(greaterThan(abs(col - threshold), vec3(0.05)))) { + if (any(greaterThan(abs(col - vec3(threshold)), vec3(threshold.w)))) { weight = 0.0; } } diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index c07271a0d33..49b5e0fecd3 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -348,7 +348,14 @@ static void gpencil_buffer_add_stroke(gpStrokeVert *verts, } /* Draw line to first point to complete the loop for cyclic strokes. */ if (is_cyclic) { - gpencil_buffer_add_point(verts, cols, gps, &pts[0], v++, false); + gpencil_buffer_add_point(verts, cols, gps, &pts[0], v, false); + /* UV factor needs to be adjusted for the last point to not be equal to the UV factor of the + * first point. It should be the factor of the last point plus the distance from the last point + * to the first. + */ + gpStrokeVert *vert = &verts[v]; + vert->u_stroke = verts[v - 1].u_stroke + len_v3v3(&pts[pts_len - 1].x, &pts[0].x); + v++; } /* Last adjacency point (not drawn). */ adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2); diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 68fff1091af..3902f6613a1 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -339,7 +339,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) } BKE_pose_channels_hash_free(ob->pose); - BKE_pose_channels_hash_make(ob->pose); + BKE_pose_channels_hash_ensure(ob->pose); GHash *name_map = BLI_ghash_str_new(__func__); @@ -390,7 +390,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone, bConstraint *curcon; ListBase *conlist; - if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) { + if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name))) { if ((conlist = &pchan->constraints)) { for (curcon = conlist->first; curcon; curcon = curcon->next) { /* does this constraint have a subtarget in @@ -825,7 +825,7 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig bConstraint *curcon; ListBase *conlist; - if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL || + if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name)) == NULL || (conlist = &pchan->constraints) == NULL) { return; } @@ -855,7 +855,7 @@ static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Obj return; } bPoseChannel *pchan; - pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name); + pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name); if (pchan->custom != NULL) { Main *bmain = CTX_data_main(C); @@ -885,12 +885,12 @@ static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, O if (src_ob->pose) { bPoseChannel *chanold, *channew; - chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name); + chanold = BKE_pose_channel_ensure(src_ob->pose, src_bone->name); if (chanold) { /* WARNING: this creates a new posechannel, but there will not be an attached bone * yet as the new bones created here are still 'EditBones' not 'Bones'. */ - channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name); + channew = BKE_pose_channel_ensure(dst_ob->pose, dst_bone->name); if (channew) { BKE_pose_channel_copy_data(channew, chanold); @@ -1193,7 +1193,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) * is synchronized. */ bPoseChannel *pchan; /* Make sure we clean up the old data before overwriting it */ - pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name); + pchan = BKE_pose_channel_ensure(obedit->pose, ebone_iter->temp.ebone->name); BKE_pose_channel_free(pchan); /* Sync pchan data */ copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 767d822aa39..535ccaa06fd 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5576,7 +5576,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL, - .use_object_edit_cage = false, + .edit_mode_type = SNAP_GEOM_FINAL, }, mval, NULL, diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 364444f99ae..68322ed56af 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -292,7 +292,7 @@ static int gizmo_move_modal(bContext *C, (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE), &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, .use_occlusion_test = true, }, mval_fl, diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 3cff07dde3f..b2d3a2e1576 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -68,7 +68,9 @@ typedef struct SnapGizmo3D { struct { int x; int y; +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK short shift, ctrl, alt, oskey; +#endif } last_eventstate; #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK @@ -99,28 +101,30 @@ static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManage if (wm && wm->winactive) { const wmEvent *event = wm->winactive->eventstate; if ((event->x != snap_gizmo->last_eventstate.x) || - (event->y != snap_gizmo->last_eventstate.y) || - (event->ctrl != snap_gizmo->last_eventstate.ctrl) || - (event->shift != snap_gizmo->last_eventstate.shift) || - (event->alt != snap_gizmo->last_eventstate.alt) || - (event->oskey != snap_gizmo->last_eventstate.oskey)) { + (event->y != snap_gizmo->last_eventstate.y)) { return true; } +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { + if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) || + (event->shift != snap_gizmo->last_eventstate.shift) || + (event->alt != snap_gizmo->last_eventstate.alt) || + (event->oskey != snap_gizmo->last_eventstate.oskey)) { + return true; + } + } +#endif } return false; } /* Copies the current eventstate. */ -static void eventstate_save(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) +static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) { if (wm && wm->winactive) { const wmEvent *event = wm->winactive->eventstate; snap_gizmo->last_eventstate.x = event->x; snap_gizmo->last_eventstate.y = event->y; - snap_gizmo->last_eventstate.ctrl = event->ctrl; - snap_gizmo->last_eventstate.shift = event->shift; - snap_gizmo->last_eventstate.alt = event->alt; - snap_gizmo->last_eventstate.oskey = event->oskey; } } @@ -140,6 +144,12 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) return snap_gizmo->invert_snap; } + /* Save new eventstate. */ + snap_gizmo->last_eventstate.ctrl = event->ctrl; + snap_gizmo->last_eventstate.shift = event->shift; + snap_gizmo->last_eventstate.alt = event->alt; + snap_gizmo->last_eventstate.oskey = event->oskey; + const int snap_on = snap_gizmo->snap_on; wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap); @@ -328,23 +338,17 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz, Scene *scene = DEG_get_input_scene(depsgraph); #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if ((snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE) == 0) { - bool invert_snap_toggle = invert_snap(snap_gizmo, wm); - if (invert_snap_toggle != snap_gizmo->invert_snap) { - snap_gizmo->invert_snap = invert_snap_toggle; - - /* Status has changed, be sure to save before early return. */ - eventstate_save(snap_gizmo, wm); - } + if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { + snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm); const ToolSettings *ts = scene->toolsettings; - if (invert_snap_toggle != !(ts->snap_flag & SCE_SNAP)) { + if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) { snap_gizmo->snap_elem = 0; return 0; } } #endif - eventstate_save(snap_gizmo, wm); + eventstate_save_xy(snap_gizmo, wm); snap_gizmo->is_enabled = true; @@ -364,25 +368,39 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz, snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; } + eSnapSelect snap_select = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_ONLY_ACTIVE) ? + SNAP_ONLY_ACTIVE : + SNAP_ALL; + + eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ? + SNAP_GEOM_FINAL : + (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ? + SNAP_GEOM_CAGE : + SNAP_GEOM_EDIT; + + bool use_occlusion_test = (snap_gizmo->flag & ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE) ? false : + true; + float dist_px = 12.0f * U.pixelsize; ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, gz); - snap_elem = ED_transform_snap_object_project_view3d_ex(snap_gizmo->snap_context_v3d, - depsgraph, - snap_elements, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - .use_object_edit_cage = true, - .use_occlusion_test = true, - }, - mval_fl, - prev_co, - &dist_px, - co, - no, - &index, - NULL, - NULL); + snap_elem = ED_transform_snap_object_project_view3d_ex( + snap_gizmo->snap_context_v3d, + depsgraph, + snap_elements, + &(const struct SnapObjectParams){ + .snap_select = snap_select, + .edit_mode_type = edit_mode_type, + .use_occlusion_test = use_occlusion_test, + }, + mval_fl, + prev_co, + &dist_px, + co, + no, + &index, + NULL, + NULL); } if (snap_elem == 0) { @@ -596,8 +614,8 @@ static void snap_gizmo_draw(const bContext *C, wmGizmo *gz) GPU_line_width(1.0f); - const float *prev_point = snap_gizmo_snap_elements(snap_gizmo) & - SCE_SNAP_MODE_EDGE_PERPENDICULAR ? + const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) & + SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? snap_gizmo->prevpoint : NULL; diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 47ae90acb74..f7ab72a8a43 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -36,6 +36,7 @@ set(SRC annotate_paint.c drawgpencil.c editaction_gpencil.c + gpencil_add_blank.c gpencil_add_lineart.c gpencil_add_monkey.c gpencil_add_stroke.c diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c new file mode 100644 index 00000000000..3bd61d5c88a --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_add_blank.c @@ -0,0 +1,101 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup edgpencil + */ + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" + +#include "ED_gpencil.h" + +/* Definition of the most important info from a color */ +typedef struct ColorTemplate { + const char *name; + float line[4]; + float fill[4]; +} ColorTemplate; + +/* Add color an ensure duplications (matched by name) */ +static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct) +{ + int index; + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); + + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba); + + return index; +} + +/* ***************************************************************** */ +/* Stroke Geometry */ + +/* ***************************************************************** */ +/* Color Data */ + +static const ColorTemplate gp_stroke_material_black = { + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, +}; + +/* ***************************************************************** */ +/* Blank API */ + +/* Add a Simple empty object with one layer and one color. */ +void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4])) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + bGPdata *gpd = (bGPdata *)ob->data; + + /* create colors */ + int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black); + + /* set first color as active and in brushes */ + ob->actcol = color_black + 1; + + /* layers */ + bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true); + + /* frames */ + BKE_gpencil_frame_addnew(layer, CFRA); + + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; +} diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index b269fd84d5f..c93fcb9eb8c 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -3745,3 +3745,51 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot) ot->exec = gpencil_layer_mask_remove_exec; ot->poll = gpencil_active_layer_poll; } + +static int gpencil_layer_mask_move_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + const int direction = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) { + return OPERATOR_CANCELLED; + } + if (gpl->act_mask > 0) { + bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1); + if (mask != NULL) { + BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ + if (BLI_listbase_link_move(&gpl->mask_layers, mask, direction)) { + gpl->act_mask += direction; + 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_layer_mask_move(wmOperatorType *ot) +{ + static const EnumPropertyItem slot_move[] = { + {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, + {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Grease Pencil Layer Mask"; + ot->idname = "GPENCIL_OT_layer_mask_move"; + ot->description = "Move the active Grease Pencil mask layer up/down in the list"; + + /* api callbacks */ + ot->exec = gpencil_layer_mask_move_exec; + ot->poll = gpencil_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index d388e11dc8c..d90f74fbf4f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - Scene *scene = CTX_data_scene(C); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; int layer_num = RNA_int_get(op->ptr, "layer"); const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* if autolock enabled, disabled now */ + /* If autolock enabled, disabled now. */ if (use_autolock) { gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; } @@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Extract all strokes to move to this layer - * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes - * getting repeatedly moved - */ - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - - /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ - if ((gpl == target_layer) || (gpf == NULL)) { + /* Extract all strokes to move to this layer. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) { + /* Skip if this is the layer we're moving strokes to. */ + if (gpl_src == target_layer) { continue; } + bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe; + for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) { + if ((gpf_src == gpl_src->actframe) || + ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf_src == NULL) { + continue; + } - /* make copies of selected strokes, and deselect these once we're done */ - LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + bGPDstroke *gpsn = NULL; + BLI_listbase_clear(&strokes); + for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + /* 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_src, gps) == false) { + continue; + } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } + if (gps->flag & GP_STROKE_SELECT) { + BLI_remlink(&gpf_src->strokes, gps); + BLI_addtail(&strokes, gps); + } + } + /* Paste them all in one go. */ + if (strokes.first) { + bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get( + target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW); - /* Check if the color is editable. */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { - continue; + BLI_movelisttolist(&gpf_dst->strokes, &strokes); + BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); + } } - - /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */ - if (gps->flag & GP_STROKE_SELECT) { - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&strokes, gps); + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } - - /* if new layer and autolock, lock old layer */ + /* If new layer and autolock, lock old layer. */ if ((layer_num == -1) && (use_autolock)) { - gpl->flag |= GP_LAYER_LOCKED; + gpl_src->flag |= GP_LAYER_LOCKED; } } CTX_DATA_END; - /* Paste them all in one go */ - if (strokes.first) { - bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW); - - BLI_movelisttolist(&gpf->strokes, &strokes); - BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); - } - /* back autolock status */ if (use_autolock) { gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; @@ -3762,6 +3763,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); /* Init snap context for geometry projection. */ SnapObjectContext *sctx = NULL; @@ -3774,36 +3776,55 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) int cfra_prv = INT_MIN; /* Go through each editable + selected stroke, adjusting each of its points one by one... */ - GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { - bool curve_select = false; - if (is_curve_edit && gps->editcurve != NULL) { - curve_select = gps->editcurve->flag & GP_CURVE_SELECT; - } + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - if (gps->flag & GP_STROKE_SELECT || curve_select) { + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { + continue; + } + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + bool curve_select = false; + if (is_curve_edit && gps->editcurve != NULL) { + curve_select = gps->editcurve->flag & GP_CURVE_SELECT; + } - /* update frame to get the new location of objects */ - if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) { - cfra_prv = gpf_->framenum; - CFRA = gpf_->framenum; - BKE_scene_graph_update_for_newframe(depsgraph); - } + if (gps->flag & GP_STROKE_SELECT || curve_select) { - ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original); + /* update frame to get the new location of objects */ + if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) { + cfra_prv = gpf->framenum; + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph); + } - if (is_curve_edit && gps->editcurve != NULL) { - BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps); - /* Update the selection from the stroke to the curve. */ - BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve); + ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original); - gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; - BKE_gpencil_stroke_geometry_update(gpd, gps); - } + if (is_curve_edit && gps->editcurve != NULL) { + BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps); + /* Update the selection from the stroke to the curve. */ + BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve); - changed = true; + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + BKE_gpencil_stroke_geometry_update(gpd, gps); + } + + changed = true; + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } } } - GP_EDITABLE_STROKES_END(gpstroke_iter); + CTX_DATA_END; /* return frame state and DB to original state */ CFRA = oldframe; @@ -3832,7 +3853,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) "VIEW", 0, "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " "using 'Cursor' Stroke Placement"}, {GP_REPROJECT_SURFACE, "SURFACE", @@ -3851,7 +3873,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) ot->name = "Reproject Strokes"; ot->idname = "GPENCIL_OT_reproject"; ot->description = - "Reproject the selected strokes from the current viewpoint as if they had been newly drawn " + "Reproject the selected strokes from the current viewpoint as if they had been newly " + "drawn " "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, " "or for matching deforming geometry)"; @@ -4208,7 +4231,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) ot->name = "Subdivide Stroke"; ot->idname = "GPENCIL_OT_stroke_subdivide"; ot->description = - "Subdivide between continuous selected points of the stroke adding a point half way between " + "Subdivide between continuous selected points of the stroke adding a point half way " + "between " "them"; /* api callbacks */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index bb0a2916d2d..62d64c67b08 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1247,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf) static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) { ImBuf *ibuf; + Brush *brush = tgpf->brush; float rgba[4]; void *lock; int v[2]; @@ -1279,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) /* Dilate. */ if (dilate) { - dilate_shape(ibuf); + for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) { + dilate_shape(ibuf); + } } for (int idx = imagesize - 1; idx != 0; idx--) { diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index c6f74c39beb..09200125cb7 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -421,6 +421,7 @@ void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot); void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot); void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot); +void GPENCIL_OT_layer_mask_move(struct wmOperatorType *ot); void GPENCIL_OT_hide(struct wmOperatorType *ot); void GPENCIL_OT_reveal(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 7ca53779522..0062e363cdf 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -1434,36 +1434,32 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayout *col, *row; - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "step", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "step", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "layers", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "layers", 0, NULL, ICON_NONE); if (CTX_data_mode_enum(C) == CTX_MODE_EDIT_GPENCIL) { row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "interpolate_selected_only", 0, NULL, ICON_NONE); } row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "flip", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "flip", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "smooth_factor", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "smooth_steps", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "type", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE); if (type == GP_IPO_CURVEMAP) { /* Get an RNA pointer to ToolSettings to give to the custom curve. */ @@ -1477,16 +1473,16 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op) } else if (type != GP_IPO_LINEAR) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "easing", 0, NULL, ICON_NONE); if (type == GP_IPO_BACK) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "back", 0, NULL, ICON_NONE); } else if (type == GP_IPO_ELASTIC) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "amplitude", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "period", 0, NULL, ICON_NONE); } } } diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 1a6cb5670c4..7d454eb3be1 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -601,6 +601,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_mask_add); WM_operatortype_append(GPENCIL_OT_layer_mask_remove); + WM_operatortype_append(GPENCIL_OT_layer_mask_move); WM_operatortype_append(GPENCIL_OT_hide); WM_operatortype_append(GPENCIL_OT_reveal); diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 983ae94b637..8118e3c6c69 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -110,7 +110,7 @@ struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceF void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile); void ED_fileselect_params_to_userdef(struct SpaceFile *sfile, - const int temp_win_size[], + const int temp_win_size[2], const bool is_maximized); void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region); diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index 58b2cb80bbe..571519e52f7 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -263,6 +263,11 @@ struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *sce typedef enum { ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE = 1 << 0, + ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE = 1 << 1, + ED_SNAPGIZMO_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */ + ED_SNAPGIZMO_SNAP_ONLY_ACTIVE = 1 << 3, + ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL = 1 << 4, + ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE = 1 << 5, } eSnapGizmo; void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index e9ac21f60cf..1df3d993c39 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -249,6 +249,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y); /* ----------- Add Primitive Utilities -------------- */ +void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob); diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 12d6f1fce54..179c9d5b30d 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -511,6 +511,7 @@ bool ED_autokeyframe_property(struct bContext *C, #define ANIM_KS_ROTATION_ID "Rotation" #define ANIM_KS_SCALING_ID "Scaling" #define ANIM_KS_LOC_ROT_SCALE_ID "LocRotScale" +#define ANIM_KS_LOC_ROT_SCALE_CPROP_ID "LocRotScaleCProp" #define ANIM_KS_AVAILABLE_ID "Available" #define ANIM_KS_WHOLE_CHARACTER_ID "WholeCharacter" #define ANIM_KS_WHOLE_CHARACTER_SELECTED_ID "WholeCharacterSelected" diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 78c4c2a8eba..c46f0f78eb5 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -162,7 +162,7 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area); void ED_area_tag_redraw_regiontype(ScrArea *area, int type); void ED_area_tag_refresh(ScrArea *area); void ED_area_do_refresh(struct bContext *C, ScrArea *area); -struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[]); +struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]); void ED_area_status_text(ScrArea *area, const char *str); void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool skip_region_exit); void ED_area_prevspace(struct bContext *C, ScrArea *area); @@ -200,7 +200,7 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); /* screens */ void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm); void ED_screen_draw_edges(struct wmWindow *win); -void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2); +void ED_screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2); void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac); void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win); void ED_screen_ensure_updated(struct wmWindowManager *wm, diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index b7174964ef6..42e73bbf744 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -39,12 +39,19 @@ struct View3D; /* ED_transform_snap_object_*** API */ -typedef enum eSnapSelect { +typedef enum { SNAP_ALL = 0, SNAP_NOT_SELECTED = 1, SNAP_NOT_ACTIVE = 2, + SNAP_ONLY_ACTIVE = 3, } eSnapSelect; +typedef enum { + SNAP_GEOM_FINAL = 0, + SNAP_GEOM_CAGE = 1, + SNAP_GEOM_EDIT = 2, /* Bmesh for mesh-type. */ +} eSnapEditType; + /** used for storing multiple hits */ struct SnapObjectHitDepth { struct SnapObjectHitDepth *next, *prev; @@ -54,7 +61,7 @@ struct SnapObjectHitDepth { float no[3]; int index; - struct Object *ob; + struct Object *ob_eval; float obmat[4][4]; /* needed to tell which ray-cast this was part of, @@ -64,10 +71,10 @@ struct SnapObjectHitDepth { /** parameters that define which objects will be used to snap. */ struct SnapObjectParams { - /* special context sensitive handling for the active or selected object */ + /* Special context sensitive handling for the active or selected object. */ char snap_select; - /* use editmode cage */ - unsigned int use_object_edit_cage : 1; + /* Geometry for snapping in edit mode. */ + char edit_mode_type; /* snap to the closest element, use when using more than one snap type */ unsigned int use_occlusion_test : 1; /* exclude back facing geometry from snapping */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 81872428038..338b12f7985 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -186,17 +186,17 @@ enum { UI_RETURN_POPUP_OK = 1 << 5, }; -/* but->flag - general state flags. */ +/** #uiBut.flag general state flags. */ enum { - /** Warning, the first 6 flags are internal. */ - UI_BUT_ICON_SUBMENU = 1 << 6, - UI_BUT_ICON_PREVIEW = 1 << 7, + /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */ + UI_BUT_ICON_SUBMENU = 1 << 7, + UI_BUT_ICON_PREVIEW = 1 << 8, - UI_BUT_NODE_LINK = 1 << 8, - UI_BUT_NODE_ACTIVE = 1 << 9, - UI_BUT_DRAG_LOCK = 1 << 10, + UI_BUT_NODE_LINK = 1 << 9, + UI_BUT_NODE_ACTIVE = 1 << 10, + UI_BUT_DRAG_LOCK = 1 << 11, /** Grayed out and un-editable. */ - UI_BUT_DISABLED = 1 << 11, + UI_BUT_DISABLED = 1 << 12, UI_BUT_ANIMATED = 1 << 13, UI_BUT_ANIMATED_KEY = 1 << 14, @@ -2137,7 +2137,7 @@ void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name); -void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color); +void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float color[4]); void uiTemplateCacheFile(uiLayout *layout, const struct bContext *C, struct PointerRNA *ptr, @@ -2405,9 +2405,12 @@ void uiItemS_ex(uiLayout *layout, float factor); void uiItemSpacer(uiLayout *layout); void uiItemPopoverPanel_ptr( - uiLayout *layout, struct bContext *C, struct PanelType *pt, const char *name, int icon); -void uiItemPopoverPanel( - uiLayout *layout, struct bContext *C, const char *panel_type, const char *name, int icon); + uiLayout *layout, const struct bContext *C, struct PanelType *pt, const char *name, int icon); +void uiItemPopoverPanel(uiLayout *layout, + const struct bContext *C, + const char *panel_type, + const char *name, + int icon); void uiItemPopoverPanelFromGroup(uiLayout *layout, struct bContext *C, int space_id, diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 1820c2f133c..c99c7f681b3 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -451,7 +451,7 @@ int UI_ThemeMenuShadowWidth(void); /* only for buttons in theme editor! */ const unsigned char *UI_ThemeGetColorPtr(struct bTheme *btheme, int spacetype, int colorid); -void UI_make_axis_color(const unsigned char *src_col, unsigned char *dst_col, const char axis); +void UI_make_axis_color(const unsigned char src_col[3], unsigned char dst_col[3], const char axis); #ifdef __cplusplus } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index a5a5a69728e..5a254db0eec 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1114,6 +1114,13 @@ static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data) { if (data->str) { + double value; + /* Check if the string value is a number and cancel if it's equal to the startvalue. */ + if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) { + data->cancel = true; + return; + } + if (ui_but_string_set(C, but, data->str)) { data->value = ui_but_value_get(but); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 4c96512b4f3..333dc1beb22 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -62,7 +62,7 @@ struct wmTimer; #define UI_MENU_PADDING (int)(0.2f * UI_UNIT_Y) #define UI_MENU_WIDTH_MIN (UI_UNIT_Y * 9) -/* some extra padding added to menus containing submenu icons */ +/** Some extra padding added to menus containing sub-menu icons. */ #define UI_MENU_SUBMENU_PADDING (6 * UI_DPI_FAC) /* menu scrolling */ @@ -74,28 +74,31 @@ struct wmTimer; #define UI_PANEL_MINX 100 #define UI_PANEL_MINY 70 -/* popover width (multiplied by 'U.widget_unit') */ +/** Popover width (multiplied by #U.widget_unit) */ #define UI_POPOVER_WIDTH_UNITS 10 -/* uiBut->flag */ +/** #uiBut.flag */ enum { - UI_SELECT = (1 << 0), /* use when the button is pressed */ - UI_SCROLLED = (1 << 1), /* temp hidden, scrolled away */ + /** Use when the button is pressed. */ + UI_SELECT = (1 << 0), + /** Temporarily hidden (scrolled out of the view). */ + UI_SCROLLED = (1 << 1), UI_ACTIVE = (1 << 2), UI_HAS_ICON = (1 << 3), UI_HIDDEN = (1 << 4), - UI_SELECT_DRAW = (1 << 5), /* Display selected, doesn't impact interaction. */ + /** Display selected, doesn't impact interaction. */ + UI_SELECT_DRAW = (1 << 5), /** Property search filter is active and the button does not match. */ - UI_SEARCH_FILTER_NO_MATCH = (1 << 12), - /* warn: rest of uiBut->flag in UI_interface.h */ + UI_SEARCH_FILTER_NO_MATCH = (1 << 6), + /* WARNING: rest of #uiBut.flag in UI_interface.h */ }; -/* uiBut->dragflag */ +/** #uiBut.dragflag */ enum { UI_BUT_DRAGPOIN_FREE = (1 << 0), }; -/* but->pie_dir */ +/** #uiBut.pie_dir */ typedef enum RadialDirection { UI_RADIAL_NONE = -1, UI_RADIAL_N = 0, @@ -126,13 +129,13 @@ extern const short ui_radial_dir_to_angle[8]; #define UI_BITBUT_ROW(min, max) \ (((max) >= 31 ? 0xFFFFFFFF : (1 << ((max) + 1)) - 1) - ((min) ? ((1 << (min)) - 1) : 0)) -/* split numbuts by ':' and align l/r */ +/** Split number-buttons by ':' and align left/right. */ #define USE_NUMBUTS_LR_ALIGN -/* Use new 'align' computation code. */ +/** Use new 'align' computation code. */ #define USE_UIBUT_SPATIAL_ALIGN -/* PieMenuData->flags */ +/** #PieMenuData.flags */ enum { /** pie menu item collision is detected at 90 degrees */ UI_PIE_DEGREES_RANGE_LARGE = (1 << 0), @@ -152,13 +155,13 @@ enum { #define PIE_CLICK_THRESHOLD_SQ 50.0f -/* max amount of items a radial menu (pie menu) can contain */ +/** The maximum number of items a radial menu (pie menu) can contain. */ #define PIE_MAX_ITEMS 8 struct uiBut { struct uiBut *next, *prev; - /* Pointer back to the layout item holding this button. */ + /** Pointer back to the layout item holding this button. */ uiLayout *layout; int flag, drawflag; eButType type; @@ -235,10 +238,10 @@ struct uiBut { short modifier_key; short iconadd; - /* UI_BTYPE_BLOCK data */ + /** #UI_BTYPE_BLOCK data */ uiBlockCreateFunc block_create_func; - /* UI_BTYPE_PULLDOWN/UI_BTYPE_MENU data */ + /** #UI_BTYPE_PULLDOWN / #UI_BTYPE_MENU data */ uiMenuCreateFunc menu_create_func; uiMenuStepFunc menu_step_func; @@ -252,9 +255,11 @@ struct uiBut { struct wmOperatorType *optype; struct PointerRNA *opptr; short opcontext; - uchar menu_key; /* 'a'-'z', always lower case */ - ListBase extra_op_icons; /* uiButExtraOpIcon */ + /** When non-zero, this is the key used to activate a menu items (`a-z` always lower case). */ + uchar menu_key; + + ListBase extra_op_icons; /** #uiButExtraOpIcon */ /* Draggable data, type is WM_DRAG_... */ char dragtype; @@ -263,10 +268,10 @@ struct uiBut { struct ImBuf *imb; float imb_scale; - /* active button data */ + /** Active button data (set when the user is hovering or interacting with a button). */ struct uiHandleButtonData *active; - /* Custom button data. */ + /** Custom button data (borrowed, not owned). */ void *custom_data; char *editstr; @@ -429,7 +434,7 @@ struct PieMenuData { float alphafac; }; -/* uiBlock.content_hints */ +/** #uiBlock.content_hints */ enum eBlockContentHints { /** In a menu block, if there is a single sub-menu button, we add some * padding to the right to put nicely aligned triangle icons there. */ @@ -463,7 +468,8 @@ struct uiBlock { struct Panel *panel; uiBlock *oldblock; - ListBase butstore; /* UI_butstore_* runtime function */ + /** Used for `UI_butstore_*` runtime function. */ + ListBase butstore; ListBase button_groups; /* #uiButtonGroup. */ @@ -479,7 +485,8 @@ struct uiBlock { rctf rect; float aspect; - uint puphash; /* popup menu hash for memory */ + /** Unique hash used to implement popup menu memory. */ + uint puphash; uiButHandleFunc func; void *func_arg1; @@ -494,10 +501,10 @@ struct uiBlock { uiBlockHandleFunc handle_func; void *handle_func_arg; - /* custom extra handling */ + /** Custom extra event handling. */ int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *); - /* extra draw function for custom blocks */ + /** Custom extra draw function for custom blocks. */ void (*drawextra)(const struct bContext *C, void *idv, void *arg1, void *arg2, rcti *rect); void *drawextra_arg1; void *drawextra_arg2; @@ -507,7 +514,7 @@ struct uiBlock { /** Hints about the buttons of this block. Used to avoid iterating over * buttons to find out if some criteria is met by any. Instead, check this * criteria when adding the button and set a flag here if it's met. */ - short content_hints; /* eBlockContentHints */ + short content_hints; /* #eBlockContentHints */ char direction; /** UI_BLOCK_THEME_STYLE_* */ @@ -521,11 +528,11 @@ struct uiBlock { const char *lockstr; bool lock; - /** to keep blocks while drawing and free them afterwards */ + /** To keep blocks while drawing and free them afterwards. */ bool active; - /** to avoid tooltip after click */ + /** To avoid tool-tip after click. */ bool tooltipdisabled; - /** UI_block_end done? */ + /** True when #UI_block_end has been called. */ bool endblock; /** for doing delayed */ @@ -535,12 +542,12 @@ struct uiBlock { /** for doing delayed */ int bounds, minbounds; - /** pull-downs, to detect outside, can differ per case how it is created. */ + /** Pull-downs, to detect outside, can differ per case how it is created. */ rctf safety; - /** uiSafetyRct list */ + /** #uiSafetyRct list */ ListBase saferct; - uiPopupBlockHandle *handle; /* handle */ + uiPopupBlockHandle *handle; /** use so presets can find the operator, * across menus and from nested popups which fail for operator context. */ @@ -555,10 +562,12 @@ struct uiBlock { /** \note only accessed by color picker templates. */ ColorPickerData color_pickers; - bool is_color_gamma_picker; /* Block for color picker with gamma baked in. */ + /** Block for color picker with gamma baked in. */ + bool is_color_gamma_picker; - /** display device name used to display this block, - * used by color widgets to transform colors from/to scene linear + /** + * Display device name used to display this block, + * used by color widgets to transform colors from/to scene linear. */ char display_device[64]; @@ -671,9 +680,9 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]); /* interface_regions.c */ struct uiKeyNavLock { - /* Set when we're using key-input. */ + /** Set when we're using keyboard-input. */ bool is_keynav; - /* only used to check if we've moved the cursor */ + /** Only used to check if we've moved the cursor. */ int event_xy[2]; }; @@ -689,7 +698,7 @@ struct uiPopupBlockCreate { int event_xy[2]; - /* when popup is initialized from a button */ + /** Set when popup is initialized from a button. */ struct ARegion *butregion; uiBut *but; }; @@ -698,7 +707,7 @@ struct uiPopupBlockHandle { /* internal */ struct ARegion *region; - /* use only for 'UI_BLOCK_MOVEMOUSE_QUIT' popups */ + /** Use only for #UI_BLOCK_MOVEMOUSE_QUIT popups. */ float towards_xy[2]; double towardstime; bool dotowards; @@ -708,9 +717,9 @@ struct uiPopupBlockHandle { void (*cancel_func)(struct bContext *C, void *arg); void *popup_arg; - /* store data for refreshing popups */ + /** Store data for refreshing popups. */ struct uiPopupBlockCreate popup_create_vars; - /* true if we can re-create the popup using 'popup_create_vars' */ + /** True if we can re-create the popup using #uiPopupBlockHandle.popup_create_vars. */ bool can_refresh; bool refresh; @@ -730,7 +739,7 @@ struct uiPopupBlockHandle { int retvalue; float retvec[4]; - /* menu direction */ + /** Menu direction. */ int direction; /* Previous values so we don't resize or reposition on refresh. */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index cabd98902a6..8f2871ce18b 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -3038,7 +3038,7 @@ void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, i /* popover */ void uiItemPopoverPanel_ptr( - uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon) + uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon) { if (!name) { name = CTX_IFACE_(pt->translation_context, pt->label); @@ -3067,7 +3067,7 @@ void uiItemPopoverPanel_ptr( } void uiItemPopoverPanel( - uiLayout *layout, bContext *C, const char *panel_type, const char *name, int icon) + uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon) { PanelType *pt = WM_paneltype_find(panel_type, true); if (pt == NULL) { diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1fb7a931c08..0cf3ad59903 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1495,117 +1495,115 @@ static void edittranslation_find_po_file(const char *root, static int edittranslation_exec(bContext *C, wmOperator *op) { uiBut *but = UI_context_active_but_get(C); - int ret = OPERATOR_CANCELLED; - - if (but) { - wmOperatorType *ot; - PointerRNA ptr; - char popath[FILE_MAX]; - const char *root = U.i18ndir; - const char *uilng = BLT_lang_get(); - - uiStringInfo but_label = {BUT_GET_LABEL, NULL}; - uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; - uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; - uiStringInfo but_tip = {BUT_GET_TIP, NULL}; - uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; - uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; - uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; - uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; - uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; - uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; - - if (!BLI_is_dir(root)) { - BKE_report(op->reports, - RPT_ERROR, - "Please set your Preferences' 'Translation Branches " - "Directory' path to a valid directory"); - return OPERATOR_CANCELLED; - } - ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); - if (ot == NULL) { - BKE_reportf(op->reports, - RPT_ERROR, - "Could not find operator '%s'! Please enable ui_translate add-on " - "in the User Preferences", - EDTSRC_I18N_OP_NAME); - return OPERATOR_CANCELLED; - } - /* Try to find a valid po file for current language... */ - edittranslation_find_po_file(root, uilng, popath, FILE_MAX); - /* printf("po path: %s\n", popath); */ - if (popath[0] == '\0') { - BKE_reportf( - op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); - return OPERATOR_CANCELLED; - } - - UI_but_string_info_get(C, - but, - &but_label, - &rna_label, - &enum_label, - &but_tip, - &rna_tip, - &enum_tip, - &rna_struct, - &rna_prop, - &rna_enum, - &rna_ctxt, - NULL); - - WM_operator_properties_create_ptr(&ptr, ot); - RNA_string_set(&ptr, "lang", uilng); - RNA_string_set(&ptr, "po_file", popath); - RNA_string_set(&ptr, "but_label", but_label.strinfo); - RNA_string_set(&ptr, "rna_label", rna_label.strinfo); - RNA_string_set(&ptr, "enum_label", enum_label.strinfo); - RNA_string_set(&ptr, "but_tip", but_tip.strinfo); - RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo); - RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo); - RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo); - RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); - RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); - RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); - ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); - - /* Clean up */ - if (but_label.strinfo) { - MEM_freeN(but_label.strinfo); - } - if (rna_label.strinfo) { - MEM_freeN(rna_label.strinfo); - } - if (enum_label.strinfo) { - MEM_freeN(enum_label.strinfo); - } - if (but_tip.strinfo) { - MEM_freeN(but_tip.strinfo); - } - if (rna_tip.strinfo) { - MEM_freeN(rna_tip.strinfo); - } - if (enum_tip.strinfo) { - MEM_freeN(enum_tip.strinfo); - } - if (rna_struct.strinfo) { - MEM_freeN(rna_struct.strinfo); - } - if (rna_prop.strinfo) { - MEM_freeN(rna_prop.strinfo); - } - if (rna_enum.strinfo) { - MEM_freeN(rna_enum.strinfo); - } - if (rna_ctxt.strinfo) { - MEM_freeN(rna_ctxt.strinfo); - } + if (but == NULL) { + BKE_report(op->reports, RPT_ERROR, "Active button not found"); + return OPERATOR_CANCELLED; + } - return ret; + wmOperatorType *ot; + PointerRNA ptr; + char popath[FILE_MAX]; + const char *root = U.i18ndir; + const char *uilng = BLT_lang_get(); + + uiStringInfo but_label = {BUT_GET_LABEL, NULL}; + uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; + uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; + uiStringInfo but_tip = {BUT_GET_TIP, NULL}; + uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; + uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; + uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; + uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; + uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; + uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; + + if (!BLI_is_dir(root)) { + BKE_report(op->reports, + RPT_ERROR, + "Please set your Preferences' 'Translation Branches " + "Directory' path to a valid directory"); + return OPERATOR_CANCELLED; + } + ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); + if (ot == NULL) { + BKE_reportf(op->reports, + RPT_ERROR, + "Could not find operator '%s'! Please enable ui_translate add-on " + "in the User Preferences", + EDTSRC_I18N_OP_NAME); + return OPERATOR_CANCELLED; + } + /* Try to find a valid po file for current language... */ + edittranslation_find_po_file(root, uilng, popath, FILE_MAX); + /* printf("po path: %s\n", popath); */ + if (popath[0] == '\0') { + BKE_reportf( + op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); + return OPERATOR_CANCELLED; } - BKE_report(op->reports, RPT_ERROR, "Active button not found"); - return OPERATOR_CANCELLED; + UI_but_string_info_get(C, + but, + &but_label, + &rna_label, + &enum_label, + &but_tip, + &rna_tip, + &enum_tip, + &rna_struct, + &rna_prop, + &rna_enum, + &rna_ctxt, + NULL); + + WM_operator_properties_create_ptr(&ptr, ot); + RNA_string_set(&ptr, "lang", uilng); + RNA_string_set(&ptr, "po_file", popath); + RNA_string_set(&ptr, "but_label", but_label.strinfo); + RNA_string_set(&ptr, "rna_label", rna_label.strinfo); + RNA_string_set(&ptr, "enum_label", enum_label.strinfo); + RNA_string_set(&ptr, "but_tip", but_tip.strinfo); + RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo); + RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo); + RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo); + RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); + RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); + RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); + const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + + /* Clean up */ + if (but_label.strinfo) { + MEM_freeN(but_label.strinfo); + } + if (rna_label.strinfo) { + MEM_freeN(rna_label.strinfo); + } + if (enum_label.strinfo) { + MEM_freeN(enum_label.strinfo); + } + if (but_tip.strinfo) { + MEM_freeN(but_tip.strinfo); + } + if (rna_tip.strinfo) { + MEM_freeN(rna_tip.strinfo); + } + if (enum_tip.strinfo) { + MEM_freeN(enum_tip.strinfo); + } + if (rna_struct.strinfo) { + MEM_freeN(rna_struct.strinfo); + } + if (rna_prop.strinfo) { + MEM_freeN(rna_prop.strinfo); + } + if (rna_enum.strinfo) { + MEM_freeN(rna_enum.strinfo); + } + if (rna_ctxt.strinfo) { + MEM_freeN(rna_ctxt.strinfo); + } + + return ret; } static void UI_OT_edittranslation_init(wmOperatorType *ot) diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index f234f0fbbf5..58a74a3473e 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -86,6 +86,17 @@ int ui_but_menu_step(uiBut *but, int direction) return 0; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Popup Menu Memory + * + * Support menu-memory, a feature that positions the cursor + * over the previously used menu item. + * + * \note This is stored for each unique menu title. + * \{ */ + static uint ui_popup_string_hash(const char *str, const bool use_sep) { /* sometimes button contains hotkey, sometimes not, strip for proper compare */ @@ -392,7 +403,7 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, pup->layout = UI_block_layout( pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); - /* note, this intentionally differs from the menu & submenu default because many operators + /* note, this intentionally differs from the menu & sub-menu default because many operators * use popups like this to select one of their options - * where having invoke doesn't make sense */ uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index b11a727b173..accfb78ab94 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -947,12 +947,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* button is disabled, we may be able to tell user why */ if (but->flag & UI_BUT_DISABLED) { const char *disabled_msg = NULL; + bool disabled_msg_free = false; /* if operator poll check failed, it can give pretty precise info why */ if (but->optype) { - CTX_wm_operator_poll_msg_set(C, NULL); + CTX_wm_operator_poll_msg_clear(C); WM_operator_poll_context(C, but->optype, but->opcontext); - disabled_msg = CTX_wm_operator_poll_msg_get(C); + disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free); } /* alternatively, buttons can store some reasoning too */ else if (but->disabled_info) { @@ -967,6 +968,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) }); field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg); } + if (disabled_msg_free) { + MEM_freeN((void *)disabled_msg); + } } if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 0484565ee2e..e3df9704826 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -7195,7 +7195,7 @@ void uiTemplateComponentMenu(uiLayout *layout, /** \name Node Socket Icon Template * \{ */ -void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color) +void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float color[4]) { uiBlock *block = uiLayoutGetBlock(layout); UI_block_align_begin(block); diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 1f1165a464b..592467c2a85 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -240,22 +240,17 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) static void wm_alembic_export_draw(bContext *C, wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - /* Conveniently set start and end frame to match the scene's frame range. */ Scene *scene = CTX_data_scene(C); - if (scene != NULL && RNA_boolean_get(&ptr, "init_scene_frame_range")) { - RNA_int_set(&ptr, "start", SFRA); - RNA_int_set(&ptr, "end", EFRA); + if (scene != NULL && RNA_boolean_get(op->ptr, "init_scene_frame_range")) { + RNA_int_set(op->ptr, "start", SFRA); + RNA_int_set(op->ptr, "end", EFRA); - RNA_boolean_set(&ptr, "init_scene_frame_range", false); + RNA_boolean_set(op->ptr, "init_scene_frame_range", false); } - ui_alembic_export_settings(op->layout, &ptr); + ui_alembic_export_settings(op->layout, op->ptr); } static bool wm_alembic_export_check(bContext *UNUSED(C), wmOperator *op) @@ -594,13 +589,9 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE); } -static void wm_alembic_import_draw(bContext *C, wmOperator *op) +static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - ui_alembic_import_settings(op->layout, &ptr); + ui_alembic_import_settings(op->layout, op->ptr); } /* op->invoke, opens fileselect if path property not set, otherwise executes */ diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index c8e3e1814fc..859c12d7e52 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -400,13 +400,9 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr) } } -static void wm_collada_export_draw(bContext *C, wmOperator *op) +static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiCollada_exportSettings(op->layout, &ptr); + uiCollada_exportSettings(op->layout, op->ptr); } static bool wm_collada_export_check(bContext *UNUSED(C), wmOperator *op) @@ -798,13 +794,9 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr) uiItemR(box, imfptr, "keep_bind_info", 0, NULL, ICON_NONE); } -static void wm_collada_import_draw(bContext *C, wmOperator *op) +static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiCollada_importSettings(op->layout, &ptr); + uiCollada_importSettings(op->layout, op->ptr); } void WM_OT_collada_import(wmOperatorType *ot) diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c index 7b4b59902f9..b49be324372 100644 --- a/source/blender/editors/io/io_gpencil_export.c +++ b/source/blender/editors/io/io_gpencil_export.c @@ -215,14 +215,9 @@ static void ui_gpencil_export_svg_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "use_clip_camera", 0, NULL, ICON_NONE); } -static void wm_gpencil_export_svg_draw(bContext *C, wmOperator *op) +static void wm_gpencil_export_svg_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - ui_gpencil_export_svg_settings(op->layout, &ptr); + ui_gpencil_export_svg_settings(op->layout, op->ptr); } static bool wm_gpencil_export_svg_poll(bContext *C) @@ -378,14 +373,9 @@ static void ui_gpencil_export_pdf_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "use_normalized_thickness", 0, NULL, ICON_NONE); } -static void wm_gpencil_export_pdf_draw(bContext *C, wmOperator *op) +static void wm_gpencil_export_pdf_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - ui_gpencil_export_pdf_settings(op->layout, &ptr); + ui_gpencil_export_pdf_settings(op->layout, op->ptr); } static bool wm_gpencil_export_pdf_poll(bContext *C) diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c index e4fabc0c5de..a9911f1cef2 100644 --- a/source/blender/editors/io/io_gpencil_import.c +++ b/source/blender/editors/io/io_gpencil_import.c @@ -136,13 +136,9 @@ static void ui_gpencil_import_svg_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "scale", 0, NULL, ICON_NONE); } -static void wm_gpencil_import_svg_draw(bContext *C, wmOperator *op) +static void wm_gpencil_import_svg_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - ui_gpencil_import_svg_settings(op->layout, &ptr); + ui_gpencil_import_svg_settings(op->layout, op->ptr); } static bool wm_gpencil_import_svg_poll(bContext *C) diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index 1eb6613d8fe..81bf66da72c 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -220,8 +220,8 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot) } /* - * - loop over selected shapekeys. - * - find firstsel/lastsel pairs. + * - loop over selected shape-keys. + * - find first-selected/last-selected pairs. * - move these into a temp list. * - re-key all the original shapes. * - copy unselected values back from the original. diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 51190268c4c..43492cd57af 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -911,76 +911,73 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) static void edbm_bevel_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayout *col, *row; - PointerRNA ptr, toolsettings_ptr; + PointerRNA toolsettings_ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - int profile_type = RNA_enum_get(&ptr, "profile_type"); - int offset_type = RNA_enum_get(&ptr, "offset_type"); - bool affect_type = RNA_enum_get(&ptr, "affect"); + int profile_type = RNA_enum_get(op->ptr, "profile_type"); + int offset_type = RNA_enum_get(op->ptr, "offset_type"); + bool affect_type = RNA_enum_get(op->ptr, "affect"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); - uiItemR(layout, &ptr, "offset_type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset_type", 0, NULL, ICON_NONE); if (offset_type == BEVEL_AMT_PERCENT) { - uiItemR(layout, &ptr, "offset_pct", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset_pct", 0, NULL, ICON_NONE); } else { - uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE); } - uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "segments", 0, NULL, ICON_NONE); if (ELEM(profile_type, BEVEL_PROFILE_SUPERELLIPSE, BEVEL_PROFILE_CUSTOM)) { uiItemR(layout, - &ptr, + op->ptr, "profile", UI_ITEM_R_SLIDER, (profile_type == BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") : IFACE_("Miter Shape"), ICON_NONE); } - uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "material", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "clamp_overlap", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "loop_slide", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "harden_normals", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "clamp_overlap", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "loop_slide", 0, NULL, ICON_NONE); col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark")); uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES); - uiItemR(col, &ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE); - uiItemR(col, &ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE); + uiItemR(col, op->ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE); + uiItemR(col, op->ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE); uiItemS(layout); col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES); - uiItemR(col, &ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE); - uiItemR(col, &ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE); - if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) { - uiItemR(col, &ptr, "spread", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE); + uiItemR(col, op->ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE); + if (RNA_enum_get(op->ptr, "miter_inner") == BEVEL_MITER_ARC) { + uiItemR(col, op->ptr, "spread", 0, NULL, ICON_NONE); } uiItemS(layout); col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES); - uiItemR(col, &ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE); + uiItemR(col, op->ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE); - uiItemR(layout, &ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE); + uiItemR(layout, op->ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); if (profile_type == BEVEL_PROFILE_CUSTOM) { /* Get an RNA pointer to ToolSettings to give to the curve profile template code. */ Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 141f120396a..d1f228e951a 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -251,32 +251,28 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void edbm_intersect_ui(bContext *C, wmOperator *op) +static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayout *row; - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; + bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); if (!use_exact) { - uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE); } } @@ -418,32 +414,28 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void edbm_intersect_boolean_ui(bContext *C, wmOperator *op) +static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; uiLayout *row; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; + bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); - uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_swap", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_self", 0, NULL, ICON_NONE); if (!use_exact) { - uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE); } } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 33e4691bfb5..bb332a4094c 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3694,21 +3694,18 @@ static const EnumPropertyItem *shape_itemf(bContext *C, static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; Object *obedit = CTX_data_edit_object(C); Mesh *me = obedit->data; PointerRNA ptr_key; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); RNA_id_pointer_create((ID *)me->key, &ptr_key); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - uiItemPointerR(layout, &ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA); - uiItemR(layout, &ptr, "blend", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "add", 0, NULL, ICON_NONE); + uiItemPointerR(layout, op->ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA); + uiItemR(layout, op->ptr, "blend", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "add", 0, NULL, ICON_NONE); } void MESH_OT_blend_from_shape(wmOperatorType *ot) @@ -5612,29 +5609,25 @@ static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op)) return true; } -static void edbm_decimate_ui(bContext *C, wmOperator *op) +static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout, *row, *col, *sub; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiLayoutSetPropSep(layout, true); - uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "ratio", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "use_vertex_group", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_vertex_group", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); - uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group")); - uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE); + uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group")); + uiItemR(col, op->ptr, "vertex_group_factor", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "invert_vertex_group", 0, NULL, ICON_NONE); row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry")); - uiItemR(row, &ptr, "use_symmetry", 0, "", ICON_NONE); + uiItemR(row, op->ptr, "use_symmetry", 0, "", ICON_NONE); sub = uiLayoutRow(row, true); - uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_symmetry")); - uiItemR(sub, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry")); + uiItemR(sub, op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE); } void MESH_OT_decimate(wmOperatorType *ot) diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 2e98f0558f3..19c9909039c 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1721,7 +1721,7 @@ void EDBM_project_snap_verts( SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_NOT_ACTIVE, - .use_object_edit_cage = false, + .edit_mode_type = SNAP_GEOM_FINAL, .use_occlusion_test = true, }, mval, diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 6f8b5f7d244..4f77a3690a8 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -117,6 +117,7 @@ #include "ED_physics.h" #include "ED_render.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_transform.h" #include "ED_view3d.h" @@ -1319,12 +1320,14 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) { return OPERATOR_CANCELLED; } - /* add new object if not currently editing a GP object, - * or if "empty" was chosen (i.e. user wants a blank GP canvas) - */ - if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) { + /* Add new object if not currently editing a GP object. */ + if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) { const char *ob_name = NULL; switch (type) { + case GP_EMPTY: { + ob_name = "GPencil"; + break; + } case GP_MONKEY: { ob_name = "Suzanne"; break; @@ -1354,6 +1357,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) /* create relevant geometry */ switch (type) { + case GP_EMPTY: { + float mat[4][4]; + + ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + ED_gpencil_create_blank(C, ob, mat); + break; + } case GP_STROKE: { float radius = RNA_float_get(op->ptr, "radius"); float mat[4][4]; @@ -1420,11 +1430,8 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) /* Stroke object is drawn in front of meshes by default. */ ob->dtx |= OB_DRAW_IN_FRONT; - } - case GP_EMPTY: - /* do nothing */ break; - + } default: BKE_report(op->reports, RPT_WARNING, "Not implemented"); break; @@ -2620,6 +2627,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) int a, mballConverted = 0; bool gpencilConverted = false; + bool gpencilCurveConverted = false; /* don't forget multiple users! */ @@ -2904,6 +2912,16 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* meshes doesn't use displist */ BKE_object_free_curve_cache(newob); } + else if (target == OB_GPENCIL) { + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + Object *ob_gpencil = ED_gpencil_add_object(C, newob->loc, local_view_bits); + copy_v3_v3(ob_gpencil->rot, newob->rot); + copy_v3_v3(ob_gpencil->scale, newob->scale); + BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f); + gpencilConverted = true; + gpencilCurveConverted = true; + basen = NULL; + } } else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { ob->flag |= OB_DONE; @@ -3085,6 +3103,17 @@ static int object_convert_exec(bContext *C, wmOperator *op) FOREACH_SCENE_OBJECT_END; } } + else { + /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */ + if (gpencilCurveConverted) { + FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) { + if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) { + ED_object_base_free_and_unlink(bmain, scene, ob_delete); + } + } + FOREACH_SCENE_OBJECT_END; + } + } // XXX ED_object_editmode_enter(C, 0); // XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */ @@ -3108,24 +3137,21 @@ static int object_convert_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void object_convert_ui(bContext *C, wmOperator *op) +static void object_convert_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; uiLayoutSetPropSep(layout, true); - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiItemR(layout, &ptr, "target", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "keep_original", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE); - if (RNA_enum_get(&ptr, "target") == OB_GPENCIL) { - uiItemR(layout, &ptr, "thickness", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "angle", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "seams", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "faces", 0, NULL, ICON_NONE); + if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) { + uiItemR(layout, op->ptr, "thickness", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "angle", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "seams", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "faces", 0, NULL, ICON_NONE); } } @@ -3405,7 +3431,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) /* 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(view_layer, NULL, BA_DESELECT); + ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); ED_object_base_select(basen, BA_SELECT); ED_object_base_activate(C, basen); diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index ededcbfdba8..5bf04e195fe 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -157,10 +157,6 @@ bool edit_modifier_poll_generic(struct bContext *C, const bool is_liboverride_allowed); void edit_modifier_properties(struct wmOperatorType *ot); bool edit_modifier_invoke_properties(struct bContext *C, struct wmOperator *op); -bool edit_modifier_invoke_properties_with_hover(struct bContext *C, - struct wmOperator *op, - const struct wmEvent *event, - int *r_retval); struct ModifierData *edit_modifier_property_get(struct wmOperator *op, struct Object *ob, diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 49c07b28f07..0d896f14888 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1120,10 +1120,10 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op) * with a UI panel below the mouse cursor, unless a specific modifier is set with a context * pointer. Used in order to apply modifier operators on hover over their panels. */ -bool edit_modifier_invoke_properties_with_hover(bContext *C, - wmOperator *op, - const wmEvent *event, - int *r_retval) +static bool edit_modifier_invoke_properties_with_hover(bContext *C, + wmOperator *op, + const wmEvent *event, + int *r_retval) { if (RNA_struct_property_is_set(op->ptr, "modifier")) { return true; diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 10e0a143d9b..157f2b3ecb4 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -692,8 +692,9 @@ struct ObjectPreviewData { int sizey; }; -static Object *object_preview_camera_create( - Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey) +static Object *object_preview_camera_create(Main *preview_main, + ViewLayer *view_layer, + Object *preview_object) { Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera"); @@ -701,18 +702,17 @@ static Object *object_preview_camera_create( float dummyscale[3]; mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat); - /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */ + /* Camera is Y up, so needs additional rotations to obliquely face the front. */ float drotmat[3][3]; - axis_angle_to_mat3_single(drotmat, 'X', M_PI_2); + const float eul[3] = {M_PI * 0.4f, 0.0f, M_PI * 0.1f}; + eul_to_mat3(drotmat, eul); mul_m3_m3_post(rotmat, drotmat); camera->rotmode = ROT_MODE_QUAT; mat3_to_quat(camera->quat, rotmat); - /* shader_preview_render() does this too. */ - if (sizex > sizey) { - ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex; - } + /* Nice focal length for close portraiture. */ + ((Camera *)camera->data)->lens = 85; return camera; } @@ -730,11 +730,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object); - Object *camera_object = object_preview_camera_create(preview_data->pr_main, - view_layer, - preview_data->object, - preview_data->sizex, - preview_data->sizey); + Object *camera_object = object_preview_camera_create( + preview_data->pr_main, view_layer, preview_data->object); scene->camera = camera_object; scene->r.xsch = preview_data->sizex; diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 2ba7ef8f972..6d1409a9044 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -33,184 +33,11 @@ #include "WM_api.h" +#include "UI_interface.h" #include "UI_resources.h" #include "screen_intern.h" -/** - * Draw horizontal shape visualizing future joining - * (left as well right direction of future joining). - */ -static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 8; - w = height / 4; - } - else { - h = width / 8; - w = width / 4; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x; - points[0].y = area->v1->vec.y + height / 2; - - points[1].x = area->v1->vec.x; - points[1].y = area->v1->vec.y; - - points[2].x = area->v4->vec.x - w; - points[2].y = area->v4->vec.y; - - points[3].x = area->v4->vec.x - w; - points[3].y = area->v4->vec.y + height / 2 - 2 * h; - - points[4].x = area->v4->vec.x - 2 * w; - points[4].y = area->v4->vec.y + height / 2; - - points[5].x = area->v4->vec.x - w; - points[5].y = area->v4->vec.y + height / 2 + 2 * h; - - points[6].x = area->v3->vec.x - w; - points[6].y = area->v3->vec.y; - - points[7].x = area->v2->vec.x; - points[7].y = area->v2->vec.y; - - points[8].x = area->v4->vec.x; - points[8].y = area->v4->vec.y + height / 2 - h; - - points[9].x = area->v4->vec.x; - points[9].y = area->v4->vec.y + height / 2 + h; - - if (dir == 'l') { - /* when direction is left, then we flip direction of arrow */ - float cx = area->v1->vec.x + width; - for (int i = 0; i < 10; i++) { - points[i].x -= cx; - points[i].x = -points[i].x; - points[i].x += area->v1->vec.x; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw vertical shape visualizing future joining (up/down direction). - */ -static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 4; - w = height / 8; - } - else { - h = width / 4; - w = width / 8; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x + width / 2; - points[0].y = area->v3->vec.y; - - points[1].x = area->v2->vec.x; - points[1].y = area->v2->vec.y; - - points[2].x = area->v1->vec.x; - points[2].y = area->v1->vec.y + h; - - points[3].x = area->v1->vec.x + width / 2 - 2 * w; - points[3].y = area->v1->vec.y + h; - - points[4].x = area->v1->vec.x + width / 2; - points[4].y = area->v1->vec.y + 2 * h; - - points[5].x = area->v1->vec.x + width / 2 + 2 * w; - points[5].y = area->v1->vec.y + h; - - points[6].x = area->v4->vec.x; - points[6].y = area->v4->vec.y + h; - - points[7].x = area->v3->vec.x; - points[7].y = area->v3->vec.y; - - points[8].x = area->v1->vec.x + width / 2 - w; - points[8].y = area->v1->vec.y; - - points[9].x = area->v1->vec.x + width / 2 + w; - points[9].y = area->v1->vec.y; - - if (dir == 'u') { - /* when direction is up, then we flip direction of arrow */ - float cy = area->v1->vec.y + height; - for (int i = 0; i < 10; i++) { - points[i].y -= cy; - points[i].y = -points[i].y; - points[i].y += area->v1->vec.y; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw join shape due to direction of joining. - */ -static void draw_join_shape(ScrArea *area, char dir, uint pos) -{ - if (ELEM(dir, 'u', 'd')) { - draw_vertical_join_shape(area, dir, pos); - } - else { - draw_horizontal_join_shape(area, dir, pos); - } -} - #define CORNER_RESOLUTION 3 static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i) @@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len) #undef CORNER_RESOLUTION -/** - * Draw screen area darker with arrow (visualization of future joining). - */ -static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(0, 0, 0, 50); - - draw_join_shape(area, dir, pos); -} - -/** - * Draw screen area lighter with arrow shape ("eraser" of previous dark shape). - */ -static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(255, 255, 255, 25); - - immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y); -} - static void drawscredge_area_draw( int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness) { @@ -427,50 +232,92 @@ void ED_screen_draw_edges(wmWindow *win) } /** - * The blended join arrows. + * Visual indication of the two areas involved in a proposed join. * * \param sa1: Area from which the resultant originates. * \param sa2: Target area that will be replaced. */ -void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2) +void ED_screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - GPU_line_width(1); - - /* blended join arrow */ int dir = area_getorientation(sa1, sa2); - int dira = -1; - if (dir != -1) { - switch (dir) { - case 0: /* W */ - dir = 'r'; - dira = 'l'; - break; - case 1: /* N */ - dir = 'd'; - dira = 'u'; - break; - case 2: /* E */ - dir = 'l'; - dira = 'r'; - break; - case 3: /* S */ - dir = 'u'; - dira = 'd'; - break; - } + if (dir == -1) { + return; + } - GPU_blend(GPU_BLEND_ALPHA); + /* Rect of the combined areas.*/ + bool vertical = ELEM(dir, 1, 3); + rctf combined = {.xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) : + MIN2(sa1->totrct.xmin, sa2->totrct.xmin), + .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) : + MAX2(sa1->totrct.xmax, sa2->totrct.xmax), + .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) : + MAX2(sa1->totrct.ymin, sa2->totrct.ymin), + .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) : + MIN2(sa1->totrct.ymax, sa2->totrct.ymax)}; - scrarea_draw_shape_dark(sa2, dir, pos); - scrarea_draw_shape_light(sa1, dira, pos); + uint pos_id = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); - GPU_blend(GPU_BLEND_NONE); + /* Highlight source (sa1) within combined area. */ + immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f}); + immRectf(pos_id, + MAX2(sa1->totrct.xmin, combined.xmin), + MAX2(sa1->totrct.ymin, combined.ymin), + MIN2(sa1->totrct.xmax, combined.xmax), + MIN2(sa1->totrct.ymax, combined.ymax)); + + /* Highlight destination (sa2) within combined area. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f}); + immRectf(pos_id, + MAX2(sa2->totrct.xmin, combined.xmin), + MAX2(sa2->totrct.ymin, combined.ymin), + MIN2(sa2->totrct.xmax, combined.xmax), + MIN2(sa2->totrct.ymax, combined.ymax)); + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + if (offset1 < 0 || offset2 > 0) { + /* Show partial areas that will be closed. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f}); + if (vertical) { + if (sa1->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax); + } + if (sa2->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax); + } + if (sa1->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax); + } + if (sa2->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax); + } + } + else { + if (sa1->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin); + } + if (sa2->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin); + } + if (sa1->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax); + } + if (sa2->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax); + } + } } immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); + + /* Outline the combined area. */ + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f}); } void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 7ad8eada3b9..75aa709a5d1 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -279,47 +279,44 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new) screen_new->do_draw = true; } -/* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */ -/* -1 = not valid check */ -/* used with join operator */ -int area_getorientation(ScrArea *area, ScrArea *sb) +/** + * with `sa_a` as center, `sa_b` is located at: 0=W, 1=N, 2=E, 3=S + * -1 = not valid check. + * used with join operator. + */ +int area_getorientation(ScrArea *sa_a, ScrArea *sa_b) { - if (area == NULL || sb == NULL) { + if (sa_a == NULL || sa_b == NULL || sa_a == sa_b) { return -1; } - ScrVert *saBL = area->v1; - ScrVert *saTL = area->v2; - ScrVert *saTR = area->v3; - ScrVert *saBR = area->v4; + const vec2s *sa_bl = &sa_a->v1->vec; + const vec2s *sa_tl = &sa_a->v2->vec; + const vec2s *sa_tr = &sa_a->v3->vec; + const vec2s *sa_br = &sa_a->v4->vec; - ScrVert *sbBL = sb->v1; - ScrVert *sbTL = sb->v2; - ScrVert *sbTR = sb->v3; - ScrVert *sbBR = sb->v4; + const vec2s *sb_bl = &sa_b->v1->vec; + const vec2s *sb_tl = &sa_b->v2->vec; + const vec2s *sb_tr = &sa_b->v3->vec; + const vec2s *sb_br = &sa_b->v4->vec; - if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */ - if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) && - (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) { + if (sa_bl->x == sb_br->x && sa_tl->x == sb_tr->x) { /* sa_a to right of sa_b = W */ + if ((MIN2(sa_tl->y, sb_tr->y) - MAX2(sa_bl->y, sb_br->y)) > AREAJOINTOLERANCEY) { return 0; } } - else if (saTL->vec.y == sbBL->vec.y && - saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */ - if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) { + else if (sa_tl->y == sb_bl->y && sa_tr->y == sb_br->y) { /* sa_a to bottom of sa_b = N */ + if ((MIN2(sa_tr->x, sb_br->x) - MAX2(sa_tl->x, sb_bl->x)) > AREAJOINTOLERANCEX) { return 1; } } - else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */ - if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) { + else if (sa_tr->x == sb_tl->x && sa_br->x == sb_bl->x) { /* sa_a to left of sa_b = E */ + if ((MIN2(sa_tr->y, sb_tl->y) - MAX2(sa_br->y, sb_bl->y)) > AREAJOINTOLERANCEY) { return 2; } } - else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/ - if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) { + else if (sa_bl->y == sb_tl->y && sa_br->y == sb_tr->y) { /* sa_a on top of sa_b = S */ + if ((MIN2(sa_br->x, sb_tr->x) - MAX2(sa_bl->x, sb_tl->x)) > AREAJOINTOLERANCEX) { return 3; } } @@ -327,6 +324,37 @@ int area_getorientation(ScrArea *area, ScrArea *sb) return -1; } +/** + * Get alignment offset of adjacent areas. 'dir' value is like #area_getorientation(). + */ +void area_getoffsets(ScrArea *sa_a, ScrArea *sa_b, const int dir, int *r_offset1, int *r_offset2) +{ + if (sa_a == NULL || sa_b == NULL) { + *r_offset1 = INT_MAX; + *r_offset2 = INT_MAX; + } + else if (dir == 0) { /* West: sa on right and sa_b to the left. */ + *r_offset1 = sa_b->v3->vec.y - sa_a->v2->vec.y; + *r_offset2 = sa_b->v4->vec.y - sa_a->v1->vec.y; + } + else if (dir == 1) { /* North: sa below and sa_b above. */ + *r_offset1 = sa_a->v2->vec.x - sa_b->v1->vec.x; + *r_offset2 = sa_a->v3->vec.x - sa_b->v4->vec.x; + } + else if (dir == 2) { /* East: sa on left and sa_b to the right. */ + *r_offset1 = sa_b->v2->vec.y - sa_a->v3->vec.y; + *r_offset2 = sa_b->v1->vec.y - sa_a->v4->vec.y; + } + else if (dir == 3) { /* South: sa above and sa_b below. */ + *r_offset1 = sa_a->v1->vec.x - sa_b->v2->vec.x; + *r_offset2 = sa_a->v4->vec.x - sa_b->v3->vec.x; + } + else { + *r_offset1 = INT_MAX; + *r_offset2 = INT_MAX; + } +} + /* Screen verts with horizontal position equal to from_x are moved to to_x. */ static void screen_verts_halign(const wmWindow *win, const bScreen *screen, @@ -390,18 +418,24 @@ static void screen_areas_align( } } -/* Helper function to join 2 areas, it has a return value, 0=failed 1=success - * used by the split, join operators - */ -int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +/* Simple join of two areas without any splitting. Will return false if not possible. */ +static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) { int dir = area_getorientation(sa1, sa2); - if (dir == -1) { - return 0; + return false; } - /* Align areas if they are not. Do sanity checking before getting here. */ + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + int tolerance = ELEM(dir, 0, 2) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX; + if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) { + return false; + } + + /* Align areas if they are not. */ screen_areas_align(C, screen, sa1, sa2, dir); if (dir == 0) { /* sa1 to right of sa2 = W */ @@ -434,7 +468,107 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) /* Update preview thumbnail */ BKE_icon_changed(screen->id.icon_id); - return 1; + return true; +} + +/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */ +static ScrArea *screen_area_trim( + bContext *C, bScreen *screen, ScrArea **area, int size, int dir, bool reverse) +{ + bool vertical = ELEM(dir, 1, 3); + if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) { + return NULL; + } + + /* Measurement with ScrVerts because winx and winy might not be correct at this time. */ + float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) : + ((*area)->v3->vec.y - (*area)->v1->vec.y)); + fac = (reverse == vertical) ? 1.0f - fac : fac; + ScrArea *newsa = area_split(CTX_wm_window(C), screen, *area, vertical ? 'v' : 'h', fac, 1); + + /* area_split always returns smallest of the two areas, so might have to swap. */ + if (((fac > 0.5f) == vertical) != reverse) { + ScrArea *temp = *area; + *area = newsa; + newsa = temp; + } + + return newsa; +} + +/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */ +static bool screen_area_join_ex( + bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders) +{ + int dir = area_getorientation(sa1, sa2); + if (dir == -1) { + return false; + } + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + /* Split Left/Top into new area if overhanging. */ + ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false); + + /* Split Right/Bottom into new area if overhanging. */ + ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true); + + /* The two areas now line up, so join them. */ + screen_area_join_aligned(C, screen, sa1, sa2); + + if (close_all_remainders || offset1 < 0 || offset2 > 0) { + /* Close both if trimming `sa1`. */ + screen_area_close(C, screen, side1); + screen_area_close(C, screen, side2); + } + + BKE_icon_changed(screen->id.icon_id); + return true; +} + +/* Join any two neighboring areas. Might involve complex changes. */ +int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +{ + return screen_area_join_ex(C, screen, sa1, sa2, false); +} + +/* Close a screen area, allowing any neighbor to take its place. */ +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area) +{ + if (area == NULL) { + return false; + } + + ScrArea *sa2 = NULL; + + /* Find the most-aligned joinable area. Larger size breaks ties. */ + int min_alignment = INT_MAX; + int max_size = 0; + LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) { + int dir = area_getorientation(area, ar); + if (dir != -1) { + int offset1; + int offset2; + area_getoffsets(area, ar, dir, &offset1, &offset2); + int area_alignment = abs(offset1) + abs(offset2); + if (area_alignment < min_alignment) { + min_alignment = area_alignment; + max_size = ar->winx * ar->winy; + sa2 = ar; + } + else if (area_alignment == min_alignment) { + int area_size = ar->winx * ar->winy; + if (area_size > max_size) { + max_size = area_size; + sa2 = ar; + } + } + } + } + + return screen_area_join_ex(C, screen, sa2, area, true); } /* ****************** EXPORTED API TO OTHER MODULES *************************** */ diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index c51ff559786..dd196f9621b 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -34,7 +34,9 @@ struct bContextDataResult; #define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */ #define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */ -#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */ +/* Edges must be within these to allow joining. */ +#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac) +#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac) /* Expanded interaction influence of area borders. */ #define BORDERPADDING (U.dpi_fac + U.pixelsize) @@ -57,8 +59,9 @@ void screen_change_prepare(bScreen *screen_old, ScrArea *area_split( const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge); int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2); -int area_getorientation(ScrArea *area, ScrArea *sb); - +int area_getorientation(ScrArea *sa_a, ScrArea *sa_b); +void area_getoffsets(ScrArea *sa_a, ScrArea *sa_b, const int dir, int *r_offset1, int *r_offset2); +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area); struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]); /* screen_geometry.c */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 765877d6a8e..eadc18de443 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1395,6 +1395,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Area Close Operator + * + * Close selected area, replace by expanding a neighbor + * \{ */ + +/* operator callback */ +static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +{ + ScrArea *area = CTX_wm_area(C); + if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) { + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool area_close_poll(bContext *C) +{ + if (!ED_operator_areaactive(C)) { + return false; + } + + ScrArea *area = CTX_wm_area(C); + + if (ED_area_is_global(area)) { + return false; + } + + bScreen *screen = CTX_wm_screen(C); + + /* Can this area join with ANY other area? */ + LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) { + if (area_getorientation(ar, area) != -1) { + return true; + } + } + + return false; +} + +static void SCREEN_OT_area_close(wmOperatorType *ot) +{ + ot->name = "Close Area"; + ot->description = "Close selected area"; + ot->idname = "SCREEN_OT_area_close"; + ot->invoke = area_close_invoke; + ot->poll = area_close_poll; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Move Area Edge Operator * \{ */ @@ -3218,9 +3270,10 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot) */ typedef struct sAreaJoinData { - ScrArea *sa1; /* first area to be considered */ - ScrArea *sa2; /* second area to be considered */ - void *draw_callback; /* call `ED_screen_draw_join_shape` */ + ScrArea *sa1; /* Potential source area (kept). */ + ScrArea *sa2; /* Potential target area (removed or reduced). */ + int dir; /* Direction of potential join. */ + void *draw_callback; /* call 'ED_screen_draw_join_highlight' */ } sAreaJoinData; @@ -3229,8 +3282,8 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata const wmOperator *op = userdata; sAreaJoinData *sd = op->customdata; - if (sd->sa1 && sd->sa2) { - ED_screen_draw_join_shape(sd->sa1, sd->sa2); + if (sd->sa1 && sd->sa2 && (sd->dir != -1)) { + ED_screen_draw_join_highlight(sd->sa1, sd->sa2); } } @@ -3252,6 +3305,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s jd->sa1 = sa1; jd->sa2 = sa2; + jd->dir = -1; op->customdata = jd; @@ -3264,7 +3318,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s static bool area_join_apply(bContext *C, wmOperator *op) { sAreaJoinData *jd = (sAreaJoinData *)op->customdata; - if (!jd) { + if (!jd || (jd->dir == -1)) { return false; } @@ -3366,61 +3420,30 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: { ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); - int dir = -1; + jd->dir = area_getorientation(jd->sa1, jd->sa2); - if (area) { - if (jd->sa1 != area) { - dir = area_getorientation(jd->sa1, area); - if (dir != -1) { - jd->sa2 = area; - } - else { - /* we are not bordering on the previously selected area - * we check if area has common border with the one marked for removal - * in this case we can swap areas. - */ - dir = area_getorientation(area, jd->sa2); - if (dir != -1) { - jd->sa1 = jd->sa2; - jd->sa2 = area; - } - else { - jd->sa2 = NULL; - } - } - WM_event_add_notifier(C, NC_WINDOW, NULL); - } - else { - /* we are back in the area previously selected for keeping - * we swap the areas if possible to allow user to choose */ - if (jd->sa2 != NULL) { - jd->sa1 = jd->sa2; - jd->sa2 = area; - dir = area_getorientation(jd->sa1, jd->sa2); - if (dir == -1) { - printf("oops, didn't expect that!\n"); - } - } - else { - dir = area_getorientation(jd->sa1, area); - if (dir != -1) { - jd->sa2 = area; - } - } - WM_event_add_notifier(C, NC_WINDOW, NULL); - } + if (area == jd->sa1) { + /* Hovering current source, so change direction. */ + jd->sa1 = jd->sa2; + jd->sa2 = area; + jd->dir = area_getorientation(jd->sa1, jd->sa2); + } + else if (area != jd->sa2) { + jd->dir = -1; } - if (dir == 1) { + WM_event_add_notifier(C, NC_WINDOW, NULL); + + if (jd->dir == 1) { WM_cursor_set(win, WM_CURSOR_N_ARROW); } - else if (dir == 3) { + else if (jd->dir == 3) { WM_cursor_set(win, WM_CURSOR_S_ARROW); } - else if (dir == 2) { + else if (jd->dir == 2) { WM_cursor_set(win, WM_CURSOR_E_ARROW); } - else if (dir == 0) { + else if (jd->dir == 0) { WM_cursor_set(win, WM_CURSOR_W_ARROW); } else { @@ -3431,6 +3454,10 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) } case LEFTMOUSE: if (event->val == KM_RELEASE) { + if (jd->dir == -1) { + area_join_cancel(C, op); + return OPERATOR_CANCELLED; + } ED_area_tag_redraw(jd->sa1); ED_area_tag_redraw(jd->sa2); @@ -4077,6 +4104,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \name Region Context Menu Operator (Header/Footer/Navbar) * \{ */ +static void screen_area_menu_items(ScrArea *area, uiLayout *layout) +{ + if (ED_area_is_global(area)) { + return; + } + + PointerRNA ptr; + + /* Mouse position as if in middle of area. */ + const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)}; + + /* Vertical Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Vertical Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", loc); + RNA_enum_set(&ptr, "direction", 'v'); + + /* Horizontal Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Horizontal Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", &loc[0]); + RNA_enum_set(&ptr, "direction", 'h'); + + uiItemS(layout); + + if (area->spacetype != SPACE_FILE) { + uiItemO(layout, + area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"), + ICON_NONE, + "SCREEN_OT_screen_full_area"); + + if (!area->full) { + uiItemFullO(layout, + "SCREEN_OT_screen_full_area", + IFACE_("Full Screen Area"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + RNA_boolean_set(&ptr, "use_hide_panels", true); + } + } + + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli"); + uiItemS(layout); + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close"); +} + void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) { ScrArea *area = CTX_wm_area(C); @@ -4110,17 +4200,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN if (!ELEM(area->spacetype, SPACE_TOPBAR)) { uiItemS(layout); - uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - } - - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored. */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); + screen_area_menu_items(area, layout); } } @@ -4142,14 +4224,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored... */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { - uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); - } + uiItemS(layout); + screen_area_menu_items(area, layout); } void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) @@ -5469,6 +5545,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_area_move); WM_operatortype_append(SCREEN_OT_area_split); WM_operatortype_append(SCREEN_OT_area_join); + WM_operatortype_append(SCREEN_OT_area_close); WM_operatortype_append(SCREEN_OT_area_options); WM_operatortype_append(SCREEN_OT_area_dupli); WM_operatortype_append(SCREEN_OT_area_swap); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 3df63d423e2..6df96b1e30f 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -199,10 +199,9 @@ static bool screenshot_draw_check_prop(PointerRNA *UNUSED(ptr), return !(STREQ(prop_id, "filepath")); } -static void screenshot_draw(bContext *C, wmOperator *op) +static void screenshot_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); ScreenshotData *scd = op->customdata; uiLayoutSetPropSep(layout, true); @@ -214,9 +213,8 @@ static void screenshot_draw(bContext *C, wmOperator *op) uiTemplateImageSettings(layout, &ptr, false); /* main draw call */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); } static bool screenshot_poll(bContext *C) diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 309b280177c..f1d0197b9ae 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -63,6 +63,7 @@ void FILE_OT_bookmark_move(struct wmOperatorType *ot); void FILE_OT_reset_recent(wmOperatorType *ot); void FILE_OT_hidedot(struct wmOperatorType *ot); void FILE_OT_execute(struct wmOperatorType *ot); +void FILE_OT_mouse_execute(struct wmOperatorType *ot); void FILE_OT_cancel(struct wmOperatorType *ot); void FILE_OT_parent(struct wmOperatorType *ot); void FILE_OT_directory_new(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 856bd5b1bc3..61f3c046550 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -536,6 +536,14 @@ void FILE_OT_select_box(wmOperatorType *ot) /** \name Select Pick Operator * \{ */ +static rcti file_select_mval_to_select_rect(const int mval[2]) +{ + rcti rect; + rect.xmin = rect.xmax = mval[0]; + rect.ymin = rect.ymax = mval[1]; + return rect; +} + static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); @@ -551,8 +559,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - rect.xmin = rect.xmax = event->mval[0]; - rect.ymin = rect.ymax = event->mval[1]; + rect = file_select_mval_to_select_rect(event->mval); if (!ED_fileselect_layout_is_inside_pt(sfile->layout, ®ion->v2d, rect.xmin, rect.ymin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; @@ -1711,14 +1718,14 @@ bool file_draw_check_exists(SpaceFile *sfile) /** \name Execute File Window Operator * \{ */ -static int file_exec(bContext *C, wmOperator *exec_op) +/** + * Execute the active file, as set in the file select params. + */ +static bool file_execute(bContext *C, SpaceFile *sfile) { Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_active_params(sfile); - struct FileDirEntry *file = filelist_file(sfile->files, params->active_file); - char filepath[FILE_MAX]; + FileDirEntry *file = filelist_file(sfile->files, params->active_file); if (file && file->redirection_path) { /* redirection_path is an absolute path that takes precedence @@ -1753,22 +1760,7 @@ static int file_exec(bContext *C, wmOperator *exec_op) /* opening file - sends events now, so things get handled on windowqueue level */ else if (sfile->op) { wmOperator *op = sfile->op; - - /* When used as a macro, for double-click, to prevent closing when double-clicking on item. */ - if (RNA_boolean_get(exec_op->ptr, "need_active")) { - const int numfiles = filelist_files_ensure(sfile->files); - int i, active = 0; - - for (i = 0; i < numfiles; i++) { - if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) { - active = 1; - break; - } - } - if (active == 0) { - return OPERATOR_CANCELLED; - } - } + char filepath[FILE_MAX]; sfile->op = NULL; @@ -1788,50 +1780,94 @@ static int file_exec(bContext *C, wmOperator *exec_op) BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); fsmenu_write_file(ED_fsmenu_get(), filepath); - WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC); + WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC); } return OPERATOR_FINISHED; } -static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int file_exec(bContext *C, wmOperator *UNUSED(op)) { - ARegion *region = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); - if (!ED_fileselect_layout_is_inside_pt( - sfile->layout, ®ion->v2d, event->mval[0], event->mval[1])) { - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + if (!file_execute(C, sfile)) { + return OPERATOR_CANCELLED; } - return file_exec(C, op); + return OPERATOR_FINISHED; } void FILE_OT_execute(struct wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Execute File Window"; ot->description = "Execute selected file"; ot->idname = "FILE_OT_execute"; /* api callbacks */ - ot->invoke = file_exec_invoke; ot->exec = file_exec; /* Important since handler is on window level. * * Avoid using #file_operator_poll since this is also used for entering directories * which is used even when the file manager doesn't have an operator. */ ot->poll = ED_operator_file_active; +} - /* properties */ - prop = RNA_def_boolean(ot->srna, - "need_active", - 0, - "Need Active", - "Only execute if there's an active selected file in the file list"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); +/** + * \returns false if the mouse doesn't hover a selectable item. + */ +static bool file_ensure_hovered_is_active(bContext *C, const wmEvent *event) +{ + rcti rect = file_select_mval_to_select_rect(event->mval); + if (file_select(C, &rect, FILE_SEL_ADD, false, false) == FILE_SELECT_NOTHING) { + return false; + } + + return true; +} + +static int file_execute_mouse_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + if (!ED_fileselect_layout_is_inside_pt( + sfile->layout, ®ion->v2d, event->mval[0], event->mval[1])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + /* Note that this isn't needed practically, because the keymap already activates the hovered item + * on mouse-press. This execute operator is called afterwards on the double-click event then. + * However relying on this would be fragile and could break with keymap changes, so better to + * have this mouse-execute operator that makes sure once more that the hovered file is active. */ + if (!file_ensure_hovered_is_active(C, event)) { + return OPERATOR_CANCELLED; + } + + if (!file_execute(C, sfile)) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +/** + * Variation of #FILE_OT_execute that accounts for some mouse specific handling. Otherwise calls + * the same logic. + */ +void FILE_OT_mouse_execute(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Execute File"; + ot->description = + "Perform the current execute action for the file under the cursor (e.g. open the file)"; + ot->idname = "FILE_OT_mouse_execute"; + + /* api callbacks */ + ot->invoke = file_execute_mouse_invoke; + ot->poll = ED_operator_file_active; + + ot->flag = OPTYPE_INTERNAL; } /** \} */ diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 993b1d9b69c..12bc0a68ca6 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -467,6 +467,15 @@ static void file_listener(const wmSpaceTypeListenerParams *params) break; } break; + case NC_ID: { + switch (wmn->action) { + case NA_RENAME: + /* Force list to update sorting (with a full reset for now). */ + file_reset_filelist_showing_main_data(area, sfile); + break; + } + break; + } case NC_ASSET: { switch (wmn->action) { case NA_SELECTED: @@ -654,6 +663,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_highlight); WM_operatortype_append(FILE_OT_sort_column_ui_context); WM_operatortype_append(FILE_OT_execute); + WM_operatortype_append(FILE_OT_mouse_execute); WM_operatortype_append(FILE_OT_cancel); WM_operatortype_append(FILE_OT_parent); WM_operatortype_append(FILE_OT_previous); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 83dd5098a30..6053253790a 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1479,18 +1479,16 @@ static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), return !(STR_ELEM(prop_id, "filepath", "directory", "filename")); } -static void image_open_draw(bContext *C, wmOperator *op) +static void image_open_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); ImageOpenData *iod = op->customdata; ImageFormatData *imf = &iod->im_format; - PointerRNA imf_ptr, ptr; + PointerRNA imf_ptr; /* main draw call */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* image template */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); @@ -2001,12 +1999,11 @@ static bool image_save_as_draw_check_prop(PointerRNA *ptr, ((STREQ(prop_id, "relative_path")) && RNA_boolean_get(ptr, "copy"))); } -static void image_save_as_draw(bContext *C, wmOperator *op) +static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); ImageSaveData *isd = op->customdata; - PointerRNA imf_ptr, ptr; + PointerRNA imf_ptr; const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview"); /* image template */ @@ -2014,9 +2011,8 @@ static void image_save_as_draw(bContext *C, wmOperator *op) uiTemplateImageSettings(layout, &imf_ptr, false); /* main draw call */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* multiview template */ if (is_multiview) { @@ -2614,38 +2610,34 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e return WM_operator_props_dialog_popup(C, op, 300); } -static void image_new_draw(bContext *C, wmOperator *op) +static void image_new_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *col; uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; #if 0 Scene *scene = CTX_data_scene(C); const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; #endif - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - /* copy of WM_operator_props_dialog_popup() layout */ uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); col = uiLayoutColumn(layout, false); - uiItemR(col, &ptr, "name", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "width", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "height", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "color", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "alpha", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "generated_type", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "float", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "tiled", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "name", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "color", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "generated_type", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "float", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "tiled", 0, NULL, ICON_NONE); #if 0 if (is_multiview) { uiItemL(col[0], "", ICON_NONE); - uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE); + uiItemR(col[1], op->ptr, "use_stereo_3d", 0, NULL, ICON_NONE); } #endif } @@ -3991,26 +3983,22 @@ static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev return WM_operator_props_dialog_popup(C, op, 300); } -static void tile_add_draw(bContext *C, wmOperator *op) +static void tile_add_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *col; uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); col = uiLayoutColumn(layout, false); - uiItemR(col, &ptr, "number", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "count", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "label", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "fill", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "number", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "count", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "label", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "fill", 0, NULL, ICON_NONE); - if (RNA_boolean_get(&ptr, "fill")) { - draw_fill_tile(&ptr, layout); + if (RNA_boolean_get(op->ptr, "fill")) { + draw_fill_tile(op->ptr, layout); } } @@ -4128,13 +4116,9 @@ static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e return WM_operator_props_dialog_popup(C, op, 300); } -static void tile_fill_draw(bContext *C, wmOperator *op) +static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - draw_fill_tile(&ptr, op->layout); + draw_fill_tile(op->ptr, op->layout); } void IMAGE_OT_tile_fill(wmOperatorType *ot) diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 0583628be43..aaf9852e212 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -56,7 +56,9 @@ #include "info_intern.h" -/********************* pack blend file libraries operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Pack Blend File Libraries Operator + * \{ */ static int pack_libraries_exec(bContext *C, wmOperator *op) { @@ -70,9 +72,11 @@ static int pack_libraries_exec(bContext *C, wmOperator *op) void FILE_OT_pack_libraries(wmOperatorType *ot) { /* identifiers */ - ot->name = "Pack Blender Libraries"; + ot->name = "Pack Linked Libraries"; ot->idname = "FILE_OT_pack_libraries"; - ot->description = "Pack all used Blender library files into the current .blend"; + ot->description = + "Store all data-blocks linked from other .blend files in the current .blend file. " + "Library references are preserved so the linked data-blocks can be unpacked again"; /* api callbacks */ ot->exec = pack_libraries_exec; @@ -90,18 +94,24 @@ static int unpack_libraries_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack Blend File Libraries Operator + * \{ */ + static int unpack_libraries_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { return WM_operator_confirm_message( - C, op, "Unpack Blender Libraries - creates directories, all new paths should work"); + C, op, "Unpack Linked Libraries - creates directories, all new paths should work"); } void FILE_OT_unpack_libraries(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unpack Blender Libraries"; + ot->name = "Unpack Linked Libraries"; ot->idname = "FILE_OT_unpack_libraries"; - ot->description = "Unpack all used Blender library files from this .blend file"; + ot->description = "Restore all packed linked data-blocks to their original locations"; /* api callbacks */ ot->invoke = unpack_libraries_invoke; @@ -111,7 +121,11 @@ void FILE_OT_unpack_libraries(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* toggle auto-pack operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Auto-Pack Operator + * \{ */ static int autopack_toggle_exec(bContext *C, wmOperator *op) { @@ -131,7 +145,7 @@ static int autopack_toggle_exec(bContext *C, wmOperator *op) void FILE_OT_autopack_toggle(wmOperatorType *ot) { /* identifiers */ - ot->name = "Automatically Pack Into .blend"; + ot->name = "Automatically Pack Resources"; ot->idname = "FILE_OT_autopack_toggle"; ot->description = "Automatically pack all external files into the .blend file"; @@ -142,7 +156,11 @@ void FILE_OT_autopack_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* pack all operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pack All Operator + * \{ */ static int pack_all_exec(bContext *C, wmOperator *op) { @@ -176,9 +194,9 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev void FILE_OT_pack_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Pack All Into .blend"; + ot->name = "Pack Resources"; ot->idname = "FILE_OT_pack_all"; - ot->description = "Pack all used external files into the .blend"; + ot->description = "Pack all used external files into this .blend"; /* api callbacks */ ot->exec = pack_all_exec; @@ -188,7 +206,11 @@ void FILE_OT_pack_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* unpack all operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack All Operator + * \{ */ static const EnumPropertyItem unpack_all_method_items[] = { {PF_USE_LOCAL, "USE_LOCAL", 0, "Use files in current directory (create when necessary)", ""}, @@ -263,7 +285,7 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( void FILE_OT_unpack_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unpack All Into Files"; + ot->name = "Unpack Resources"; ot->idname = "FILE_OT_unpack_all"; ot->description = "Unpack all files packed into this .blend to external ones"; @@ -279,7 +301,11 @@ void FILE_OT_unpack_all(wmOperatorType *ot) ot->srna, "method", unpack_all_method_items, PF_USE_LOCAL, "Method", "How to unpack"); } -/********************* unpack single item operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack Single Item Operator + * \{ */ static const EnumPropertyItem unpack_item_method_items[] = { {PF_USE_LOCAL, "USE_LOCAL", 0, "Use file from current directory (create when necessary)", ""}, @@ -373,7 +399,11 @@ void FILE_OT_unpack_item(wmOperatorType *ot) INT_MAX); } -/********************* make paths relative operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Paths Relative Operator + * \{ */ static int make_paths_relative_exec(bContext *C, wmOperator *op) { @@ -395,7 +425,7 @@ static int make_paths_relative_exec(bContext *C, wmOperator *op) void FILE_OT_make_paths_relative(wmOperatorType *ot) { /* identifiers */ - ot->name = "Make All Paths Relative"; + ot->name = "Make Paths Relative"; ot->idname = "FILE_OT_make_paths_relative"; ot->description = "Make all paths to external files relative to current .blend"; @@ -406,7 +436,11 @@ void FILE_OT_make_paths_relative(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* make paths absolute operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Paths Absolute Operator + * \{ */ static int make_paths_absolute_exec(bContext *C, wmOperator *op) { @@ -428,7 +462,7 @@ static int make_paths_absolute_exec(bContext *C, wmOperator *op) void FILE_OT_make_paths_absolute(wmOperatorType *ot) { /* identifiers */ - ot->name = "Make All Paths Absolute"; + ot->name = "Make Paths Absolute"; ot->idname = "FILE_OT_make_paths_absolute"; ot->description = "Make all paths to external files absolute"; @@ -439,7 +473,11 @@ void FILE_OT_make_paths_absolute(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* report missing files operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Report Missing Files Operator + * \{ */ static int report_missing_files_exec(bContext *C, wmOperator *op) { @@ -465,7 +503,11 @@ void FILE_OT_report_missing_files(wmOperatorType *ot) ot->flag = 0; /* only reports so no need to undo/register */ } -/********************* find missing files operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Find Missing Files Operator + * \{ */ static int find_missing_files_exec(bContext *C, wmOperator *op) { @@ -516,7 +558,11 @@ void FILE_OT_find_missing_files(wmOperatorType *ot) FILE_SORT_DEFAULT); } -/********************* report box operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Report Box Operator + * \{ */ /* Hard to decide whether to keep this as an operator, * or turn it into a hardcoded ui control feature, @@ -621,3 +667,5 @@ void INFO_OT_reports_display_update(wmOperatorType *ot) } /* report operators */ + +/** \} */ diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index ffac5c982d6..0bdfceb36b6 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -697,7 +697,8 @@ void ED_info_draw_stats( UI_FontThemeColor(font_id, TH_TEXT_HI); BLF_enable(font_id, BLF_SHADOW); - BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f}); + const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + BLF_shadow(font_id, 5, shadow_color); BLF_shadow_offset(font_id, 1, -1); /* Translated labels for each stat row. */ diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index dfc0abee704..e56bb44b1e6 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -255,11 +255,10 @@ static void info_header_region_message_subscribe(const wmRegionMessageSubscribeP struct wmMsgBus *mbus = params->message_bus; ARegion *region = params->region; - wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { - .owner = region, - .user_data = region, - .notify = ED_region_do_msg_notify_tag_redraw, - }; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {NULL}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw); WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw); diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 5d774049e3e..728be1ccaaf 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -155,6 +155,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params) case NC_OBJECT: switch (wmn->data) { case ND_TRANSFORM: + ED_region_tag_redraw_no_rebuild(region); + break; case ND_BONE_ACTIVE: case ND_BONE_SELECT: case ND_DRAW: diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index a61260acfb2..cd75bc98b15 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -754,18 +754,16 @@ static int sequencer_add_movie_strip_invoke(bContext *C, return OPERATOR_RUNNING_MODAL; } -static void sequencer_add_draw(bContext *C, wmOperator *op) +static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); SequencerAddData *sad = op->customdata; ImageFormatData *imf = &sad->im_format; - PointerRNA imf_ptr, ptr; + PointerRNA imf_ptr; /* Main draw call. */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* Image template. */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 5d616969a4f..7279fdd2da2 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1485,26 +1485,22 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev return sequencer_split_exec(C, op); } -static void sequencer_split_ui(bContext *C, wmOperator *op) +static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiLayout *row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "frame", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "side", 0, NULL, ICON_NONE); uiItemS(layout); - uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE); - if (RNA_boolean_get(&ptr, "use_cursor_position")) { - uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_cursor_position", 0, NULL, ICON_NONE); + if (RNA_boolean_get(op->ptr, "use_cursor_position")) { + uiItemR(layout, op->ptr, "channel", 0, NULL, ICON_NONE); } } @@ -2278,43 +2274,45 @@ void SEQUENCER_OT_swap(wmOperatorType *ot) static int sequencer_rendersize_exec(bContext *C, wmOperator *UNUSED(op)) { - int retval = OPERATOR_CANCELLED; Scene *scene = CTX_data_scene(C); Sequence *active_seq = SEQ_select_active_get(scene); StripElem *se = NULL; - if (active_seq == NULL) { + if (active_seq == NULL || active_seq->strip == NULL) { return OPERATOR_CANCELLED; } - if (active_seq->strip) { - switch (active_seq->type) { - case SEQ_TYPE_IMAGE: - se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); - break; - case SEQ_TYPE_MOVIE: - se = active_seq->strip->stripdata; - break; - case SEQ_TYPE_SCENE: - case SEQ_TYPE_META: - case SEQ_TYPE_SOUND_RAM: - case SEQ_TYPE_SOUND_HD: - default: - break; - } + switch (active_seq->type) { + case SEQ_TYPE_IMAGE: + se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); + break; + case SEQ_TYPE_MOVIE: + se = active_seq->strip->stripdata; + break; + default: + return OPERATOR_CANCELLED; } - if (se) { - /* Prevent setting the render size if sequence values aren't initialized. */ - if ((se->orig_width > 0) && (se->orig_height > 0)) { - scene->r.xsch = se->orig_width; - scene->r.ysch = se->orig_height; - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); - retval = OPERATOR_FINISHED; - } + if (se == NULL) { + return OPERATOR_CANCELLED; + } + + /* Prevent setting the render size if sequence values aren't initialized. */ + if (se->orig_width <= 0 || se->orig_height <= 0) { + return OPERATOR_CANCELLED; } - return retval; + scene->r.xsch = se->orig_width; + scene->r.ysch = se->orig_height; + + active_seq->strip->transform->scale_x = active_seq->strip->transform->scale_y = 1.0f; + active_seq->strip->transform->xofs = active_seq->strip->transform->yofs = 0.0f; + + SEQ_relations_invalidate_cache_preprocessed(scene, active_seq); + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + return OPERATOR_FINISHED; } void SEQUENCER_OT_rendersize(wmOperatorType *ot) diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index acdf30d0ea7..1f0b5d5d13e 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -256,7 +256,7 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C) return {}; } Object *object_orig = (Object *)used_id; - if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) { + if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { return {}; } Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig); 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 018431225e5..02ffa1259fc 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -57,44 +57,45 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( { std::lock_guard lock{mutex_}; - bke::ReadAttributePtr attribute_ptr = component_->attribute_try_get_for_read(column_id.name); - if (!attribute_ptr) { + bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); + if (!attribute) { return {}; } - const bke::ReadAttribute *attribute = scope_.add(std::move(attribute_ptr), __func__); - if (attribute->domain() != domain_) { + const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__); + if (attribute.domain != domain_) { return {}; } - int domain_size = attribute->size(); - switch (attribute->custom_data_type()) { + int domain_size = varray->size(); + const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type()); + switch (type) { case CD_PROP_FLOAT: return column_values_from_function( - column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) { + column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { float value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_float = value; }); case CD_PROP_INT32: return column_values_from_function( - column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) { + column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { int value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_int = value; }); case CD_PROP_BOOL: return column_values_from_function( - column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) { + column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { bool value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_bool = value; }); case CD_PROP_FLOAT2: { return column_values_from_function( column_id.name, domain_size, - [attribute](int index, CellValue &r_cell_value) { + [varray](int index, CellValue &r_cell_value) { float2 value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_float2 = value; }, default_float2_column_width); @@ -103,9 +104,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( return column_values_from_function( column_id.name, domain_size, - [attribute](int index, CellValue &r_cell_value) { + [varray](int index, CellValue &r_cell_value) { float3 value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_float3 = value; }, default_float3_column_width); @@ -114,9 +115,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( return column_values_from_function( column_id.name, domain_size, - [attribute](int index, CellValue &r_cell_value) { + [varray](int index, CellValue &r_cell_value) { Color4f value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_color = value; }, default_color_column_width); @@ -260,7 +261,7 @@ void InstancesDataSource::foreach_default_column_ids( SpreadsheetColumnID column_id; column_id.name = (char *)"Name"; fn(column_id); - for (const char *name : {"Position", "Rotation", "Scale"}) { + for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { column_id.name = (char *)name; fn(column_id); } @@ -275,25 +276,31 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( const int size = this->tot_rows(); if (STREQ(column_id.name, "Name")) { - Span<InstancedData> instance_data = component_->instanced_data(); + Span<int> reference_handles = component_->instance_reference_handles(); + Span<InstanceReference> references = component_->references(); std::unique_ptr<ColumnValues> values = column_values_from_function( - "Name", size, [instance_data](int index, CellValue &r_cell_value) { - const InstancedData &data = instance_data[index]; - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - if (data.data.object != nullptr) { - r_cell_value.value_object = ObjectCellValue{data.data.object}; + "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) { + const InstanceReference &reference = references[reference_handles[index]]; + switch (reference.type()) { + case InstanceReference::Type::Object: { + Object &object = reference.object(); + r_cell_value.value_object = ObjectCellValue{&object}; + break; } - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - if (data.data.collection != nullptr) { - r_cell_value.value_collection = CollectionCellValue{data.data.collection}; + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); + r_cell_value.value_collection = CollectionCellValue{&collection}; + break; + } + case InstanceReference::Type::None: { + break; } } }); values->default_width = 8.0f; return values; } - Span<float4x4> transforms = component_->transforms(); + Span<float4x4> transforms = component_->instance_transforms(); if (STREQ(column_id.name, "Position")) { return column_values_from_function( column_id.name, @@ -321,6 +328,15 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( }, default_float3_column_width); } + Span<int> ids = component_->instance_ids(); + if (STREQ(column_id.name, "ID")) { + /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ + return column_values_from_function( + column_id.name, + size, + [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, + 5.5f); + } return {}; } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 5f3d71cc190..967ad966320 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -2507,7 +2507,7 @@ static void view_dolly_to_vector_3d(ARegion *region, madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac)); } -static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const short zoom_invert) +static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert) { float zfac = 1.0; @@ -5065,7 +5065,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = false, + .edit_mode_type = SNAP_GEOM_FINAL, .use_occlusion_test = true, }, mval_fl, diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index e202276831c..3134553c159 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -338,7 +338,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_CAGE, }, mval_fl, NULL, @@ -352,7 +352,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, depsgraph, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_CAGE, }, ray_start, ray_normal, @@ -1124,12 +1124,13 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup) const wmGizmoType *gzt_snap; gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true); gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL); + RNA_enum_set(gizmo->ptr, "snap_elements_force", (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */ SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT)); - + ED_gizmotypes_snap_3d_flag_set(gizmo, ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE); WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}); wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 7347e528edc..00e30f8afae 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -323,7 +323,7 @@ static bool idp_poject_surface_normal(SnapObjectContext *snap_context, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, }, mval_fl, NULL, @@ -941,7 +941,7 @@ static void view3d_interactive_add_calc_plane(bContext *C, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, }, mval_fl, NULL, @@ -2061,7 +2061,7 @@ static void cursor_plane_draw(bContext *C, int x, int y, void *customdata) GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); - const float scale_mod = U.gizmo_size * 2 * U.dpi_fac; + const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; float final_scale = (scale_mod * pixel_size); diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index cbd65e3175d..ab4cf0c2135 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -407,7 +407,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, /* Avoid having to convert the edit-mesh to a regular mesh. */ - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, }, ray_start, ray_normal, diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 2fbcbe22349..bb3afd77dba 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -74,7 +74,7 @@ static void drawTransformApply(const struct bContext *C, ARegion *region, void *arg); -static void initSnapSpatial(TransInfo *t, float r_snap[3]); +static void initSnapSpatial(TransInfo *t, float r_snap[2]); bool transdata_check_local_islands(TransInfo *t, short around) { diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f2d5800abb7..090b8b83dcc 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -88,12 +88,11 @@ typedef enum { CTX_TEXTURE_SPACE = (1 << 9), CTX_NO_PET = (1 << 10), - CTX_NO_MIRROR = (1 << 11), - CTX_AUTOCONFIRM = (1 << 12), + CTX_AUTOCONFIRM = (1 << 11), /** When transforming object's, adjust the object data so it stays in the same place. */ - CTX_OBMODE_XFORM_OBDATA = (1 << 13), + CTX_OBMODE_XFORM_OBDATA = (1 << 12), /** Transform object parents without moving their children. */ - CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14), + CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13), } eTContext; /** #TransInfo.flag */ @@ -105,51 +104,50 @@ typedef enum { /** restrictions flags */ T_NO_CONSTRAINT = 1 << 2, T_NULL_ONE = 1 << 3, - T_NO_ZERO = 1 << 4, - T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE | T_NO_ZERO, + T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE, - T_PROP_EDIT = 1 << 5, - T_PROP_CONNECTED = 1 << 6, - T_PROP_PROJECTED = 1 << 7, + T_PROP_EDIT = 1 << 4, + T_PROP_CONNECTED = 1 << 5, + T_PROP_PROJECTED = 1 << 6, T_PROP_EDIT_ALL = T_PROP_EDIT | T_PROP_CONNECTED | T_PROP_PROJECTED, - T_V3D_ALIGN = 1 << 8, + T_V3D_ALIGN = 1 << 7, /** For 2D views such as UV or f-curve. */ - T_2D_EDIT = 1 << 9, - T_CLIP_UV = 1 << 10, + T_2D_EDIT = 1 << 8, + T_CLIP_UV = 1 << 9, /** Auto-IK is on. */ - T_AUTOIK = 1 << 11, + T_AUTOIK = 1 << 10, /** Don't use mirror even if the data-block option is set. */ - T_NO_MIRROR = 1 << 12, + T_NO_MIRROR = 1 << 11, /** To indicate that the value set in the `value` parameter is the final * value of the transformation, modified only by the constrain. */ - T_INPUT_IS_VALUES_FINAL = 1 << 13, + T_INPUT_IS_VALUES_FINAL = 1 << 12, /** To specify if we save back settings at the end. */ - T_MODAL = 1 << 14, + T_MODAL = 1 << 13, /** No re-topology (projection). */ - T_NO_PROJECT = 1 << 15, + T_NO_PROJECT = 1 << 14, - T_RELEASE_CONFIRM = 1 << 16, + T_RELEASE_CONFIRM = 1 << 15, /** Alternative transformation. used to add offset to tracking markers. */ - T_ALT_TRANSFORM = 1 << 17, + T_ALT_TRANSFORM = 1 << 16, /** #TransInfo.center has been set, don't change it. */ - T_OVERRIDE_CENTER = 1 << 18, + T_OVERRIDE_CENTER = 1 << 17, - T_MODAL_CURSOR_SET = 1 << 19, + T_MODAL_CURSOR_SET = 1 << 18, - T_CLNOR_REBUILD = 1 << 20, + T_CLNOR_REBUILD = 1 << 19, /** Merges unselected into selected after transforming (runs after transforming). */ - T_AUTOMERGE = 1 << 21, + T_AUTOMERGE = 1 << 20, /** Runs auto-merge & splits. */ - T_AUTOSPLIT = 1 << 22, + T_AUTOSPLIT = 1 << 21, } eTFlag; /** #TransInfo.modifiers */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index c021c084a23..98db27c83f7 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1122,8 +1122,7 @@ static void init_TransDataContainers(TransInfo *t, for (int i = 0; i < objects_len; i++) { TransDataContainer *tc = &t->data_container[i]; - if (((t->flag & T_NO_MIRROR) == 0) && ((t->options & CTX_NO_MIRROR) == 0) && - (objects[i]->type == OB_MESH)) { + if (!(t->flag & T_NO_MIRROR) && (objects[i]->type == OB_MESH)) { tc->use_mirror_axis_x = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_X) != 0; tc->use_mirror_axis_y = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Y) != 0; tc->use_mirror_axis_z = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Z) != 0; @@ -1472,91 +1471,89 @@ void createTransData(bContext *C, TransInfo *t) /** \name Transform Data Recalc/Flush * \{ */ -void clipMirrorModifier(TransInfo *t) +void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc) { - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - Object *ob = tc->obedit; - ModifierData *md = ob->modifiers.first; - float tolerance[3] = {0.0f, 0.0f, 0.0f}; - int axis = 0; - - for (; md; md = md->next) { - if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { - MirrorModifierData *mmd = (MirrorModifierData *)md; - - if (mmd->flag & MOD_MIR_CLIPPING) { - axis = 0; - if (mmd->flag & MOD_MIR_AXIS_X) { - axis |= 1; - tolerance[0] = mmd->tolerance; - } - if (mmd->flag & MOD_MIR_AXIS_Y) { - axis |= 2; - tolerance[1] = mmd->tolerance; - } - if (mmd->flag & MOD_MIR_AXIS_Z) { - axis |= 4; - tolerance[2] = mmd->tolerance; - } - if (axis) { - float mtx[4][4], imtx[4][4]; - int i; + Object *ob = tc->obedit; + ModifierData *md = ob->modifiers.first; + float tolerance[3] = {0.0f, 0.0f, 0.0f}; + int axis = 0; + + for (; md; md = md->next) { + if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + + if (mmd->flag & MOD_MIR_CLIPPING) { + axis = 0; + if (mmd->flag & MOD_MIR_AXIS_X) { + axis |= 1; + tolerance[0] = mmd->tolerance; + } + if (mmd->flag & MOD_MIR_AXIS_Y) { + axis |= 2; + tolerance[1] = mmd->tolerance; + } + if (mmd->flag & MOD_MIR_AXIS_Z) { + axis |= 4; + tolerance[2] = mmd->tolerance; + } + if (axis) { + float mtx[4][4], imtx[4][4]; + int i; - if (mmd->mirror_ob) { - float obinv[4][4]; + if (mmd->mirror_ob) { + float obinv[4][4]; - invert_m4_m4(obinv, mmd->mirror_ob->obmat); - mul_m4_m4m4(mtx, obinv, ob->obmat); - invert_m4_m4(imtx, mtx); - } + invert_m4_m4(obinv, mmd->mirror_ob->obmat); + mul_m4_m4m4(mtx, obinv, ob->obmat); + invert_m4_m4(imtx, mtx); + } - TransData *td = tc->data; - for (i = 0; i < tc->data_len; i++, td++) { - int clip; - float loc[3], iloc[3]; + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + int clip; + float loc[3], iloc[3]; - if (td->loc == NULL) { - break; - } + if (td->loc == NULL) { + break; + } - if (td->flag & TD_SKIP) { - continue; - } + if (td->flag & TD_SKIP) { + continue; + } - copy_v3_v3(loc, td->loc); - copy_v3_v3(iloc, td->iloc); + copy_v3_v3(loc, td->loc); + copy_v3_v3(iloc, td->iloc); - if (mmd->mirror_ob) { - mul_m4_v3(mtx, loc); - mul_m4_v3(mtx, iloc); - } + if (mmd->mirror_ob) { + mul_m4_v3(mtx, loc); + mul_m4_v3(mtx, iloc); + } - clip = 0; - if (axis & 1) { - if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) { - loc[0] = 0.0f; - clip = 1; - } + clip = 0; + if (axis & 1) { + if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) { + loc[0] = 0.0f; + clip = 1; } + } - if (axis & 2) { - if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) { - loc[1] = 0.0f; - clip = 1; - } + if (axis & 2) { + if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) { + loc[1] = 0.0f; + clip = 1; } - if (axis & 4) { - if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) { - loc[2] = 0.0f; - clip = 1; - } + } + if (axis & 4) { + if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) { + loc[2] = 0.0f; + clip = 1; } - if (clip) { - if (mmd->mirror_ob) { - mul_m4_v3(imtx, loc); - } - copy_v3_v3(td->loc, loc); + } + if (clip) { + if (mmd->mirror_ob) { + mul_m4_v3(imtx, loc); } + copy_v3_v3(td->loc, loc); } } } @@ -1644,38 +1641,6 @@ void animrecord_check_state(TransInfo *t, struct Object *ob) } } -static void recalcData_cursor_image(TransInfo *t) -{ - TransDataContainer *tc = t->data_container; - TransData *td = tc->data; - float aspect_inv[2]; - - aspect_inv[0] = 1.0f / t->aspect[0]; - aspect_inv[1] = 1.0f / t->aspect[1]; - - td->loc[0] = td->loc[0] * aspect_inv[0]; - td->loc[1] = td->loc[1] * aspect_inv[1]; - - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -static void recalcData_cursor(TransInfo *t) -{ - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -static void recalcData_obedit(TransInfo *t) -{ - if (t->state != TRANS_CANCEL) { - applyProject(t); - } - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->data_len) { - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ - } - } -} - /* called for updating while transform acts, once per redraw */ void recalcData(TransInfo *t) { @@ -1742,7 +1707,7 @@ void recalcData(TransInfo *t) recalcData_tracking(t); break; case TC_MBALL_VERTS: - recalcData_obedit(t); + recalcData_mball(t); break; case TC_LATTICE_VERTS: recalcData_lattice(t); diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 36a51d57f64..11550ec8803 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -45,7 +45,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); /* transform_convert_mesh.c */ -void mesh_customdatacorrect_init(TransInfo *t); +void transform_convert_mesh_customdatacorrect_init(TransInfo *t); /* transform_convert_sequencer.c */ int transform_convert_sequencer_get_snap_bound(TransInfo *t); @@ -62,7 +62,7 @@ void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic); struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt); char transform_convert_frame_side_dir_get(TransInfo *t, float cframe); bool FrameOnMouseSide(char side, float frame, float cframe); -void clipMirrorModifier(TransInfo *t); +void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc); void animrecord_check_state(TransInfo *t, struct Object *ob); /* transform_convert_action.c */ @@ -84,6 +84,8 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t); /* transform_convert_cursor.c */ void createTransCursor_image(TransInfo *t); void createTransCursor_view3d(TransInfo *t); +void recalcData_cursor_image(TransInfo *t); +void recalcData_cursor(TransInfo *t); /* transform_convert_curve.c */ void createTransCurveVerts(TransInfo *t); @@ -109,6 +111,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t); /* transform_convert_mball.c */ void createTransMBallVerts(TransInfo *t); +void recalcData_mball(TransInfo *t); /* transform_convert_mesh.c */ struct TransIslandData { diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c index 67d85f9610b..1f3eff31205 100644 --- a/source/blender/editors/transform/transform_convert_cursor.c +++ b/source/blender/editors/transform/transform_convert_cursor.c @@ -134,3 +134,29 @@ void createTransCursor_view3d(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Cursor + * \{ */ + +void recalcData_cursor_image(TransInfo *t) +{ + TransDataContainer *tc = t->data_container; + TransData *td = tc->data; + float aspect_inv[2]; + + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; + + td->loc[0] = td->loc[0] * aspect_inv[0]; + td->loc[1] = td->loc[1] * aspect_inv[1]; + + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +void recalcData_cursor(TransInfo *t) +{ + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c index 3ff83e00f43..f294bbbf0aa 100644 --- a/source/blender/editors/transform/transform_convert_curve.c +++ b/source/blender/editors/transform/transform_convert_curve.c @@ -441,7 +441,6 @@ void createTransCurveVerts(TransInfo *t) void recalcData_curve(TransInfo *t) { if (t->state != TRANS_CANCEL) { - clipMirrorModifier(t); applyProject(t); } @@ -460,7 +459,10 @@ void recalcData_curve(TransInfo *t) } } else { - /* Normal updating */ + /* Apply clipping after so we never project past the clip plane T25423. */ + transform_convert_clip_mirror_modifier_apply(tc); + + /* Normal updating. */ BKE_curve_dimension_update(cu); } } diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c index 6f5c0318054..e47cf4edc4a 100644 --- a/source/blender/editors/transform/transform_convert_mball.c +++ b/source/blender/editors/transform/transform_convert_mball.c @@ -30,6 +30,8 @@ #include "BKE_context.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" /* -------------------------------------------------------------------- */ @@ -128,3 +130,21 @@ void createTransMBallVerts(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Meta Ball + * \{ */ + +void recalcData_mball(TransInfo *t) +{ + if (t->state != TRANS_CANCEL) { + applyProject(t); + } + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len) { + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + } + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 5c05e35feb4..8d942b1094e 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -48,7 +48,575 @@ #include "transform_convert.h" +/* -------------------------------------------------------------------- */ +/** \name Container TransCustomData Creation + * \{ */ + +struct TransCustomDataMergeGroup { + /** map {BMVert: TransCustomDataLayerVert} */ + struct LinkNode **cd_loop_groups; +}; + +struct TransCustomDataLayer { + BMesh *bm; + struct MemArena *arena; + + struct GHash *origfaces; + struct BMesh *bm_origfaces; + + /* Special handle for multi-resolution. */ + int cd_loop_mdisp_offset; + + /* Optionally merge custom-data groups (this keeps UVs connected for example). */ + struct { + /** map {BMVert: TransDataBasic} */ + struct GHash *origverts; + struct TransCustomDataMergeGroup *data; + int data_len; + /** Array size of 'layer_math_map_len' + * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */ + int *customdatalayer_map; + /** Number of math BMLoop layers. */ + int customdatalayer_map_len; + } merge_group; + + bool use_merge_group; +}; + +static void tc_mesh_customdatacorrect_free_fn(struct TransInfo *UNUSED(t), + struct TransDataContainer *UNUSED(tc), + struct TransCustomData *custom_data) +{ + struct TransCustomDataLayer *tcld = custom_data->data; + bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + + if (tcld->bm_origfaces) { + BM_mesh_free(tcld->bm_origfaces); + } + if (tcld->origfaces) { + BLI_ghash_free(tcld->origfaces, NULL, NULL); + } + if (tcld->merge_group.origverts) { + BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); + } + if (tcld->arena) { + BLI_memarena_free(tcld->arena); + } + if (tcld->merge_group.customdatalayer_map) { + MEM_freeN(tcld->merge_group.customdatalayer_map); + } + + MEM_freeN(tcld); + custom_data->data = NULL; +} + #define USE_FACE_SUBSTITUTE +#ifdef USE_FACE_SUBSTITUTE +# define FACE_SUBSTITUTE_INDEX INT_MIN + +/** + * Search for a neighboring face with area and preferably without selected vertex. + * Used to replace area-less faces in custom-data correction. + */ +static BMFace *tc_mesh_customdatacorrect_find_best_face_substitute(BMFace *f) +{ + BMFace *best_face = NULL; + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BMLoop *l_radial_next = l->radial_next; + BMFace *f_test = l_radial_next->f; + if (f_test == f) { + continue; + } + if (is_zero_v3(f_test->no)) { + continue; + } + + /* Check the loops edge isn't selected. */ + if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) && + !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) { + /* Prefer edges with unselected vertices. + * Useful for extrude. */ + best_face = f_test; + break; + } + if (best_face == NULL) { + best_face = f_test; + } + } + return best_face; +} + +static void tc_mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld, + BMFace *f, + BMFace *f_copy) +{ + BLI_assert(is_zero_v3(f->no)); + BMesh *bm = tcld->bm; + /* It is impossible to calculate the loops weights of a face without area. + * Find a substitute. */ + BMFace *f_substitute = tc_mesh_customdatacorrect_find_best_face_substitute(f); + if (f_substitute) { + /* Copy the custom-data from the substitute face. */ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false); + } while ((l_iter = l_iter->next) != l_first); + + /* Use the substitute face as the reference during the transformation. */ + BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true); + + /* Hack: reference substitute face in `f_copy->no`. + * `tcld->origfaces` is already used to restore the initial value. */ + BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX); + *((BMFace **)&f_copy->no[0]) = f_substitute_copy; + } +} + +static BMFace *tc_mesh_customdatacorrect_face_substitute_get(BMFace *f_copy) +{ + BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX); + return *((BMFace **)&f_copy->no[0]); +} + +#endif /* USE_FACE_SUBSTITUTE */ + +static void tc_mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + const int index) +{ + BMesh *bm = tcld->bm; + BMVert *v = td->extra; + BMIter liter; + int j, l_num; + float *loop_weights; + + // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { + BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); + l_num = liter.count; + loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL; + for (j = 0; j < l_num; j++) { + BMLoop *l = BM_iter_step(&liter); + BMLoop *l_prev, *l_next; + + /* Generic custom-data correction. Copy face data. */ + void **val_p; + if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) { + BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true); + *val_p = f_copy; +#ifdef USE_FACE_SUBSTITUTE + if (is_zero_v3(l->f->no)) { + tc_mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy); + } +#endif + } + + if (tcld->use_merge_group) { + if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && + (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { + loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); + } + else { + loop_weights[j] = 0.0f; + } + } + } + + if (tcld->use_merge_group) { + /* Store cd_loop_groups. */ + struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index]; + if (l_num != 0) { + merge_data->cd_loop_groups = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)); + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + const int layer_nr = tcld->merge_group.customdatalayer_map[j]; + merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( + bm, v, layer_nr, loop_weights, tcld->arena); + } + } + else { + merge_data->cd_loop_groups = NULL; + } + + BLI_ghash_insert(tcld->merge_group.origverts, v, td); + } +} + +static void tc_mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc), + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + + struct GHash *origfaces = BLI_ghash_ptr_new(__func__); + struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + + /* We need to have matching loop custom-data. */ + BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL); + + tcld->origfaces = origfaces; + tcld->bm_origfaces = bm_origfaces; + + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); +} + +static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + BLI_assert(CustomData_has_math(&bm->ldata)); + + /* TODO: We don't need `layer_math_map` when there are no loops linked + * to one of the sliding vertices. */ + + /* Over allocate, only 'math' layers are indexed. */ + int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__); + int layer_math_map_len = 0; + for (int i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) { + customdatalayer_map[layer_math_map_len++] = i; + } + } + BLI_assert(layer_math_map_len != 0); + + tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len; + tcld->merge_group.customdatalayer_map = customdatalayer_map; + tcld->merge_group.customdatalayer_map_len = layer_math_map_len; + tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len); + tcld->merge_group.data = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); +} + +static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataContainer *tc, + const bool use_merge_group) +{ + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMesh *bm = em->bm; + + if (bm->shapenr > 1) { + /* Don't do this at all for non-basis shape keys, too easy to + * accidentally break uv maps or vertex colors then */ + /* create copies of faces for custom-data projection. */ + return NULL; + } + if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* There is no custom-data to correct. */ + return NULL; + } + + struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); + tcld->bm = bm; + tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + + /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */ + tcld->cd_loop_mdisp_offset = -1; + tcld->use_merge_group = use_merge_group; + + tc_mesh_customdatacorrect_init_container_generic(tc, tcld); + + if (tcld->use_merge_group) { + tc_mesh_customdatacorrect_init_container_merge_group(tc, tcld); + } + + { + /* Setup Verts. */ + int i = 0; + + TransData *tob = tc->data; + for (int j = tc->data_len; j--; tob++, i++) { + tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i); + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { + tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i); + } + } + + return tcld; +} + +static void tc_mesh_customdata_create(TransDataContainer *tc, const bool use_merge_group) +{ + struct TransCustomDataLayer *customdatacorrect; + customdatacorrect = tc_mesh_customdatacorrect_create(tc, use_merge_group); + + if (!customdatacorrect) { + return; + } + + BLI_assert(tc->custom.type.data == NULL); + tc->custom.type.data = customdatacorrect; + tc->custom.type.free_cb = tc_mesh_customdatacorrect_free_fn; +} + +void transform_convert_mesh_customdatacorrect_init(TransInfo *t) +{ + bool use_merge_group = false; + if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) { + /* No custom-data correction. */ + return; + } + use_merge_group = true; + } + else if (ELEM(t->mode, + TFM_TRANSLATION, + TFM_ROTATION, + TFM_RESIZE, + TFM_TOSPHERE, + TFM_SHEAR, + TFM_BEND, + TFM_SHRINKFATTEN, + TFM_TRACKBALL, + TFM_PUSHPULL, + TFM_ALIGN)) { + { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + /* No custom-data correction. */ + return; + } + use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0; + } + } + else { + return; + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->custom.type.data != NULL) { + tc_mesh_customdatacorrect_free_fn(t, tc, &tc->custom.type); + } + + tc_mesh_customdata_create(tc, use_merge_group); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Layer Correction Apply + * \{ */ + +/** + * If we're sliding the vert, return its original location, if not, the current location is good. + */ +static const float *tc_mesh_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v) +{ + TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v); + return td ? td->iloc : v->co; +} + +static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + struct TransCustomDataMergeGroup *merge_data, + bool do_loop_mdisps) +{ + BMesh *bm = tcld->bm; + BMVert *v = td->extra; + const float *co_orig_3d = td->iloc; + + BMIter liter; + int j, l_num; + float *loop_weights; + const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON); + const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len; + const float *v_proj_axis = v->no; + /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ + float v_proj[3][3]; + + if (do_loop_weight) { + project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); + } + + // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) + BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); + l_num = liter.count; + loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; + for (j = 0; j < l_num; j++) { + BMFace *f_copy; /* the copy of 'f' */ + BMLoop *l = BM_iter_step(&liter); + + f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + +#ifdef USE_FACE_SUBSTITUTE + /* In some faces it is not possible to calculate interpolation, + * so we use a substitute. */ + if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) { + f_copy = tc_mesh_customdatacorrect_face_substitute_get(f_copy); + } +#endif + + /* only loop data, no vertex data since that contains shape keys, + * and we do not want to mess up other shape keys */ + BM_loop_interp_from_face(bm, l, f_copy, false, false); + + /* weight the loop */ + if (do_loop_weight) { + const float eps = 1.0e-8f; + const BMLoop *l_prev = l->prev; + const BMLoop *l_next = l->next; + const float *co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v); + const float *co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v); + bool co_prev_ok; + bool co_next_ok; + + /* In the unlikely case that we're next to a zero length edge - + * walk around the to the next. + * + * Since we only need to check if the vertex is in this corner, + * its not important _which_ loop - as long as its not overlapping + * 'sv->co_orig_3d', see: T45096. */ + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && + ((l_prev = l_prev->prev) != l->next))) { + co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v); + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + } + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && + ((l_next = l_next->next) != l->prev))) { + co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v); + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + } + + if (co_prev_ok && co_next_ok) { + const float dist = dist_signed_squared_to_corner_v3v3v3( + v->co, UNPACK3(v_proj), v_proj_axis); + + loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); + if (UNLIKELY(!isfinite(loop_weights[j]))) { + loop_weights[j] = 0.0f; + } + } + else { + loop_weights[j] = 0.0f; + } + } + } + + if (tcld->use_merge_group) { + struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups; + if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) { + if (do_loop_weight) { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge_weights( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights); + } + } + else { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]); + } + } + } + } + + /* Special handling for multires + * + * Interpolate from every other loop (not ideal) + * However values will only be taken from loops which overlap other mdisps. + */ + const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); + if (update_loop_mdisps) { + float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); + BMLoop *l; + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BM_face_calc_center_median(l->f, faces_center[j]); + } + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + float f_copy_center[3]; + BMIter liter_other; + BMLoop *l_other; + int j_other; + + BM_face_calc_center_median(f_copy, f_copy_center); + + BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) { + BM_face_interp_multires_ex(bm, + l_other->f, + f_copy, + faces_center[j_other], + f_copy_center, + tcld->cd_loop_mdisp_offset); + } + } + } +} + +static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final) +{ + if (!tc->custom.type.data) { + return; + } + struct TransCustomDataLayer *tcld = tc->custom.type.data; + const bool use_merge_group = tcld->use_merge_group; + + struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; + TransData *tob = tc->data; + for (int i = tc->data_len; i--; tob++) { + tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = tc->data_mirror_len; i--; td_mirror++) { + tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Layer Correction Restore + * \{ */ + +static void tc_mesh_customdatacorrect_restore(struct TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + struct TransCustomDataLayer *tcld = tc->custom.type.data; + if (!tcld) { + continue; + } + + BMesh *bm = tcld->bm; + BMesh *bm_copy = tcld->bm_origfaces; + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, tcld->origfaces) { + BMFace *f = BLI_ghashIterator_getKey(&gh_iter); + BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter); + BLI_assert(f->len == f_copy->len); + + BMLoop *l_iter, *l_first, *l_copy; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + l_copy = BM_FACE_FIRST_LOOP(f_copy); + do { + /* TODO: Restore only the elements that transform. */ + BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter); + l_copy = l_copy->next; + } while ((l_iter = l_iter->next) != l_first); + } + } +} + +/** \} */ /* -------------------------------------------------------------------- */ /** \name Island Creation @@ -741,10 +1309,10 @@ void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_cr /** \name Edit Mesh Verts Transform Creation * \{ */ -static void transdata_center_get(const struct TransIslandData *island_data, - const int island_index, - const float iloc[3], - float r_center[3]) +static void tc_mesh_transdata_center_copy(const struct TransIslandData *island_data, + const int island_index, + const float iloc[3], + float r_center[3]) { if (island_data->center && island_index != -1) { copy_v3_v3(r_center, island_data->center[island_index]); @@ -783,7 +1351,7 @@ static void VertsToTransData(TransInfo *t, no = eve->no; } - transdata_center_get(island_data, island_index, td->iloc, td->center); + tc_mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center); if ((island_index != -1) && island_data->axismtx) { copy_m3_m3(td->axismtx, island_data->axismtx[island_index]); @@ -973,7 +1541,8 @@ void createTransEditVerts(TransInfo *t) copy_v3_v3(td_mirror->iloc, eve->co); td_mirror->flag = mirror_data.vert_map[a].flag; td_mirror->loc_src = v_src->co; - transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center); + tc_mesh_transdata_center_copy( + &island_data, island_index, td_mirror->iloc, td_mirror->center); td_mirror++; } @@ -1045,590 +1614,39 @@ void createTransEditVerts(TransInfo *t) /** \} */ /* -------------------------------------------------------------------- */ -/** \name CustomData Layer Correction +/** \name Recalc Mesh Data * \{ */ -struct TransCustomDataMergeGroup { - /** map {BMVert: TransCustomDataLayerVert} */ - struct LinkNode **cd_loop_groups; -}; - -struct TransCustomDataLayer { - BMesh *bm; - struct MemArena *arena; - - struct GHash *origfaces; - struct BMesh *bm_origfaces; - - /* Special handle for multi-resolution. */ - int cd_loop_mdisp_offset; - - /* Optionally merge custom-data groups (this keeps UVs connected for example). */ - struct { - /** map {BMVert: TransDataBasic} */ - struct GHash *origverts; - struct TransCustomDataMergeGroup *data; - int data_len; - /** Array size of 'layer_math_map_len' - * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */ - int *customdatalayer_map; - /** Number of math BMLoop layers. */ - int customdatalayer_map_len; - } merge_group; - - bool use_merge_group; -}; - -static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t), - struct TransDataContainer *UNUSED(tc), - struct TransCustomData *custom_data) -{ - struct TransCustomDataLayer *tcld = custom_data->data; - bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - - if (tcld->bm_origfaces) { - BM_mesh_free(tcld->bm_origfaces); - } - if (tcld->origfaces) { - BLI_ghash_free(tcld->origfaces, NULL, NULL); - } - if (tcld->merge_group.origverts) { - BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); - } - if (tcld->arena) { - BLI_memarena_free(tcld->arena); - } - if (tcld->merge_group.customdatalayer_map) { - MEM_freeN(tcld->merge_group.customdatalayer_map); - } - - MEM_freeN(tcld); - custom_data->data = NULL; -} - -#ifdef USE_FACE_SUBSTITUTE - -# define FACE_SUBSTITUTE_INDEX INT_MIN - -/** - * Search for a neighboring face with area and preferably without selected vertex. - * Used to replace area-less faces in custom-data correction. - */ -static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f) -{ - BMFace *best_face = NULL; - BMLoop *l; - BMIter liter; - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMLoop *l_radial_next = l->radial_next; - BMFace *f_test = l_radial_next->f; - if (f_test == f) { - continue; - } - if (is_zero_v3(f_test->no)) { - continue; - } - - /* Check the loops edge isn't selected. */ - if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) && - !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) { - /* Prefer edges with unselected vertices. - * Useful for extrude. */ - best_face = f_test; - break; - } - if (best_face == NULL) { - best_face = f_test; - } - } - return best_face; -} - -static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld, - BMFace *f, - BMFace *f_copy) -{ - BLI_assert(is_zero_v3(f->no)); - BMesh *bm = tcld->bm; - /* It is impossible to calculate the loops weights of a face without area. - * Find a substitute. */ - BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f); - if (f_substitute) { - /* Copy the custom-data from the substitute face. */ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false); - } while ((l_iter = l_iter->next) != l_first); - - /* Use the substitute face as the reference during the transformation. */ - BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true); - - /* Hack: reference substitute face in `f_copy->no`. - * `tcld->origfaces` is already used to restore the initial value. */ - BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX); - *((BMFace **)&f_copy->no[0]) = f_substitute_copy; - } -} - -static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy) -{ - BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX); - return *((BMFace **)&f_copy->no[0]); -} - -#endif /* USE_FACE_SUBSTITUTE */ - -static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld, - struct TransDataBasic *td, - const int index) -{ - BMesh *bm = tcld->bm; - BMVert *v = td->extra; - BMIter liter; - int j, l_num; - float *loop_weights; - - // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { - BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); - l_num = liter.count; - loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL; - for (j = 0; j < l_num; j++) { - BMLoop *l = BM_iter_step(&liter); - BMLoop *l_prev, *l_next; - - /* Generic custom-data correction. Copy face data. */ - void **val_p; - if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) { - BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true); - *val_p = f_copy; -#ifdef USE_FACE_SUBSTITUTE - if (is_zero_v3(l->f->no)) { - mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy); - } -#endif - } - - if (tcld->use_merge_group) { - if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && - (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { - loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); - } - else { - loop_weights[j] = 0.0f; - } - } - } - - if (tcld->use_merge_group) { - /* Store cd_loop_groups. */ - struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index]; - if (l_num != 0) { - merge_data->cd_loop_groups = BLI_memarena_alloc( - tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)); - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - const int layer_nr = tcld->merge_group.customdatalayer_map[j]; - merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( - bm, v, layer_nr, loop_weights, tcld->arena); - } - } - else { - merge_data->cd_loop_groups = NULL; - } - - BLI_ghash_insert(tcld->merge_group.origverts, v, td); - } -} - -static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc), - struct TransCustomDataLayer *tcld) -{ - BMesh *bm = tcld->bm; - - struct GHash *origfaces = BLI_ghash_ptr_new(__func__); - struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - - /* We need to have matching loop custom-data. */ - BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL); - - tcld->origfaces = origfaces; - tcld->bm_origfaces = bm_origfaces; - - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); -} - -static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, - struct TransCustomDataLayer *tcld) -{ - BMesh *bm = tcld->bm; - BLI_assert(CustomData_has_math(&bm->ldata)); - - /* TODO: We don't need `layer_math_map` when there are no loops linked - * to one of the sliding vertices. */ - - /* Over allocate, only 'math' layers are indexed. */ - int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__); - int layer_math_map_len = 0; - for (int i = 0; i < bm->ldata.totlayer; i++) { - if (CustomData_layer_has_math(&bm->ldata, i)) { - customdatalayer_map[layer_math_map_len++] = i; - } - } - BLI_assert(layer_math_map_len != 0); - - tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len; - tcld->merge_group.customdatalayer_map = customdatalayer_map; - tcld->merge_group.customdatalayer_map_len = layer_math_map_len; - tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len); - tcld->merge_group.data = BLI_memarena_alloc( - tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); -} - -static void mesh_customdatacorrect_init_container(TransDataContainer *tc, - const bool use_merge_group) -{ - if (tc->custom.type.data) { - /* The custom-data correction has been initiated before. - * Free since some modes have different settings. */ - mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type); - } - - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - - if (bm->shapenr > 1) { - /* Don't do this at all for non-basis shape keys, too easy to - * accidentally break uv maps or vertex colors then */ - /* create copies of faces for custom-data projection. */ - return; - } - if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - /* There is no custom-data to correct. */ - return; - } - - struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); - tcld->bm = bm; - tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - - /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */ - tcld->cd_loop_mdisp_offset = -1; - tcld->use_merge_group = use_merge_group; - - mesh_customdatacorrect_init_container_generic(tc, tcld); - - if (tcld->use_merge_group) { - mesh_customdatacorrect_init_container_merge_group(tc, tcld); - } - - { - /* Setup Verts. */ - int i = 0; - - TransData *tob = tc->data; - for (int j = tc->data_len; j--; tob++, i++) { - mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i); - } - - TransDataMirror *td_mirror = tc->data_mirror; - for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { - mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i); - } - } - - tc->custom.type.data = tcld; - tc->custom.type.free_cb = mesh_customdatacorrect_free_cb; -} - -void mesh_customdatacorrect_init(TransInfo *t) +static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) { - bool use_merge_group = false; - if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { - if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) { - /* No custom-data correction. */ - return; - } - use_merge_group = true; - } - else if (ELEM(t->mode, - TFM_TRANSLATION, - TFM_ROTATION, - TFM_RESIZE, - TFM_TOSPHERE, - TFM_SHEAR, - TFM_BEND, - TFM_SHRINKFATTEN, - TFM_TRACKBALL, - TFM_PUSHPULL, - TFM_ALIGN)) { - { - if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { - /* No custom-data correction. */ - return; - } - use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0; - } - } - else { - return; - } - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - mesh_customdatacorrect_init_container(tc, use_merge_group); - } -} - -/** - * If we're sliding the vert, return its original location, if not, the current location is good. - */ -static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v) -{ - TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v); - return td ? td->iloc : v->co; -} - -static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, - struct TransDataBasic *td, - struct TransCustomDataMergeGroup *merge_data, - bool do_loop_mdisps) -{ - BMesh *bm = tcld->bm; - BMVert *v = td->extra; - const float *co_orig_3d = td->iloc; - - BMIter liter; - int j, l_num; - float *loop_weights; - const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON); - const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len; - const float *v_proj_axis = v->no; - /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ - float v_proj[3][3]; - - if (do_loop_weight) { - project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); - } - - // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) - BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); - l_num = liter.count; - loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; - for (j = 0; j < l_num; j++) { - BMFace *f_copy; /* the copy of 'f' */ - BMLoop *l = BM_iter_step(&liter); - - f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); - -#ifdef USE_FACE_SUBSTITUTE - /* In some faces it is not possible to calculate interpolation, - * so we use a substitute. */ - if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) { - f_copy = mesh_customdatacorrect_face_substitute_get(f_copy); - } -#endif - - /* only loop data, no vertex data since that contains shape keys, - * and we do not want to mess up other shape keys */ - BM_loop_interp_from_face(bm, l, f_copy, false, false); - - /* weight the loop */ - if (do_loop_weight) { - const float eps = 1.0e-8f; - const BMLoop *l_prev = l->prev; - const BMLoop *l_next = l->next; - const float *co_prev = trans_vert_orig_co_get(tcld, l_prev->v); - const float *co_next = trans_vert_orig_co_get(tcld, l_next->v); - bool co_prev_ok; - bool co_next_ok; - - /* In the unlikely case that we're next to a zero length edge - - * walk around the to the next. - * - * Since we only need to check if the vertex is in this corner, - * its not important _which_ loop - as long as its not overlapping - * 'sv->co_orig_3d', see: T45096. */ - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && - ((l_prev = l_prev->prev) != l->next))) { - co_prev = trans_vert_orig_co_get(tcld, l_prev->v); - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - } - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && - ((l_next = l_next->next) != l->prev))) { - co_next = trans_vert_orig_co_get(tcld, l_next->v); - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - } - - if (co_prev_ok && co_next_ok) { - const float dist = dist_signed_squared_to_corner_v3v3v3( - v->co, UNPACK3(v_proj), v_proj_axis); - - loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); - if (UNLIKELY(!isfinite(loop_weights[j]))) { - loop_weights[j] = 0.0f; + if (tc->use_mirror_axis_any) { + int i; + TransData *td; + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) { + if (td->flag & TD_MIRROR_EDGE_X) { + td->loc[0] = 0.0f; } - } - else { - loop_weights[j] = 0.0f; - } - } - } - - if (tcld->use_merge_group) { - struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups; - if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) { - if (do_loop_weight) { - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - BM_vert_loop_groups_data_layer_merge_weights( - bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights); + if (td->flag & TD_MIRROR_EDGE_Y) { + td->loc[1] = 0.0f; } - } - else { - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - BM_vert_loop_groups_data_layer_merge( - bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]); + if (td->flag & TD_MIRROR_EDGE_Z) { + td->loc[2] = 0.0f; } } } - } - - /* Special handling for multires - * - * Interpolate from every other loop (not ideal) - * However values will only be taken from loops which overlap other mdisps. - */ - const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); - if (update_loop_mdisps) { - float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); - BMLoop *l; - - BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { - BM_face_calc_center_median(l->f, faces_center[j]); - } - - BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { - BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); - float f_copy_center[3]; - BMIter liter_other; - BMLoop *l_other; - int j_other; - - BM_face_calc_center_median(f_copy, f_copy_center); - - BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) { - BM_face_interp_multires_ex(bm, - l_other->f, - f_copy, - faces_center[j_other], - f_copy_center, - tcld->cd_loop_mdisp_offset); - } - } - } -} - -static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (!tc->custom.type.data) { - continue; - } - struct TransCustomDataLayer *tcld = tc->custom.type.data; - const bool use_merge_group = tcld->use_merge_group; - - struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; - TransData *tob = tc->data; - for (int i = tc->data_len; i--; tob++) { - mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final); - - if (use_merge_group) { - merge_data++; - } - } TransDataMirror *td_mirror = tc->data_mirror; - for (int i = tc->data_mirror_len; i--; td_mirror++) { - mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final); - - if (use_merge_group) { - merge_data++; + for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + copy_v3_v3(td_mirror->loc, td_mirror->loc_src); + if (td_mirror->flag & TD_MIRROR_X) { + td_mirror->loc[0] *= -1; } - } - } -} - -static void mesh_customdatacorrect_restore(struct TransInfo *t) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - struct TransCustomDataLayer *tcld = tc->custom.type.data; - if (!tcld) { - continue; - } - - BMesh *bm = tcld->bm; - BMesh *bm_copy = tcld->bm_origfaces; - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, tcld->origfaces) { - BMFace *f = BLI_ghashIterator_getKey(&gh_iter); - BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter); - BLI_assert(f->len == f_copy->len); - - BMLoop *l_iter, *l_first, *l_copy; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - l_copy = BM_FACE_FIRST_LOOP(f_copy); - do { - /* TODO: Restore only the elements that transform. */ - BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter); - l_copy = l_copy->next; - } while ((l_iter = l_iter->next) != l_first); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Recalc Mesh Data - * \{ */ - -static void mesh_apply_to_mirror(TransInfo *t) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->use_mirror_axis_any) { - int i; - TransData *td; - for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { - if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) { - if (td->flag & TD_MIRROR_EDGE_X) { - td->loc[0] = 0.0f; - } - if (td->flag & TD_MIRROR_EDGE_Y) { - td->loc[1] = 0.0f; - } - if (td->flag & TD_MIRROR_EDGE_Z) { - td->loc[2] = 0.0f; - } - } + if (td_mirror->flag & TD_MIRROR_Y) { + td_mirror->loc[1] *= -1; } - - TransDataMirror *td_mirror = tc->data_mirror; - for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { - copy_v3_v3(td_mirror->loc, td_mirror->loc_src); - if (td_mirror->flag & TD_MIRROR_X) { - td_mirror->loc[0] *= -1; - } - if (td_mirror->flag & TD_MIRROR_Y) { - td_mirror->loc[1] *= -1; - } - if (td_mirror->flag & TD_MIRROR_Z) { - td_mirror->loc[2] *= -1; - } + if (td_mirror->flag & TD_MIRROR_Z) { + td_mirror->loc[2] *= -1; } } } @@ -1637,20 +1655,24 @@ static void mesh_apply_to_mirror(TransInfo *t) void recalcData_mesh(TransInfo *t) { bool is_canceling = t->state == TRANS_CANCEL; - /* mirror modifier clipping? */ + /* Apply corrections. */ if (!is_canceling) { - /* apply clipping after so we never project past the clip plane T25423. */ applyProject(t); - clipMirrorModifier(t); - if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { - mesh_apply_to_mirror(t); - } + bool do_mirror = !(t->flag & T_NO_MIRROR); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* Apply clipping after so we never project past the clip plane T25423. */ + transform_convert_clip_mirror_modifier_apply(tc); + + if (do_mirror) { + tc_mesh_transdata_mirror_apply(tc); + } - mesh_customdatacorrect_apply(t, false); + tc_mesh_customdatacorrect_apply(tc, false); + } } else { - mesh_customdatacorrect_restore(t); + tc_mesh_customdatacorrect_restore(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -1674,7 +1696,9 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { /* NOTE(joeedh): Handle multi-res re-projection, * done on transform completion since it's really slow. */ - mesh_customdatacorrect_apply(t, true); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc_mesh_customdatacorrect_apply(tc, true); + } } if (use_automerge) { diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c index 5dbb1947773..68305c45280 100644 --- a/source/blender/editors/transform/transform_convert_mesh_skin.c +++ b/source/blender/editors/transform/transform_convert_mesh_skin.c @@ -47,9 +47,9 @@ /** \name Edit Mesh #CD_MVERT_SKIN Transform Creation * \{ */ -static float *mesh_skin_transdata_center(const struct TransIslandData *island_data, - const int island_index, - BMVert *eve) +static float *tc_mesh_skin_transdata_center(const struct TransIslandData *island_data, + const int island_index, + BMVert *eve) { if (island_data->center && island_index != -1) { return island_data->center[island_index]; @@ -57,11 +57,11 @@ static float *mesh_skin_transdata_center(const struct TransIslandData *island_da return eve->co; } -static void mesh_skin_transdata_create(TransDataBasic *td, - BMEditMesh *em, - BMVert *eve, - const struct TransIslandData *island_data, - const int island_index) +static void tc_mesh_skin_transdata_create(TransDataBasic *td, + BMEditMesh *em, + BMVert *eve, + const struct TransIslandData *island_data, + const int island_index) { BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MVERT_SKIN); @@ -78,7 +78,7 @@ static void mesh_skin_transdata_create(TransDataBasic *td, td->flag |= TD_SELECTED; } - copy_v3_v3(td->center, mesh_skin_transdata_center(island_data, island_index, eve)); + copy_v3_v3(td->center, tc_mesh_skin_transdata_center(island_data, island_index, eve)); td->extra = eve; } @@ -209,7 +209,7 @@ void createTransMeshSkin(TransInfo *t) } if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { - mesh_skin_transdata_create( + tc_mesh_skin_transdata_create( (TransDataBasic *)td_mirror, em, eve, &island_data, island_index); int elem_index = mirror_data.vert_map[a].index; @@ -221,7 +221,7 @@ void createTransMeshSkin(TransInfo *t) td_mirror++; } else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index); + tc_mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index); if (t->around == V3D_AROUND_LOCAL_ORIGINS) { createSpaceNormal(td->axismtx, eve->no); @@ -275,7 +275,7 @@ void createTransMeshSkin(TransInfo *t) /** \name Recalc Mesh Data * \{ */ -static void mesh_skin_apply_to_mirror(TransInfo *t) +static void tc_mesh_skin_apply_to_mirror(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->use_mirror_axis_any) { @@ -292,8 +292,8 @@ void recalcData_mesh_skin(TransInfo *t) bool is_canceling = t->state == TRANS_CANCEL; /* mirror modifier clipping? */ if (!is_canceling) { - if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { - mesh_skin_apply_to_mirror(t); + if (!(t->flag & T_NO_MIRROR)) { + tc_mesh_skin_apply_to_mirror(t); } } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index c0c4b22da98..c9b6bef5904 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -538,7 +538,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } else { /* Avoid mirroring for unsupported contexts. */ - t->options |= CTX_NO_MIRROR; + t->flag |= T_NO_MIRROR; } /* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */ diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index d14f693da9c..867a6cc4291 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1263,7 +1263,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) if (t->data_type == TC_MESH_VERTS) { /* Init Custom Data correction. * Ideally this should be called when creating the TransData. */ - mesh_customdatacorrect_init(t); + transform_convert_mesh_customdatacorrect_init(t); } /* TODO(germano): Some of these operations change the `t->mode`. diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index ee63bf4be6f..68416c780ef 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -107,7 +107,6 @@ void initCurveShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index e67c6c03481..7c496d271ef 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -117,7 +117,6 @@ void initGPOpacity(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 95e3d89d2b7..608a49f38b1 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -119,7 +119,6 @@ void initGPShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 3019984d70b..857ee37f0ad 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -133,7 +133,6 @@ void initMaskShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c index 9891af8b9a4..f225f1a7c06 100644 --- a/source/blender/editors/transform/transform_mode_mirror.c +++ b/source/blender/editors/transform/transform_mode_mirror.c @@ -235,8 +235,5 @@ void initMirror(TransInfo *t) initMouseInputMode(t, &t->mouse, INPUT_NONE); t->flag |= T_NULL_ONE; - if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; - } } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 4d0bb7fbe9c..fcdc57a98e2 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -176,7 +176,6 @@ void initResize(TransInfo *t) t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 8beacb844b9..77e57484bef 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -111,7 +111,6 @@ void initSkinResize(TransInfo *t) t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index d0f91802fff..193954fffb6 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -345,7 +345,7 @@ void applyProject(TransInfo *t) SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, - .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, .use_occlusion_test = false, .use_backface_culling = t->tsnap.use_backface_culling, }, @@ -1167,7 +1167,7 @@ short snapObjectsTransform( t->settings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, - .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE, .use_backface_culling = t->tsnap.use_backface_culling, }, @@ -1201,7 +1201,7 @@ bool peelObjectsTransform(TransInfo *t, t->depsgraph, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, - .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, }, mval, -1.0f, diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index e5f6f207e3c..512f912a532 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -44,6 +44,7 @@ #include "BKE_duplilist.h" #include "BKE_editmesh.h" #include "BKE_geometry_set.h" +#include "BKE_global.h" #include "BKE_layer.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -145,9 +146,37 @@ struct SnapObjectContext { /** \name Utilities * \{ */ -static bool editmesh_eval_final_is_bmesh(const BMEditMesh *em) +/* Mesh used for snapping. + * If NULL the BMesh should be used. */ +static Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide) { - return (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH); + Mesh *me_eval = ob_eval->data; + bool use_hide = false; + if (BKE_object_is_in_editmode(ob_eval)) { + if (edit_mode_type == SNAP_GEOM_EDIT) { + return NULL; + } + + BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); + if ((edit_mode_type == SNAP_GEOM_FINAL) && em_eval->mesh_eval_final) { + if (em_eval->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + return NULL; + } + me_eval = em_eval->mesh_eval_final; + use_hide = true; + } + else if ((edit_mode_type == SNAP_GEOM_CAGE) && em_eval->mesh_eval_cage) { + if (em_eval->mesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + return NULL; + } + me_eval = em_eval->mesh_eval_cage; + use_hide = true; + } + } + if (r_use_hide) { + *r_use_hide = use_hide; + } + return me_eval; } /** \} */ @@ -209,30 +238,69 @@ static void snap_object_data_clear(SnapObjectData *sod) memset(&sod->type, 0x0, sizeof(*sod) - offsetof(SnapObjectData, type)); } -static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob) +static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob_eval) { - SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval); if (sod == NULL) { if (sctx->cache.data_to_object_map != NULL) { - ob = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob->data); + ob_eval = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob_eval->data); /* Could be NULl when mixing edit-mode and non edit-mode objects. */ - if (ob != NULL) { - sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + if (ob_eval != NULL) { + sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval); } } } return sod; } -static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object *ob) +static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, + Object *ob_eval, + Mesh *me_eval, + bool use_hide) { SnapObjectData *sod; void **sod_p; bool init = false; - if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) { sod = *sod_p; + bool is_dirty = false; if (sod->type != SNAP_MESH) { + is_dirty = true; + } + else if (sod->treedata_mesh.tree && sod->treedata_mesh.cached && + !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->treedata_mesh.tree)) { + /* The tree is owned by the Mesh and may have been freed since we last used. */ + is_dirty = true; + } + else if (sod->bvhtree[0] && sod->cached[0] && + !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[0])) { + /* The tree is owned by the Mesh and may have been freed since we last used. */ + is_dirty = true; + } + else if (sod->bvhtree[1] && sod->cached[1] && + !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[1])) { + /* The tree is owned by the Mesh and may have been freed since we last used. */ + is_dirty = true; + } + else if (!sod->treedata_mesh.looptri_allocated && + sod->treedata_mesh.looptri != me_eval->runtime.looptris.array) { + is_dirty = true; + } + else if (!sod->treedata_mesh.vert_allocated && sod->treedata_mesh.vert != me_eval->mvert) { + is_dirty = true; + } + else if (!sod->treedata_mesh.loop_allocated && sod->treedata_mesh.loop != me_eval->mloop) { + is_dirty = true; + } + else if (!sod->treedata_mesh.edge_allocated && sod->treedata_mesh.edge != me_eval->medge) { + is_dirty = true; + } + else if (sod->poly != me_eval->mpoly) { + is_dirty = true; + } + + if (is_dirty) { snap_object_data_clear(sod); init = true; } @@ -244,8 +312,32 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object if (init) { sod->type = SNAP_MESH; - /* start assuming that it has each of these element types */ - sod->has_looptris = true; + + /* The BVHTree from looptris is always required. */ + BLI_assert(sod->treedata_mesh.tree == NULL); + BKE_bvhtree_from_mesh_get(&sod->treedata_mesh, + me_eval, + use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI, + 4); + + if (sod->treedata_mesh.tree == NULL) { + sod->treedata_mesh.vert = me_eval->mvert; + sod->treedata_mesh.loop = me_eval->mloop; + sod->treedata_mesh.looptri = BKE_mesh_runtime_looptri_ensure(me_eval); + BLI_assert(sod->has_looptris == false); + } + else { + BLI_assert(sod->treedata_mesh.vert != NULL); + BLI_assert(sod->treedata_mesh.loop != NULL); + BLI_assert(sod->treedata_mesh.looptri != NULL); + sod->has_looptris = true; + } + + /* Required for snapping with occlusion. */ + sod->treedata_mesh.edge = me_eval->medge; + sod->poly = me_eval->mpoly; + + /* Start assuming that it has each of these element types. */ sod->has_loose_edge = true; sod->has_loose_vert = true; } @@ -253,26 +345,26 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object return sod; } -static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob) +static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval) { - BMEditMesh *em = BKE_editmesh_from_object(ob); - if (em->mesh_eval_final) { - return &em->mesh_eval_final->runtime; + BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); + if (em_eval->mesh_eval_final) { + return &em_eval->mesh_eval_final->runtime; } - if (em->mesh_eval_cage) { - return &em->mesh_eval_cage->runtime; + if (em_eval->mesh_eval_cage) { + return &em_eval->mesh_eval_cage->runtime; } - return &((Mesh *)ob->data)->runtime; + return &((Mesh *)ob_eval->data)->runtime; } static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, BMEditMesh *em) { SnapObjectData *sod; void **sod_p; - bool init = false, init_min_max = true, clear_cache = false; + bool init = false; { /* Use object-data as the key in ghash since the editmesh @@ -281,48 +373,53 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, sctx->cache.data_to_object_map = BLI_ghash_ptr_new(__func__); } void **ob_p; - if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob->data, &ob_p)) { - ob = *ob_p; + if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob_eval->data, &ob_p)) { + ob_eval = *ob_p; } else { - *ob_p = ob; + *ob_p = ob_eval; } } - if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) { sod = *sod_p; - bool clear = false; + bool is_dirty = false; /* Check if the geometry has changed. */ if (sod->type != SNAP_EDIT_MESH) { - clear = true; + is_dirty = true; } else if (sod->treedata_editmesh.em != em) { - clear_cache = true; - init = true; + is_dirty = true; } else if (sod->mesh_runtime) { - if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob)) { - clear_cache = true; - init = true; + if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) { + if (G.moving) { + /* Hack to avoid updating while transforming. */ + BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]); + sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval); + } + else { + is_dirty = true; + } } else if (sod->treedata_editmesh.tree && sod->treedata_editmesh.cached && !bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->treedata_editmesh.tree)) { /* The tree is owned by the EditMesh and may have been freed since we last used! */ - clear = true; + is_dirty = true; } else if (sod->bvhtree[0] && sod->cached[0] && !bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[0])) { /* The tree is owned by the EditMesh and may have been freed since we last used! */ - clear = true; + is_dirty = true; } else if (sod->bvhtree[1] && sod->cached[1] && !bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[1])) { /* The tree is owned by the EditMesh and may have been freed since we last used! */ - clear = true; + is_dirty = true; } } - if (clear) { + if (is_dirty) { snap_object_data_clear(sod); init = true; } @@ -335,27 +432,8 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, if (init) { sod->type = SNAP_EDIT_MESH; sod->treedata_editmesh.em = em; - - if (clear_cache) { - /* Only init min and max when you have a non-custom bvhtree pending. */ - init_min_max = false; - if (sod->treedata_editmesh.cached) { - sod->treedata_editmesh.tree = NULL; - init_min_max = true; - } - for (int i = 0; i < ARRAY_SIZE(sod->bvhtree); i++) { - if (sod->cached[i]) { - sod->bvhtree[i] = NULL; - init_min_max = true; - } - } - } - - if (init_min_max) { - bm_mesh_minmax(em->bm, sod->min, sod->max); - } - - sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob); + sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval); + bm_mesh_minmax(em->bm, sod->min, sod->max); } return sod; @@ -368,9 +446,9 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, * \{ */ typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, float obmat[4][4], - bool use_obedit, + eSnapEditType edit_mode_type, bool use_backface_culling, bool is_object_active, void *data); @@ -387,10 +465,17 @@ static void iter_snap_objects(SnapObjectContext *sctx, ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); const View3D *v3d = sctx->v3d_data.v3d; const eSnapSelect snap_select = params->snap_select; - const bool use_object_edit_cage = params->use_object_edit_cage; + const eSnapEditType edit_mode_type = params->edit_mode_type; const bool use_backface_culling = params->use_backface_culling; Base *base_act = view_layer->basact; + if (snap_select == SNAP_ONLY_ACTIVE) { + Object *obj_eval = DEG_get_evaluated_object(depsgraph, base_act->object); + sob_callback( + sctx, obj_eval, obj_eval->obmat, edit_mode_type, use_backface_culling, true, data); + return; + } + for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { if (!BASE_VISIBLE(v3d, base)) { continue; @@ -425,7 +510,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, sob_callback(sctx, dupli_ob->ob, dupli_ob->mat, - use_object_edit_cage, + edit_mode_type, use_backface_culling, is_object_active, data); @@ -436,7 +521,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, sob_callback(sctx, obj_eval, obj_eval->obmat, - use_object_edit_cage, + edit_mode_type, use_backface_culling, is_object_active, data); @@ -464,7 +549,7 @@ struct RayCastAll_Data { float len_diff; float local_scale; - Object *ob; + Object *ob_eval; uint ob_uuid; /* output data */ @@ -476,7 +561,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth, const float co[3], const float no[3], int index, - Object *ob, + Object *ob_eval, const float obmat[4][4], uint ob_uuid) { @@ -487,7 +572,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth, copy_v3_v3(hit->no, no); hit->index = index; - hit->ob = ob; + hit->ob_eval = ob_eval; copy_m4_m4(hit->obmat, (float(*)[4])obmat); hit->ob_uuid = ob_uuid; @@ -529,7 +614,7 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH normalize_v3(normal); struct SnapObjectHitDepth *hit_item = hit_depth_create( - depth, location, normal, hit->index, data->ob, data->obmat, data->ob_uuid); + depth, location, normal, hit->index, data->ob_eval, data->obmat, data->ob_uuid); BLI_addtail(data->hit_list, hit_item); } } @@ -601,8 +686,8 @@ static void editmesh_looptri_raycast_backface_culling_cb(void *userdata, static bool raycastMesh(SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], - Object *ob, - Mesh *me, + Object *ob_eval, + Mesh *me_eval, const float obmat[4][4], const uint ob_index, bool use_hide, @@ -617,7 +702,7 @@ static bool raycastMesh(SnapObjectContext *sctx, { bool retval = false; - if (me->totpoly == 0) { + if (me_eval->totpoly == 0) { return retval; } @@ -641,7 +726,7 @@ static bool raycastMesh(SnapObjectContext *sctx, } /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob); + BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); if (bb) { /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!isect_ray_aabb_v3_simple( @@ -661,53 +746,18 @@ static bool raycastMesh(SnapObjectContext *sctx, len_diff = 0.0f; } - SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob); + SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide); BVHTreeFromMesh *treedata = &sod->treedata_mesh; - /* The tree is owned by the Mesh and may have been freed since we last used. */ - if (treedata->tree) { - BLI_assert(treedata->cached); - if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) { - free_bvhtree_from_mesh(treedata); - } - else { - /* Update Pointers. */ - if (treedata->vert && treedata->vert_allocated == false) { - treedata->vert = me->mvert; - } - if (treedata->loop && treedata->loop_allocated == false) { - treedata->loop = me->mloop; - } - if (treedata->looptri && treedata->looptri_allocated == false) { - treedata->looptri = BKE_mesh_runtime_looptri_ensure(me); - } - /* required for snapping with occlusion. */ - treedata->edge = me->medge; - sod->poly = me->mpoly; - } - } - if (treedata->tree == NULL) { - if (use_hide) { - BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI_NO_HIDDEN, 4); - } - else { - BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4); - } - - /* required for snapping with occlusion. */ - treedata->edge = me->medge; - sod->poly = me->mpoly; - - if (treedata->tree == NULL) { - return retval; - } + return retval; } float timat[3][3]; /* transpose inverse matrix for normals */ transpose_m3_m4(timat, imat); + BLI_assert(treedata->raycast_callback != NULL); if (r_hit_list) { struct RayCastAll_Data data; @@ -717,7 +767,7 @@ static bool raycastMesh(SnapObjectContext *sctx, data.timat = timat; data.len_diff = len_diff; data.local_scale = local_scale; - data.ob = ob; + data.ob_eval = ob_eval; data.ob_uuid = ob_index; data.hit_list = r_hit_list; data.retval = retval; @@ -776,7 +826,7 @@ static bool raycastMesh(SnapObjectContext *sctx, static bool raycastEditMesh(SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], - Object *ob, + Object *ob_eval, BMEditMesh *em, const float obmat[4][4], const uint ob_index, @@ -813,7 +863,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, local_depth *= local_scale; } - SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em); + SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em); /* Test BoundBox */ @@ -839,7 +889,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx, if (treedata->tree == NULL) { /* Operators only update the editmesh looptris of the original mesh. */ - BLI_assert(sod->treedata_editmesh.em == BKE_editmesh_from_object(DEG_get_original_object(ob))); + BLI_assert(sod->treedata_editmesh.em == + BKE_editmesh_from_object(DEG_get_original_object(ob_eval))); em = sod->treedata_editmesh.em; if (sctx->callbacks.edit_mesh.test_face_fn) { @@ -886,7 +937,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, data.timat = timat; data.len_diff = len_diff; data.local_scale = local_scale; - data.ob = ob; + data.ob_eval = ob_eval; data.ob_uuid = ob_index; data.hit_list = r_hit_list; data.retval = retval; @@ -967,9 +1018,9 @@ struct RaycastObjUserData { * \note Duplicate args here are documented at #snapObjectsRay */ static void raycast_obj_fn(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, float obmat[4][4], - bool use_obedit, + eSnapEditType edit_mode_type, bool use_backface_culling, bool is_object_active, void *data) @@ -982,53 +1033,46 @@ static void raycast_obj_fn(SnapObjectContext *sctx, bool retval = false; if (use_occlusion_test) { - if (use_obedit && sctx->use_v3d && XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) { + if ((edit_mode_type == SNAP_GEOM_EDIT) && sctx->use_v3d && + XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) { /* Use of occlude geometry in editing mode disabled. */ return; } - if (ELEM(ob->dt, OB_BOUNDBOX, OB_WIRE)) { + if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) { /* Do not hit objects that are in wire or bounding box * display mode. */ return; } } - switch (ob->type) { + switch (ob_eval->type) { case OB_MESH: { - Mesh *me = ob->data; bool use_hide = false; - if (BKE_object_is_in_editmode(ob)) { - if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) { - /* Operators only update the editmesh looptris of the original mesh. */ - BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob)); - retval = raycastEditMesh(sctx, - dt->ray_start, - dt->ray_dir, - ob, - em_orig, - obmat, - ob_index, - use_backface_culling, - ray_depth, - dt->r_loc, - dt->r_no, - dt->r_index, - dt->r_hit_list); - break; - } - - BMEditMesh *em = BKE_editmesh_from_object(ob); - if (em->mesh_eval_final) { - me = em->mesh_eval_final; - use_hide = true; - } + Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); + if (me_eval == NULL) { + /* Operators only update the editmesh looptris of the original mesh. */ + BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval)); + retval = raycastEditMesh(sctx, + dt->ray_start, + dt->ray_dir, + ob_eval, + em_orig, + obmat, + ob_index, + use_backface_culling, + ray_depth, + dt->r_loc, + dt->r_no, + dt->r_index, + dt->r_hit_list); + break; } retval = raycastMesh(sctx, dt->ray_start, dt->ray_dir, - ob, - me, + ob_eval, + me_eval, obmat, ob_index, use_hide, @@ -1044,12 +1088,12 @@ static void raycast_obj_fn(SnapObjectContext *sctx, case OB_SURF: case OB_FONT: { if (!is_object_active) { - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (mesh_eval) { retval = raycastMesh(sctx, dt->ray_start, dt->ray_dir, - ob, + ob_eval, mesh_eval, obmat, ob_index, @@ -1068,7 +1112,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, if (retval) { if (dt->r_ob) { - *dt->r_ob = ob; + *dt->r_ob = ob_eval; } if (dt->r_obmat) { copy_m4_m4(dt->r_obmat, obmat); @@ -1323,6 +1367,33 @@ typedef struct Nearest2dUserData { bool use_backface_culling; } Nearest2dUserData; +static void nearest2d_data_init(SnapObjectData *sod, + bool is_persp, + bool use_backface_culling, + Nearest2dUserData *r_nearest2d) +{ + if (sod->type == SNAP_MESH) { + r_nearest2d->userdata = &sod->treedata_mesh; + r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; + r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; + r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; + r_nearest2d->get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get; + r_nearest2d->get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get; + } + else { + BLI_assert(sod->type == SNAP_EDIT_MESH); + r_nearest2d->userdata = sod->treedata_editmesh.em; + r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; + r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; + r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; + r_nearest2d->get_tri_verts_index = NULL; + r_nearest2d->get_tri_edges_index = NULL; + } + + r_nearest2d->is_persp = is_persp; + r_nearest2d->use_backface_culling = use_backface_culling; +} + static void cb_snap_vert(void *userdata, int index, const struct DistProjectedAABBPrecalc *precalc, @@ -1470,7 +1541,7 @@ static void cb_snap_tri_verts(void *userdata, static short snap_mesh_polygon(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], bool use_backface_culling, /* read/write args */ @@ -1495,28 +1566,21 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); } - Nearest2dUserData nearest2d = { - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, - .use_backface_culling = use_backface_culling, - }; - BVHTreeNearest nearest = { .index = -1, .dist_sq = square_f(*dist_px), }; - SnapObjectData *sod = snap_object_data_lookup(sctx, ob); - + SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval); BLI_assert(sod != NULL); + Nearest2dUserData nearest2d; + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + if (sod->type == SNAP_MESH) { BVHTreeFromMesh *treedata = &sod->treedata_mesh; - nearest2d.userdata = treedata; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; - const MPoly *mp = &sod->poly[*r_index]; const MLoop *ml = &treedata->loop[mp->loopstart]; if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { @@ -1547,11 +1611,6 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BLI_assert(sod->type == SNAP_EDIT_MESH); BMEditMesh *em = sod->treedata_editmesh.em; - nearest2d.userdata = em; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; - BM_mesh_elem_table_ensure(em->bm, BM_FACE); BMFace *f = BM_face_at_index(em->bm, *r_index); BMLoop *l_iter, *l_first; @@ -1608,7 +1667,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], float original_dist_px, const float prev_co[3], @@ -1622,32 +1681,16 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, { short elem = SCE_SNAP_MODE_EDGE; - if (ob->type != OB_MESH) { + if (ob_eval->type != OB_MESH) { return elem; } - SnapObjectData *sod = snap_object_data_lookup(sctx, ob); - + SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval); BLI_assert(sod != NULL); Nearest2dUserData nearest2d; - { - nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; - nearest2d.use_backface_culling = use_backface_culling; - if (sod->type == SNAP_MESH) { - nearest2d.userdata = &sod->treedata_mesh; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; - } - else { - BLI_assert(sod->type == SNAP_EDIT_MESH); - nearest2d.userdata = sod->treedata_editmesh.em; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; - } - } + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); int vindex[2]; nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata); @@ -1772,9 +1815,8 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, } static short snapArmature(SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], - bool use_obedit, /* read/write args */ float *dist_px, /* return args */ @@ -1795,11 +1837,11 @@ static short snapArmature(SnapData *snapdata, dist_squared_to_projected_aabb_precalc( &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); - use_obedit = use_obedit && BKE_object_is_in_editmode(ob); + bool use_obedit = ((bArmature *)ob_eval->data)->edbo != NULL; if (use_obedit == false) { /* Test BoundBox */ - BoundBox *bb = BKE_armature_boundbox_get(ob); + BoundBox *bb = BKE_armature_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { return retval; @@ -1814,7 +1856,7 @@ static short snapArmature(SnapData *snapdata, bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; - bArmature *arm = ob->data; + bArmature *arm = ob_eval->data; if (arm->edbo) { LISTBASE_FOREACH (EditBone *, eBone, arm->edbo) { if (eBone->layer & arm->layer) { @@ -1858,8 +1900,8 @@ static short snapArmature(SnapData *snapdata, } } } - else if (ob->pose && ob->pose->chanbase.first) { - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + else if (ob_eval->pose && ob_eval->pose->chanbase.first) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_eval->pose->chanbase) { Bone *bone = pchan->bone; /* skip hidden bones */ if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { @@ -1917,7 +1959,7 @@ static short snapArmature(SnapData *snapdata, } static short snapCurve(SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], bool use_obedit, /* read/write args */ @@ -1934,7 +1976,7 @@ static short snapCurve(SnapData *snapdata, return 0; } - Curve *cu = ob->data; + Curve *cu = ob_eval->data; float dist_px_sq = square_f(*dist_px); float lpmat[4][4]; @@ -1944,11 +1986,11 @@ static short snapCurve(SnapData *snapdata, dist_squared_to_projected_aabb_precalc( &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); - use_obedit = use_obedit && BKE_object_is_in_editmode(ob); + use_obedit = use_obedit && BKE_object_is_in_editmode(ob_eval); if (use_obedit == false) { /* Test BoundBox */ - BoundBox *bb = BKE_curve_boundbox_get(ob); + BoundBox *bb = BKE_curve_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { return 0; @@ -2068,7 +2110,7 @@ static short snapCurve(SnapData *snapdata, /* may extend later (for now just snaps to empty center) */ static short snap_object_center(SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], /* read/write args */ float *dist_px, @@ -2079,7 +2121,7 @@ static short snap_object_center(SnapData *snapdata, { short retval = 0; - if (ob->transflag & OB_DUPLI) { + if (ob_eval->transflag & OB_DUPLI) { return retval; } @@ -2222,9 +2264,10 @@ static short snapCamera(const SnapObjectContext *sctx, static short snapMesh(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, - Mesh *me, + Object *ob_eval, + Mesh *me_eval, const float obmat[4][4], + bool use_hide, bool use_backface_culling, /* read/write args */ float *dist_px, @@ -2234,10 +2277,10 @@ static short snapMesh(SnapObjectContext *sctx, int *r_index) { BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); - if (me->totvert == 0) { + if (me_eval->totvert == 0) { return 0; } - if (me->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (me_eval->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { return 0; } @@ -2247,65 +2290,46 @@ static short snapMesh(SnapObjectContext *sctx, float dist_px_sq = square_f(*dist_px); /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob); + BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { return 0; } - SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob); + SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide); - BVHTreeFromMesh *treedata, dummy_treedata; + BVHTreeFromMesh *treedata, treedata_tmp; treedata = &sod->treedata_mesh; - /* The tree is owned by the Mesh and may have been freed since we last used! */ - if (treedata->cached && treedata->tree && - !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) { - free_bvhtree_from_mesh(treedata); - } - if (sod->cached[0] && sod->bvhtree[0] && - !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[0])) { - sod->bvhtree[0] = NULL; - } - if (sod->cached[1] && sod->bvhtree[1] && - !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[1])) { - sod->bvhtree[1] = NULL; - } - - if (sod->has_looptris && treedata->tree == NULL) { - BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4); - sod->has_looptris = (treedata->tree != NULL); - if (sod->has_looptris) { - /* Make sure that the array of edges is referenced in the callbacks. */ - treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */ - } - } if (sod->has_loose_edge && sod->bvhtree[0] == NULL) { - sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEEDGES, 2); - sod->has_loose_edge = sod->bvhtree[0] != NULL; - sod->cached[0] = dummy_treedata.cached; - - if (sod->has_loose_edge) { - BLI_assert(treedata->vert_allocated == false); - treedata->vert = dummy_treedata.vert; - treedata->vert_allocated = dummy_treedata.vert_allocated; - - BLI_assert(treedata->edge_allocated == false); - treedata->edge = dummy_treedata.edge; - treedata->edge_allocated = dummy_treedata.edge_allocated; + sod->bvhtree[0] = BKE_bvhtree_from_mesh_get( + &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEEDGES, 2); + if (sod->bvhtree[0] == NULL) { + sod->has_loose_edge = false; } + sod->cached[0] = treedata_tmp.cached; + BLI_assert(!ELEM(true, + treedata_tmp.vert_allocated, + treedata_tmp.edge_allocated, + treedata_tmp.face_allocated, + treedata_tmp.loop_allocated, + treedata_tmp.looptri_allocated)); } + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (sod->has_loose_vert && sod->bvhtree[1] == NULL) { - sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEVERTS, 2); - sod->has_loose_vert = sod->bvhtree[1] != NULL; - sod->cached[1] = dummy_treedata.cached; - - if (sod->has_loose_vert) { - BLI_assert(treedata->vert_allocated == false); - treedata->vert = dummy_treedata.vert; - treedata->vert_allocated = dummy_treedata.vert_allocated; + sod->bvhtree[1] = BKE_bvhtree_from_mesh_get( + &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEVERTS, 2); + if (sod->bvhtree[1] == NULL) { + sod->has_loose_vert = false; } + sod->cached[1] = treedata_tmp.cached; + BLI_assert(!ELEM(true, + treedata_tmp.vert_allocated, + treedata_tmp.edge_allocated, + treedata_tmp.face_allocated, + treedata_tmp.loop_allocated, + treedata_tmp.looptri_allocated)); } } else { @@ -2313,33 +2337,9 @@ static short snapMesh(SnapObjectContext *sctx, sod->has_loose_vert = false; } - /* Update pointers. */ - if (treedata->vert_allocated == false) { - treedata->vert = me->mvert; /* CustomData_get_layer(&me->vdata, CD_MVERT);? */ - } - if (treedata->tree || sod->bvhtree[0]) { - if (treedata->edge_allocated == false) { - /* If raycast has been executed before, `treedata->edge` can be NULL. */ - treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */ - } - if (treedata->loop && treedata->loop_allocated == false) { - treedata->loop = me->mloop; /* CustomData_get_layer(&me->edata, CD_MLOOP);? */ - } - if (treedata->looptri && treedata->looptri_allocated == false) { - treedata->looptri = BKE_mesh_runtime_looptri_ensure(me); - } - } - - Nearest2dUserData nearest2d = { - .userdata = treedata, - .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get, - .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get, - .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get, - .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get, - .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy, - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, - .use_backface_culling = use_backface_culling, - }; + Nearest2dUserData nearest2d; + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2455,7 +2455,7 @@ static short snapMesh(SnapObjectContext *sctx, static short snapEditMesh(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, + Object *ob_eval, BMEditMesh *em, const float obmat[4][4], bool use_backface_culling, @@ -2484,7 +2484,7 @@ static short snapEditMesh(SnapObjectContext *sctx, float dist_px_sq = square_f(*dist_px); - SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em); + SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em); /* Test BoundBox */ @@ -2558,14 +2558,9 @@ static short snapEditMesh(SnapObjectContext *sctx, } } - Nearest2dUserData nearest2d = { - .userdata = em, - .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get, - .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get, - .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy, - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, - .use_backface_culling = use_backface_culling, - }; + Nearest2dUserData nearest2d; + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2659,9 +2654,9 @@ struct SnapObjUserData { * \note Duplicate args here are documented at #snapObjectsRay */ static void snap_obj_fn(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, float obmat[4][4], - bool use_obedit, + eSnapEditType edit_mode_type, bool use_backface_culling, bool UNUSED(is_object_active), void *data) @@ -2669,41 +2664,36 @@ static void snap_obj_fn(SnapObjectContext *sctx, struct SnapObjUserData *dt = data; short retval = 0; - switch (ob->type) { + switch (ob_eval->type) { case OB_MESH: { - Mesh *me = ob->data; - if (BKE_object_is_in_editmode(ob)) { - if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) { - /* Operators only update the editmesh looptris of the original mesh. */ - BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob)); - retval = snapEditMesh(sctx, - dt->snapdata, - ob, - em_orig, - obmat, - use_backface_culling, - dt->dist_px, - dt->r_loc, - dt->r_no, - dt->r_index); - break; - } - - BMEditMesh *em = BKE_editmesh_from_object(ob); - if (em->mesh_eval_final) { - me = em->mesh_eval_final; - } + bool use_hide; + Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); + if (me_eval == NULL) { + /* Operators only update the editmesh looptris of the original mesh. */ + BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval)); + retval = snapEditMesh(sctx, + dt->snapdata, + ob_eval, + em_orig, + obmat, + use_backface_culling, + dt->dist_px, + dt->r_loc, + dt->r_no, + dt->r_index); + break; } - else if (ob->dt == OB_BOUNDBOX) { + if (ob_eval->dt == OB_BOUNDBOX) { /* Do not snap to objects that are in bounding box display mode */ return; } retval = snapMesh(sctx, dt->snapdata, - ob, - me, + ob_eval, + me_eval, obmat, + use_hide, use_backface_culling, dt->dist_px, dt->r_loc, @@ -2713,21 +2703,28 @@ static void snap_obj_fn(SnapObjectContext *sctx, } case OB_ARMATURE: retval = snapArmature( - dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CURVE: - retval = snapCurve( - dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + retval = snapCurve(dt->snapdata, + ob_eval, + obmat, + edit_mode_type == SNAP_GEOM_EDIT, + dt->dist_px, + dt->r_loc, + dt->r_no, + dt->r_index); break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */ case OB_SURF: case OB_FONT: { - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (mesh_eval) { retval |= snapMesh(sctx, dt->snapdata, - ob, + ob_eval, mesh_eval, obmat, + false, use_backface_culling, dt->dist_px, dt->r_loc, @@ -2740,17 +2737,17 @@ static void snap_obj_fn(SnapObjectContext *sctx, case OB_GPENCIL: case OB_LAMP: retval = snap_object_center( - dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CAMERA: retval = snapCamera( - sctx, dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + sctx, dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; } if (retval) { if (dt->r_ob) { - *dt->r_ob = ob; + *dt->r_ob = ob_eval; } if (dt->r_obmat) { copy_m4_m4(dt->r_obmat, obmat); @@ -3023,7 +3020,7 @@ static short transform_snap_context_project_view3d_mixed_impl( short retval = 0; bool has_hit = false; - Object *ob = NULL; + Object *ob_eval = NULL; float loc[3]; /* Not all snapping callbacks set the normal, * initialize this since any hit copies both the `loc` and `no`. */ @@ -3060,7 +3057,7 @@ static short transform_snap_context_project_view3d_mixed_impl( loc, no, &index, - &ob, + &ob_eval, obmat, NULL); @@ -3072,7 +3069,7 @@ static short transform_snap_context_project_view3d_mixed_impl( copy_v3_v3(r_no, no); } if (r_ob) { - *r_ob = ob; + *r_ob = ob_eval; } if (r_obmat) { copy_m4_m4(r_obmat, obmat); @@ -3108,9 +3105,10 @@ static short transform_snap_context_project_view3d_mixed_impl( snapdata.has_occlusion_plane = false; /* By convention we only snap to the original elements of a curve. */ - if (has_hit && ob->type != OB_CURVE) { + if (has_hit && ob_eval->type != OB_CURVE) { /* Compute the new clip_pane but do not add it yet. */ float new_clipplane[4]; + BLI_ASSERT_UNIT_V3(no); plane_from_point_normal_v3(new_clipplane, loc, no); if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) { /* The plane is facing the wrong direction. */ @@ -3121,8 +3119,15 @@ static short transform_snap_context_project_view3d_mixed_impl( new_clipplane[3] += 0.01f; /* Try to snap only to the polygon. */ - elem_test = snap_mesh_polygon( - sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index); + elem_test = snap_mesh_polygon(sctx, + &snapdata, + ob_eval, + obmat, + params->use_backface_culling, + &dist_px_tmp, + loc, + no, + &index); if (elem_test) { elem = elem_test; } @@ -3137,7 +3142,7 @@ static short transform_snap_context_project_view3d_mixed_impl( } elem_test = snapObjectsRay( - sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob, obmat); + sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat); if (elem_test) { elem = elem_test; } @@ -3148,7 +3153,7 @@ static short transform_snap_context_project_view3d_mixed_impl( snapdata.snap_to_flag = snap_to_flag; elem = snap_mesh_edge_verts_mixed(sctx, &snapdata, - ob, + ob_eval, obmat, *dist_px, prev_co, @@ -3167,7 +3172,7 @@ static short transform_snap_context_project_view3d_mixed_impl( copy_v3_v3(r_no, no); } if (r_ob) { - *r_ob = ob; + *r_ob = ob_eval; } if (r_obmat) { copy_m4_m4(r_obmat, obmat); diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 54ea0103fe5..cd1597a742c 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -666,7 +666,7 @@ class CPPType : NonCopyable, NonMovable { template<typename T> bool is() const { - return this == &CPPType::get<T>(); + return this == &CPPType::get<std::decay_t<T>>(); } }; diff --git a/source/blender/functions/FN_generic_pointer.hh b/source/blender/functions/FN_generic_pointer.hh index 2bd66daa7fe..f88ff09f916 100644 --- a/source/blender/functions/FN_generic_pointer.hh +++ b/source/blender/functions/FN_generic_pointer.hh @@ -66,6 +66,16 @@ class GMutablePointer { return type_ != nullptr && type_->is<T>(); } + template<typename T> T relocate_out() + { + BLI_assert(this->is_type<T>()); + T value; + type_->relocate_to_initialized(data_, &value); + data_ = nullptr; + type_ = nullptr; + return value; + } + void destruct() { BLI_assert(data_ != nullptr); diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/functions/FN_generic_span.hh index 31b67dd3d70..e2c49697ba9 100644 --- a/source/blender/functions/FN_generic_span.hh +++ b/source/blender/functions/FN_generic_span.hh @@ -30,7 +30,7 @@ namespace blender::fn { * A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time. */ class GSpan { - private: + protected: const CPPType *type_; const void *data_; int64_t size_; @@ -85,6 +85,14 @@ class GSpan { BLI_assert(type_->is<T>()); return Span<T>(static_cast<const T *>(data_), size_); } + + GSpan slice(const int64_t start, int64_t size) const + { + BLI_assert(start >= 0); + BLI_assert(size >= 0); + const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start)); + return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size); + } }; /** @@ -92,7 +100,7 @@ class GSpan { * known at run-time. */ class GMutableSpan { - private: + protected: const CPPType *type_; void *data_; int64_t size_; @@ -153,6 +161,14 @@ class GMutableSpan { BLI_assert(type_->is<T>()); return MutableSpan<T>(static_cast<T *>(data_), size_); } + + GMutableSpan slice(const int64_t start, int64_t size) const + { + BLI_assert(start >= 0); + BLI_assert(size >= 0); + const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start)); + return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size); + } }; } // namespace blender::fn diff --git a/source/blender/functions/FN_generic_value_map.hh b/source/blender/functions/FN_generic_value_map.hh index 68cb945f1af..4e7fe298874 100644 --- a/source/blender/functions/FN_generic_value_map.hh +++ b/source/blender/functions/FN_generic_value_map.hh @@ -93,6 +93,11 @@ template<typename Key> class GValueMap { return values_.pop_as(key); } + template<typename ForwardKey> GPointer lookup(const ForwardKey &key) const + { + return values_.lookup_as(key); + } + /* Remove the value for the given name from the container and remove it. */ template<typename T, typename ForwardKey> T extract(const ForwardKey &key) { diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index ae6eb8a614f..b02ed471875 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -123,7 +123,7 @@ template<typename T> class GVectorArray_TypedMutableRef { void extend(const int64_t index, const VArray<T> &values) { - GVArrayForVArray<T> array{values}; + GVArray_For_VArray<T> array{values}; this->extend(index, array); } @@ -134,12 +134,12 @@ template<typename T> class GVectorArray_TypedMutableRef { }; /* A generic virtual vector array implementation for a `GVectorArray`. */ -class GVVectorArrayForGVectorArray : public GVVectorArray { +class GVVectorArray_For_GVectorArray : public GVVectorArray { private: const GVectorArray &vector_array_; public: - GVVectorArrayForGVectorArray(const GVectorArray &vector_array) + GVVectorArray_For_GVectorArray(const GVectorArray &vector_array) : GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array) { } diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index c6230730a8d..2e91479dd30 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -23,12 +23,23 @@ * the data type is only known at runtime. */ +#include <optional> + #include "BLI_virtual_array.hh" #include "FN_generic_span.hh" namespace blender::fn { +template<typename T> class GVArray_Typed; +template<typename T> class GVMutableArray_Typed; + +class GVArray; +class GVMutableArray; + +using GVArrayPtr = std::unique_ptr<GVArray>; +using GVMutableArrayPtr = std::unique_ptr<GVMutableArray>; + /* A generically typed version of `VArray<T>`. */ class GVArray { protected: @@ -86,13 +97,13 @@ class GVArray { /* Returns the internally used span of the virtual array. This invokes undefined behavior is the * virtual array is not stored as a span internally. */ - GSpan get_span() const + GSpan get_internal_span() const { BLI_assert(this->is_span()); if (size_ == 0) { return GSpan(*type_); } - return this->get_span_impl(); + return this->get_internal_span_impl(); } /* Returns true when the virtual array returns the same value for every index. */ @@ -107,57 +118,139 @@ class GVArray { /* Copies the value that is used for every element into `r_value`, which is expected to point to * initialized memory. This invokes undefined behavior if the virtual array would not return the * same value for every index. */ - void get_single(void *r_value) const + void get_internal_single(void *r_value) const { BLI_assert(this->is_single()); if (size_ == 1) { this->get(0, r_value); + return; } - this->get_single_impl(r_value); + this->get_internal_single_impl(r_value); } - /* Same as `get_single`, but `r_value` points to initialized memory. */ + /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ void get_single_to_uninitialized(void *r_value) const { type_->construct_default(r_value); - this->get_single(r_value); + this->get_internal_single(r_value); } + void materialize_to_uninitialized(void *dst) const; void materialize_to_uninitialized(const IndexMask mask, void *dst) const; + template<typename T> const VArray<T> *try_get_internal_varray() const + { + BLI_assert(type_->is<T>()); + return (const VArray<T> *)this->try_get_internal_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template<typename T> GVArray_Typed<T> typed() const + { + return GVArray_Typed<T>(*this); + } + + GVArrayPtr shallow_copy() const; + protected: virtual void get_impl(const int64_t index, void *r_value) const; virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0; virtual bool is_span_impl() const; - virtual GSpan get_span_impl() const; + virtual GSpan get_internal_span_impl() const; virtual bool is_single_impl() const; - virtual void get_single_impl(void *UNUSED(r_value)) const; + virtual void get_internal_single_impl(void *UNUSED(r_value)) const; + + virtual void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const; + + virtual const void *try_get_internal_varray_impl() const; +}; + +/* Similar to GVArray, but supports changing the elements in the virtual array. */ +class GVMutableArray : public GVArray { + public: + GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + + void set_by_copy(const int64_t index, const void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_copy_impl(index, value); + } + + void set_by_move(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_move_impl(index, value); + } + + void set_by_relocate(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_relocate_impl(index, value); + } + + GMutableSpan get_internal_span() + { + BLI_assert(this->is_span()); + GSpan span = static_cast<const GVArray *>(this)->get_internal_span(); + return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size()); + } + + template<typename T> VMutableArray<T> *try_get_internal_mutable_varray() + { + BLI_assert(type_->is<T>()); + return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template<typename T> GVMutableArray_Typed<T> typed() + { + return GVMutableArray_Typed<T>(*this); + } + + void fill(const void *value); + + protected: + virtual void set_by_copy_impl(const int64_t index, const void *value); + virtual void set_by_relocate_impl(const int64_t index, void *value); + virtual void set_by_move_impl(const int64_t index, void *value) = 0; + + virtual void *try_get_internal_mutable_varray_impl(); }; -class GVArrayForGSpan : public GVArray { +class GVArray_For_GSpan : public GVArray { protected: - const void *data_; + const void *data_ = nullptr; const int64_t element_size_; public: - GVArrayForGSpan(const GSpan span) + GVArray_For_GSpan(const GSpan span) : GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) { } protected: + GVArray_For_GSpan(const CPPType &type, const int64_t size) + : GVArray(type, size), element_size_(type.size()) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; }; -class GVArrayForEmpty : public GVArray { +class GVArray_For_Empty : public GVArray { public: - GVArrayForEmpty(const CPPType &type) : GVArray(type, 0) + GVArray_For_Empty(const CPPType &type) : GVArray(type, 0) { } @@ -168,108 +261,622 @@ class GVArrayForEmpty : public GVArray { } }; -class GVArrayForSingleValueRef : public GVArray { - private: - const void *value_; +class GVMutableArray_For_GMutableSpan : public GVMutableArray { + protected: + void *data_ = nullptr; + const int64_t element_size_; public: - GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value) + GVMutableArray_For_GMutableSpan(const GMutableSpan span) + : GVMutableArray(span.type(), span.size()), + data_(span.data()), + element_size_(span.type().size()) + { + } + + protected: + GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size) + : GVMutableArray(type, size), element_size_(type.size()) + { + } + + void get_impl(const int64_t index, void *r_value) const override; + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; + + void set_by_copy_impl(const int64_t index, const void *value) override; + void set_by_move_impl(const int64_t index, void *value) override; + void set_by_relocate_impl(const int64_t index, void *value) override; + + bool is_span_impl() const override; + GSpan get_internal_span_impl() const override; +}; + +/* Generic virtual array where each element has the same value. The value is not owned. */ +class GVArray_For_SingleValueRef : public GVArray { + protected: + const void *value_ = nullptr; + + public: + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) : GVArray(type, size), value_(value) { } protected: + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; bool is_single_impl() const override; - void get_single_impl(void *r_value) const override; + void get_internal_single_impl(void *r_value) const override; }; -template<typename T> class GVArrayForVArray : public GVArray { - private: - const VArray<T> &array_; +/* Same as GVArray_For_SingleValueRef, but the value is owned. */ +class GVArray_For_SingleValue : public GVArray_For_SingleValueRef { + public: + GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value); + ~GVArray_For_SingleValue(); +}; + +/* Used to convert a typed virtual array into a generic one. */ +template<typename T> class GVArray_For_VArray : public GVArray { + protected: + const VArray<T> *varray_ = nullptr; public: - GVArrayForVArray(const VArray<T> &array) - : GVArray(CPPType::get<T>(), array.size()), array_(array) + GVArray_For_VArray(const VArray<T> &varray) + : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray) { } protected: + GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size) + { + } + void get_impl(const int64_t index, void *r_value) const override { - *(T *)r_value = array_.get(index); + *(T *)r_value = varray_->get(index); } void get_to_uninitialized_impl(const int64_t index, void *r_value) const override { - new (r_value) T(array_.get(index)); + new (r_value) T(varray_->get(index)); } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - GSpan get_span_impl() const override + GSpan get_internal_span_impl() const override { - return GSpan(array_.get_span()); + return GSpan(varray_->get_internal_span()); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); } - void get_single_impl(void *r_value) const override + void get_internal_single_impl(void *r_value) const override { - *(T *)r_value = array_.get_single(); + *(T *)r_value = varray_->get_internal_single(); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override + { + varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + const void *try_get_internal_varray_impl() const override + { + return varray_; } }; -template<typename T> class VArrayForGVArray : public VArray<T> { - private: - const GVArray &array_; +/* Used to convert any generic virtual array into a typed one. */ +template<typename T> class VArray_For_GVArray : public VArray<T> { + protected: + const GVArray *varray_ = nullptr; public: - VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array) + VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray) { - BLI_assert(array_.type().template is<T>()); + BLI_assert(varray_->type().template is<T>()); } protected: + VArray_For_GVArray(const int64_t size) : VArray<T>(size) + { + } + T get_impl(const int64_t index) const override { T value; - array_.get(index, &value); + varray_->get(index, &value); return value; } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { - return array_.get_span().template typed<T>(); + return varray_->get_internal_span().template typed<T>(); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); } - T get_single_impl() const override + T get_internal_single_impl() const override { T value; - array_.get_single(&value); + varray_->get_internal_single(&value); return value; } }; +/* Used to convert an generic mutable virtual array into a typed one. */ +template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> { + protected: + GVMutableArray *varray_ = nullptr; + + public: + VMutableArray_For_GVMutableArray(GVMutableArray &varray) + : VMutableArray<T>(varray.size()), varray_(&varray) + { + BLI_assert(varray.type().template is<T>()); + } + + VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size) + { + } + + private: + T get_impl(const int64_t index) const override + { + T value; + varray_->get(index, &value); + return value; + } + + void set_impl(const int64_t index, T value) override + { + varray_->set_by_relocate(index, &value); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + Span<T> get_internal_span_impl() const override + { + return varray_->get_internal_span().template typed<T>(); + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + T get_internal_single_impl() const override + { + T value; + varray_->get_internal_single(&value); + return value; + } +}; + +/* Used to convert any typed virtual mutable array into a generic one. */ +template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray { + protected: + VMutableArray<T> *varray_ = nullptr; + + public: + GVMutableArray_For_VMutableArray(VMutableArray<T> &varray) + : GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray) + { + } + + protected: + GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size) + { + } + + void get_impl(const int64_t index, void *r_value) const override + { + *(T *)r_value = varray_->get(index); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + new (r_value) T(varray_->get(index)); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + GSpan get_internal_span_impl() const override + { + Span<T> span = varray_->get_internal_span(); + return span; + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + void get_internal_single_impl(void *r_value) const override + { + *(T *)r_value = varray_->get_internal_single(); + } + + void set_by_copy_impl(const int64_t index, const void *value) override + { + const T &value_ = *(const T *)value; + varray_->set(index, value_); + } + + void set_by_relocate_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + value_.~T(); + } + + void set_by_move_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + } + + const void *try_get_internal_varray_impl() const override + { + return (const VArray<T> *)varray_; + } + + void *try_get_internal_mutable_varray_impl() override + { + return varray_; + } +}; + +/* A generic version of VArray_Span. */ +class GVArray_GSpan : public GSpan { + private: + const GVArray &varray_; + void *owned_data_ = nullptr; + + public: + GVArray_GSpan(const GVArray &varray); + ~GVArray_GSpan(); +}; + +/* A generic version of VMutableArray_Span. */ +class GVMutableArray_GSpan : public GMutableSpan { + private: + GVMutableArray &varray_; + void *owned_data_ = nullptr; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true); + ~GVMutableArray_GSpan(); + + void save(); + void disable_not_applied_warning(); +}; + +/* Similar to GVArray_GSpan, but the resulting span is typed. */ +template<typename T> class GVArray_Span : public Span<T> { + private: + GVArray_GSpan varray_gspan_; + + public: + GVArray_Span(const GVArray &varray) : varray_gspan_(varray) + { + BLI_assert(varray.type().is<T>()); + this->data_ = (const T *)varray_gspan_.data(); + this->size_ = varray_gspan_.size(); + } +}; + +template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> { + private: + VArrayPtr<T> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVArray_For_OwnedVArray(VArrayPtr<T> varray) + : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> { + private: + GVArrayPtr owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VArray_For_OwnedGVArray(GVArrayPtr varray) + : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> +class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> { + private: + VMutableArrayPtr<T> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray) + : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> +class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> { + private: + GVMutableArrayPtr owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray) + : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give + * the compiler more opportunity to optimize the generic virtual array. */ +template<typename T, typename VArrayT> +class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> { + private: + VArrayT embedded_varray_; + + public: + template<typename... Args> + GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args) + : GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */ +template<typename T, typename VMutableArrayT> +class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> { + private: + VMutableArrayT embedded_varray_; + + public: + template<typename... Args> + GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args) + : GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */ +template<typename Container, typename T = typename Container::value_type> +class GVArray_For_ArrayContainer + : public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> { + public: + GVArray_For_ArrayContainer(Container container) + : GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>( + container.size(), std::move(container)) + { + } +}; + +/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */ +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +class GVArray_For_DerivedSpan + : public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> { + public: + GVArray_For_DerivedSpan(const Span<StructT> data) + : GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>( + data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */ +template<typename StructT, + typename ElemT, + ElemT (*GetFunc)(const StructT &), + void (*SetFunc)(StructT &, ElemT)> +class GVMutableArray_For_DerivedSpan + : public GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> { + public: + GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data) + : GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data) + { + } +}; + +/* Same as VArray_For_Span, but for a generic virtual array. */ +template<typename T> +class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> { + public: + GVArray_For_Span(const Span<T> data) + : GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */ +template<typename T> +class GVMutableArray_For_MutableSpan + : public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> { + public: + GVMutableArray_For_MutableSpan(const MutableSpan<T> data) + : GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(), + data) + { + } +}; + +/** + * Utility class to create the "best" typed virtual array for a given generic virtual array. + * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional + * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is + * just a span or single value). + * + * This is not a virtual array itself, but is used to get a virtual array. + */ +template<typename T> class GVArray_Typed { + private: + const VArray<T> *varray_; + /* Of these optional virtual arrays, at most one is constructed at any time. */ + std::optional<VArray_For_Span<T>> varray_span_; + std::optional<VArray_For_Single<T>> varray_single_; + std::optional<VArray_For_GVArray<T>> varray_any_; + GVArrayPtr owned_gvarray_; + + public: + explicit GVArray_Typed(const GVArray &gvarray) + { + BLI_assert(gvarray.type().is<T>()); + if (gvarray.is_span()) { + const GSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed<T>()); + varray_ = &*varray_span_; + } + else if (gvarray.is_single()) { + T single_value; + gvarray.get_internal_single(&single_value); + varray_single_.emplace(single_value, gvarray.size()); + varray_ = &*varray_single_; + } + else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + /* Same as the constructor above, but also takes ownership of the passed in virtual array. */ + explicit GVArray_Typed(GVArrayPtr gvarray) : GVArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + const VArray<T> &operator*() const + { + return *varray_; + } + + const VArray<T> *operator->() const + { + return varray_; + } + + /* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is + * used within an expression. */ + operator const VArray<T> &() const + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } + + IndexRange index_range() const + { + return IndexRange(this->size()); + } +}; + +/* Same as GVArray_Typed, but for mutable virtual arrays. */ +template<typename T> class GVMutableArray_Typed { + private: + VMutableArray<T> *varray_; + std::optional<VMutableArray_For_MutableSpan<T>> varray_span_; + std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_; + GVMutableArrayPtr owned_gvarray_; + + public: + explicit GVMutableArray_Typed(GVMutableArray &gvarray) + { + BLI_assert(gvarray.type().is<T>()); + if (gvarray.is_span()) { + const GMutableSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed<T>()); + varray_ = &*varray_span_; + } + else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + VMutableArray<T> &operator*() + { + return *varray_; + } + + VMutableArray<T> *operator->() + { + return varray_; + } + + operator VMutableArray<T> &() + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } +}; + } // namespace blender::fn diff --git a/source/blender/functions/FN_generic_virtual_vector_array.hh b/source/blender/functions/FN_generic_virtual_vector_array.hh index ef3f53b5c25..4155a55a801 100644 --- a/source/blender/functions/FN_generic_virtual_vector_array.hh +++ b/source/blender/functions/FN_generic_virtual_vector_array.hh @@ -100,13 +100,13 @@ class GVVectorArray { } }; -class GVArrayForGVVectorArrayIndex : public GVArray { +class GVArray_For_GVVectorArrayIndex : public GVArray { private: const GVVectorArray &vector_array_; const int64_t index_; public: - GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index) + GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index) : GVArray(vector_array.type(), vector_array.get_vector_size(index)), vector_array_(vector_array), index_(index) @@ -118,12 +118,12 @@ class GVArrayForGVVectorArrayIndex : public GVArray { void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override; }; -class GVVectorArrayForSingleGVArray : public GVVectorArray { +class GVVectorArray_For_SingleGVArray : public GVVectorArray { private: const GVArray &array_; public: - GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size) + GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size) : GVVectorArray(array.type(), size), array_(array) { } @@ -137,12 +137,12 @@ class GVVectorArrayForSingleGVArray : public GVVectorArray { bool is_single_vector_impl() const override; }; -class GVVectorArrayForSingleGSpan : public GVVectorArray { +class GVVectorArray_For_SingleGSpan : public GVVectorArray { private: const GSpan span_; public: - GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size) + GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size) : GVVectorArray(span.type(), size), span_(span) { } @@ -156,12 +156,12 @@ class GVVectorArrayForSingleGSpan : public GVVectorArray { bool is_single_vector_impl() const override; }; -template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> { +template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> { private: const GVVectorArray &vector_array_; public: - VVectorArrayForGVVectorArray(const GVVectorArray &vector_array) + VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array) : VVectorArray<T>(vector_array.size()), vector_array_(vector_array) { } diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 72ebc0d9b94..3b15f0278f3 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -55,13 +55,13 @@ class MFParamsBuilder { template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct<GVArrayForSingleValueRef>( + this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>( __func__, CPPType::get<T>(), min_array_size_, value), expected_name); } void add_readonly_single_input(const GSpan span, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct<GVArrayForGSpan>(__func__, span), + this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(__func__, span), expected_name); } void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "") @@ -74,7 +74,7 @@ class MFParamsBuilder { void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "") { this->add_readonly_vector_input( - scope_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name); + scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name); } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") { @@ -177,7 +177,7 @@ class MFParams { template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "") { const GVArray &array = this->readonly_single_input(param_index, name); - return builder_->scope_.construct<VArrayForGVArray<T>>(__func__, array); + return builder_->scope_.construct<VArray_For_GVArray<T>>(__func__, array); } const GVArray &readonly_single_input(int param_index, StringRef name = "") { @@ -202,7 +202,7 @@ class MFParams { const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "") { const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name); - return builder_->scope_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array); + return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array); } const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "") { diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc index b3c5517cc43..3335b07e559 100644 --- a/source/blender/functions/intern/generic_vector_array.cc +++ b/source/blender/functions/intern/generic_vector_array.cc @@ -60,21 +60,21 @@ void GVectorArray::extend(const int64_t index, const GVArray &values) void GVectorArray::extend(const int64_t index, const GSpan values) { - GVArrayForGSpan varray{values}; + GVArray_For_GSpan varray{values}; this->extend(index, varray); } void GVectorArray::extend(IndexMask mask, const GVVectorArray &values) { for (const int i : mask) { - GVArrayForGVVectorArrayIndex array{values, i}; + GVArray_For_GVVectorArrayIndex array{values, i}; this->extend(i, array); } } void GVectorArray::extend(IndexMask mask, const GVectorArray &values) { - GVVectorArrayForGVectorArray virtual_values{values}; + GVVectorArray_For_GVectorArray virtual_values{values}; this->extend(mask, virtual_values); } diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc index 9380eb257b2..ca2bd0f806f 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -18,8 +18,54 @@ namespace blender::fn { +/* -------------------------------------------------------------------- + * GVArray_For_ShallowCopy. + */ + +class GVArray_For_ShallowCopy : public GVArray { + private: + const GVArray &varray_; + + public: + GVArray_For_ShallowCopy(const GVArray &varray) + : GVArray(varray.type(), varray.size()), varray_(varray) + { + } + + private: + void get_impl(const int64_t index, void *r_value) const override + { + varray_.get(index, r_value); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + varray_.get_to_uninitialized(index, r_value); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override + { + varray_.materialize_to_uninitialized(mask, dst); + } +}; + +/* -------------------------------------------------------------------- + * GVArray. + */ + +void GVArray::materialize_to_uninitialized(void *dst) const +{ + this->materialize_to_uninitialized(IndexMask(size_), dst); +} + void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_to_uninitialized_impl(mask, dst); +} + +void GVArray::materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const +{ for (const int64_t i : mask) { void *elem_dst = POINTER_OFFSET(dst, type_->size() * i); this->get_to_uninitialized(i, elem_dst); @@ -37,7 +83,7 @@ bool GVArray::is_span_impl() const return false; } -GSpan GVArray::get_span_impl() const +GSpan GVArray::get_internal_span_impl() const { BLI_assert(false); return GSpan(*type_); @@ -48,60 +94,266 @@ bool GVArray::is_single_impl() const return false; } -void GVArray::get_single_impl(void *UNUSED(r_value)) const +void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const { BLI_assert(false); } -void GVArrayForGSpan::get_impl(const int64_t index, void *r_value) const +const void *GVArray::try_get_internal_varray_impl() const +{ + return nullptr; +} + +/** + * Creates a new `std::unique_ptr<GVArray>` based on this `GVArray`. + * The lifetime of the returned virtual array must not be longer than the lifetime of this virtual + * array. + */ +GVArrayPtr GVArray::shallow_copy() const +{ + if (this->is_span()) { + return std::make_unique<GVArray_For_GSpan>(this->get_internal_span()); + } + if (this->is_single()) { + BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer); + this->get_internal_single(buffer); + std::unique_ptr new_varray = std::make_unique<GVArray_For_SingleValue>(*type_, size_, buffer); + type_->destruct(buffer); + return new_varray; + } + return std::make_unique<GVArray_For_ShallowCopy>(*this); +} + +/* -------------------------------------------------------------------- + * GVMutableArray. + */ + +void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value) +{ + BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer); + type_->copy_to_uninitialized(value, buffer); + this->set_by_move_impl(index, buffer); + type_->destruct(buffer); +} + +void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value) +{ + this->set_by_move_impl(index, value); + type_->destruct(value); +} + +void *GVMutableArray::try_get_internal_mutable_varray_impl() +{ + return nullptr; +} + +void GVMutableArray::fill(const void *value) +{ + if (this->is_span()) { + const GMutableSpan span = this->get_internal_span(); + type_->fill_initialized(value, span.data(), size_); + } + else { + for (int64_t i : IndexRange(size_)) { + this->set_by_copy(i, value); + } + } +} + +/* -------------------------------------------------------------------- + * GVArray_For_GSpan. + */ + +void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const +{ + type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value); +} + +void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const +{ + type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value); +} + +bool GVArray_For_GSpan::is_span_impl() const +{ + return true; +} + +GSpan GVArray_For_GSpan::get_internal_span_impl() const +{ + return GSpan(*type_, data_, size_); +} + +/* -------------------------------------------------------------------- + * GVMutableArray_For_GMutableSpan. + */ + +void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const { type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value); } -void GVArrayForGSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const +void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index, + void *r_value) const { type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value); } -bool GVArrayForGSpan::is_span_impl() const +void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value) +{ + type_->copy_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value) +{ + type_->move_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value) +{ + type_->relocate_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +bool GVMutableArray_For_GMutableSpan::is_span_impl() const { return true; } -GSpan GVArrayForGSpan::get_span_impl() const +GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const { return GSpan(*type_, data_, size_); } -void GVArrayForSingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const +/* -------------------------------------------------------------------- + * GVArray_For_SingleValueRef. + */ + +void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const { type_->copy_to_initialized(value_, r_value); } -void GVArrayForSingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index), - void *r_value) const +void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index), + void *r_value) const { type_->copy_to_uninitialized(value_, r_value); } -bool GVArrayForSingleValueRef::is_span_impl() const +bool GVArray_For_SingleValueRef::is_span_impl() const { return size_ == 1; } -GSpan GVArrayForSingleValueRef::get_span_impl() const +GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const { return GSpan{*type_, value_, 1}; } -bool GVArrayForSingleValueRef::is_single_impl() const +bool GVArray_For_SingleValueRef::is_single_impl() const { return true; } -void GVArrayForSingleValueRef::get_single_impl(void *r_value) const +void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const { type_->copy_to_initialized(value_, r_value); } +/* -------------------------------------------------------------------- + * GVArray_For_SingleValue. + */ + +GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type, + const int64_t size, + const void *value) + : GVArray_For_SingleValueRef(type, size) +{ + value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + type.copy_to_uninitialized(value, (void *)value_); +} + +GVArray_For_SingleValue::~GVArray_For_SingleValue() +{ + type_->destruct((void *)value_); + MEM_freeN((void *)value_); +} + +/* -------------------------------------------------------------------- + * GVArray_GSpan. + */ + +GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray) +{ + size_ = varray_.size(); + if (varray_.is_span()) { + data_ = varray_.get_internal_span().data(); + } + else { + owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__); + varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_); + data_ = owned_data_; + } +} + +GVArray_GSpan::~GVArray_GSpan() +{ + if (owned_data_ != nullptr) { + type_->destruct_n(owned_data_, size_); + MEM_freeN(owned_data_); + } +} + +/* -------------------------------------------------------------------- + * GVMutableArray_GSpan. + */ + +GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span) + : GMutableSpan(varray.type()), varray_(varray) +{ + size_ = varray_.size(); + if (varray_.is_span()) { + data_ = varray_.get_internal_span().data(); + } + else { + owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__); + if (copy_values_to_span) { + varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_); + } + else { + type_->construct_default_n(owned_data_, size_); + } + data_ = owned_data_; + } +} + +GVMutableArray_GSpan::~GVMutableArray_GSpan() +{ + if (show_not_saved_warning_) { + if (!save_has_been_called_) { + std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n"; + } + } + if (owned_data_ != nullptr) { + type_->destruct_n(owned_data_, size_); + MEM_freeN(owned_data_); + } +} + +void GVMutableArray_GSpan::save() +{ + save_has_been_called_ = true; + if (data_ != owned_data_) { + return; + } + const int64_t element_size = type_->size(); + for (int64_t i : IndexRange(size_)) { + varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i)); + } +} + +void GVMutableArray_GSpan::disable_not_applied_warning() +{ + show_not_saved_warning_ = false; +} + } // namespace blender::fn diff --git a/source/blender/functions/intern/generic_virtual_vector_array.cc b/source/blender/functions/intern/generic_virtual_vector_array.cc index f6504cee41e..aa3d90883c6 100644 --- a/source/blender/functions/intern/generic_virtual_vector_array.cc +++ b/source/blender/functions/intern/generic_virtual_vector_array.cc @@ -18,48 +18,48 @@ namespace blender::fn { -void GVArrayForGVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const +void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const { vector_array_.get_vector_element(index_, index_in_vector, r_value); } -void GVArrayForGVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector, - void *r_value) const +void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector, + void *r_value) const { type_->construct_default(r_value); vector_array_.get_vector_element(index_, index_in_vector, r_value); } -int64_t GVVectorArrayForSingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const +int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const { return array_.size(); } -void GVVectorArrayForSingleGVArray::get_vector_element_impl(const int64_t UNUSED(index), - const int64_t index_in_vector, - void *r_value) const +void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index), + const int64_t index_in_vector, + void *r_value) const { array_.get(index_in_vector, r_value); } -bool GVVectorArrayForSingleGVArray::is_single_vector_impl() const +bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const { return true; } -int64_t GVVectorArrayForSingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const +int64_t GVVectorArray_For_SingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const { return span_.size(); } -void GVVectorArrayForSingleGSpan::get_vector_element_impl(const int64_t UNUSED(index), - const int64_t index_in_vector, - void *r_value) const +void GVVectorArray_For_SingleGSpan::get_vector_element_impl(const int64_t UNUSED(index), + const int64_t index_in_vector, + void *r_value) const { type_->copy_to_initialized(span_[index_in_vector], r_value); } -bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const +bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const { return true; } diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 86ac4f6a179..9a0cb0c35ce 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -974,11 +974,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); if (value->is_single_allocated) { - return scope.construct<GVArrayForSingleValueRef>( + return scope.construct<GVArray_For_SingleValueRef>( __func__, value->span.type(), min_array_size_, value->span.data()); } - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); @@ -987,11 +987,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS if (any_value->type == ValueType::OutputSingle) { OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } BLI_assert(false); - return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); + return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); } const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket, @@ -1004,7 +1004,7 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); BLI_assert(value->span.size() == 1); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); @@ -1015,11 +1015,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); BLI_assert(value->span.size() == 1); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } BLI_assert(false); - return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); + return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); } const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( @@ -1033,10 +1033,10 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); if (value->vector_array->size() == 1) { GSpan span = (*value->vector_array)[0]; - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_); } - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); @@ -1044,11 +1044,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( } if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); } const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( @@ -1061,7 +1061,7 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( if (any_value->type == ValueType::OwnVector) { OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); @@ -1071,11 +1071,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); } /** \} */ diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc index 51e116b5983..7b9738e5ca4 100644 --- a/source/blender/functions/tests/FN_multi_function_network_test.cc +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -223,7 +223,7 @@ TEST(multi_function_network, Test2) Array<int> output_value_2(5, -1); MFParamsBuilder params(network_fn, 5); - GVVectorArrayForSingleGSpan inputs_1{input_value_1.as_span(), 5}; + GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5}; params.add_readonly_vector_input(inputs_1); params.add_readonly_single_input(&input_value_2); params.add_vector_output(output_value_1); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index a156fca5b7b..94285b5032e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -203,6 +203,20 @@ void gpencil_modifier_curve_panel_draw(const bContext *UNUSED(C), Panel *panel) uiTemplateCurveMapping(layout, ptr, "curve", 0, false, false, false, false); } +void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel) +{ + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayout *layout = panel->layout; + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "object", 0, NULL, ICON_CUBE); + uiLayout *sub = uiLayoutColumn(layout, true); + uiItemR(sub, ptr, "fading_start", 0, NULL, ICON_NONE); + uiItemR(sub, ptr, "fading_end", 0, IFACE_("End"), ICON_NONE); + uiItemR(layout, ptr, "fading_end_factor", 0, NULL, ICON_NONE); +} + /** * Draw modifier error message. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h index 782b36d47ed..75907aaa781 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h @@ -37,6 +37,8 @@ void gpencil_modifier_masking_panel_draw(Panel *panel, bool use_material, bool u void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel); void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel); +void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel); + void gpencil_modifier_panel_end(struct uiLayout *layout, PointerRNA *ptr); struct PointerRNA *gpencil_modifier_panel_get_property_pointers(struct Panel *panel, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index d7fc08e38f3..d9f0fc9bddd 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -455,8 +455,10 @@ static void generate_geometry( /* Compute start and end frames for the animation effect * By default, the upper bound is given by the "maximum length" setting */ - float start_frame = gpf->framenum + mmd->start_delay; - float end_frame = start_frame + mmd->length; + float start_frame = is_percentage ? gpf->framenum : gpf->framenum + mmd->start_delay; + /* When use percentage don't need a limit in the upper bound, so use a maximum value for the last + * frame. */ + float end_frame = is_percentage ? start_frame + 9999 : start_frame + mmd->length; if (gpf->next) { /* Use the next frame or upper bound as end frame, whichever is lower/closer */ @@ -547,6 +549,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); int mode = RNA_enum_get(ptr, "mode"); + const bool use_percentage = RNA_boolean_get(ptr, "use_percentage"); uiLayoutSetPropSep(layout, true); @@ -558,8 +561,12 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemS(layout); uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "start_delay", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "length", 0, IFACE_("Frames"), ICON_NONE); + row = uiLayoutRow(layout, true); + uiLayoutSetActive(row, !use_percentage); + uiItemR(row, ptr, "start_delay", 0, NULL, ICON_NONE); + row = uiLayoutRow(layout, true); + uiLayoutSetActive(row, !use_percentage); + uiItemR(row, ptr, "length", 0, IFACE_("Frames"), ICON_NONE); uiItemS(layout); @@ -567,7 +574,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropDecorate(row, false); uiItemR(row, ptr, "use_percentage", 0, "", ICON_NONE); sub = uiLayoutRow(row, true); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_percentage")); + uiLayoutSetActive(sub, use_percentage); uiItemR(sub, ptr, "percentage_factor", 0, "", ICON_NONE); uiItemDecoratorR(row, ptr, "percentage_factor", 0); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 080a3caa32f..a2f35c8e575 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -335,7 +335,7 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) if (use_multiple_levels) { uiLayout *col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "level_start", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "level_end", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "level_end", 0, IFACE_("End"), ICON_NONE); } else { uiItemR(layout, ptr, "level_start", 0, IFACE_("Level"), ICON_NONE); @@ -367,7 +367,7 @@ static void transparency_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *row = uiLayoutRow(layout, true); uiLayoutSetPropDecorate(row, false); - uiLayout *sub = uiLayoutRow(row, true); + uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks")); char text[2] = "0"; PropertyRNA *prop = RNA_struct_find_property(ptr, "use_transparency_mask"); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c index bffb324f07f..cd29a006aae 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -26,7 +26,11 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + +#include "BLI_hash.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" @@ -71,7 +75,7 @@ static void deformStroke(GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), Object *ob, bGPDlayer *gpl, - bGPDframe *UNUSED(gpf), + bGPDframe *gpf, bGPDstroke *gps) { OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md; @@ -94,6 +98,46 @@ static void deformStroke(GpencilModifierData *md, mmd->flag & GP_OFFSET_INVERT_MATERIAL)) { return; } + + int seed = mmd->seed; + /* Make sure different modifiers get different seeds. */ + seed += BLI_hash_string(ob->id.name + 2); + seed += BLI_hash_string(md->name); + + float rand[3][3]; + float rand_offset = BLI_hash_int_01(seed); + + /* Get stroke index for random offset. */ + int rnd_index = BLI_findindex(&gpf->strokes, gps); + for (int j = 0; j < 3; j++) { + const uint primes[3] = {2, 3, 7}; + double offset[3] = {0.0f, 0.0f, 0.0f}; + double r[3]; + /* To ensure a nice distribution, we use halton sequence and offset using the seed. */ + BLI_halton_3d(primes, offset, rnd_index, r); + + if ((mmd->flag & GP_OFFSET_UNIFORM_RANDOM_SCALE) && j == 2) { + float rand_value; + rand_value = fmodf(r[0] * 2.0f - 1.0f + rand_offset, 1.0f); + rand_value = fmodf(sin(rand_value * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f); + copy_v3_fl(rand[j], rand_value); + } + else { + for (int i = 0; i < 3; i++) { + rand[j][i] = fmodf(r[i] * 2.0f - 1.0f + rand_offset, 1.0f); + rand[j][i] = fmodf(sin(rand[j][i] * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f); + } + } + } + /* Calculate Random matrix. */ + float mat_rnd[4][4]; + float rnd_loc[3], rnd_rot[3]; + float rnd_scale[3] = {1.0f, 1.0f, 1.0f}; + mul_v3_v3v3(rnd_loc, mmd->rnd_offset, rand[0]); + mul_v3_v3v3(rnd_rot, mmd->rnd_rot, rand[1]); + madd_v3_v3v3(rnd_scale, mmd->rnd_scale, rand[2]); + loc_eul_size_to_mat4(mat_rnd, rnd_loc, rnd_rot, rnd_scale); + bGPdata *gpd = ob->data; for (int i = 0; i < gps->totpoints; i++) { @@ -106,6 +150,9 @@ static void deformStroke(GpencilModifierData *md, if (weight < 0.0f) { continue; } + /* Apply randomness matrix. */ + mul_m4_v3(mat_rnd, &pt->x); + /* Calculate matrix. */ mul_v3_v3fl(loc, mmd->loc, weight); mul_v3_v3fl(rot, mmd->rot, weight); @@ -161,6 +208,21 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_panel_end(layout, ptr); } +static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE); + uiItemR(layout, ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE); + uiItemR(layout, ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE); + uiItemR(layout, ptr, "use_uniform_random_scale", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE); +} + static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) { gpencil_modifier_masking_panel_draw(panel, true, true); @@ -171,6 +233,8 @@ static void panelRegister(ARegionType *region_type) PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Offset, panel_draw); gpencil_modifier_subpanel_register( + region_type, "randomize", "Randomize", NULL, random_panel_draw, panel_type); + gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index c193fc49362..ea37558fa7f 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -47,6 +47,8 @@ #include "BKE_screen.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "UI_interface.h" #include "UI_resources.h" @@ -84,6 +86,39 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); } +static float give_opacity_fading_factor(OpacityGpencilModifierData *mmd, + Object *ob_this, + float *pos, + bool apply_obmat) +{ + float factor_depth = 1.0f; + + if (((mmd->flag & GP_OPACITY_FADING) == 0) || ((mmd->object) == NULL)) { + return factor_depth; + } + + float gvert[3]; + if (apply_obmat) { + mul_v3_m4v3(gvert, ob_this->obmat, pos); + } + float dist = len_v3v3(mmd->object->obmat[3], gvert); + float fading_max = MAX2(mmd->fading_start, mmd->fading_end); + float fading_min = MIN2(mmd->fading_start, mmd->fading_end); + + /* Better with ratiof() function from line art. */ + if (dist > fading_max) { + factor_depth = 0.0f; + } + else if (dist <= fading_max && dist > fading_min) { + factor_depth = (fading_max - dist) / (fading_max - fading_min); + } + else { + factor_depth = 1.0f; + } + + return factor_depth; +} + /* opacity strokes */ static void deformStroke(GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), @@ -138,6 +173,9 @@ static void deformStroke(GpencilModifierData *md, factor_curve *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); } + float factor_depth = give_opacity_fading_factor(mmd, ob, &pt->x, true); + factor_curve = interpf(factor_curve, mmd->fading_end_factor, factor_depth); + if (def_nr < 0) { if (mmd->flag & GP_OPACITY_NORMALIZE) { pt->strength = factor_curve; @@ -167,6 +205,10 @@ static void deformStroke(GpencilModifierData *md, /* Fill using opacity factor. */ if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { gps->fill_opacity_fac = mmd->factor; + + float factor_depth = give_opacity_fading_factor(mmd, ob, ob->obmat[3], true); + gps->fill_opacity_fac = interpf(mmd->factor, mmd->fading_end_factor, factor_depth); + CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f); } } @@ -201,6 +243,18 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); + walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP); +} + +static void updateDepsgraph(GpencilModifierData *md, + const ModifierUpdateDepsgraphContext *ctx, + const int UNUSED(mode)) +{ + OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; + if (mmd->object != NULL) { + DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -228,6 +282,20 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_panel_end(layout, ptr); } +static void fading_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE); +} + +static void fading_panel_draw(const bContext *C, Panel *panel) +{ + gpencil_modifier_fading_draw(C, panel); +} + static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) { PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); @@ -266,6 +334,9 @@ static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Opacity, panel_draw); + + gpencil_modifier_subpanel_register( + region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type); PanelType *mask_panel_type = gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); gpencil_modifier_subpanel_register( @@ -289,7 +360,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = { /* initData */ initData, /* freeData */ freeData, /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, + /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ NULL, /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ NULL, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index a13f8d64755..512e3af063a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -43,6 +43,8 @@ #include "BKE_screen.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "UI_interface.h" #include "UI_resources.h" @@ -128,6 +130,30 @@ static void deformStroke(GpencilModifierData *md, } float curvef = 1.0f; + + float factor_depth = 1.0f; + + if (mmd->flag & GP_THICK_FADING) { + if (mmd->object) { + float gvert[3]; + mul_v3_m4v3(gvert, ob->obmat, &pt->x); + float dist = len_v3v3(mmd->object->obmat[3], gvert); + float fading_max = MAX2(mmd->fading_start, mmd->fading_end); + float fading_min = MIN2(mmd->fading_start, mmd->fading_end); + + /* Better with ratiof() function from line art. */ + if (dist > fading_max) { + factor_depth = 0.0f; + } + else if (dist <= fading_max && dist > fading_min) { + factor_depth = (fading_max - dist) / (fading_max - fading_min); + } + else { + factor_depth = 1.0f; + } + } + } + if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) { /* Normalize value to evaluate curve. */ float value = (float)i / (gps->totpoints - 1); @@ -144,6 +170,9 @@ static void deformStroke(GpencilModifierData *md, weight *= curvef; } + float fac_begin = mmd->flag & GP_THICK_NORMALIZE ? 1 : mmd->thickness_fac; + target *= interpf(fac_begin, mmd->fading_end_factor, factor_depth); + pt->pressure = interpf(target, pt->pressure, weight); CLAMP_MIN(pt->pressure, 0.0f); @@ -171,6 +200,32 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md; walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); + walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP); +} + +static void updateDepsgraph(GpencilModifierData *md, + const ModifierUpdateDepsgraphContext *ctx, + const int UNUSED(mode)) +{ + ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md; + if (mmd->object != NULL) { + DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier"); +} + +static void fading_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE); +} + +static void fading_panel_draw(const bContext *C, Panel *panel) +{ + gpencil_modifier_fading_draw(C, panel); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -202,6 +257,8 @@ static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Thick, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type); PanelType *mask_panel_type = gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); gpencil_modifier_subpanel_register(region_type, @@ -229,7 +286,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Thick = { /* initData */ initData, /* freeData */ freeData, /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, + /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ NULL, /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ NULL, diff --git a/source/blender/gpu/GPU_common.h b/source/blender/gpu/GPU_common.h index 1be74701176..c00bf581e8c 100644 --- a/source/blender/gpu/GPU_common.h +++ b/source/blender/gpu/GPU_common.h @@ -24,6 +24,7 @@ #pragma once #define PROGRAM_NO_OPTI 0 +//#define GPU_NO_USE_PY_REFERENCES #if defined(NDEBUG) # define TRUST_NO_ONE 0 diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index af94c1fb0e6..6482d9a9d3e 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -209,6 +209,11 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, void (*callback)(void *userData, int level), void *userData); +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb); +void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref); +#endif + void GPU_framebuffer_push(GPUFrameBuffer *fb); GPUFrameBuffer *GPU_framebuffer_pop(void); uint GPU_framebuffer_stack_level_get(void); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index d9a01663de0..f980c8fdcd7 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -269,6 +269,12 @@ bool GPU_texture_cube(const GPUTexture *tex); bool GPU_texture_depth(const GPUTexture *tex); bool GPU_texture_stencil(const GPUTexture *tex); bool GPU_texture_integer(const GPUTexture *tex); + +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_texture_py_reference_get(GPUTexture *tex); +void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref); +#endif + int GPU_texture_opengl_bindcode(const GPUTexture *tex); void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size); diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index a9d32dcf297..b5a437b46f7 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -24,7 +24,7 @@ * Use these instead of glGenBuffers & its friends * - alloc must be called from a thread that is bound * to the context that will be used for drawing with - * this vao. + * this VAO. * - free can be called from any thread */ diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index 63e7024b74b..2d9fb7822ae 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -86,8 +86,8 @@ bool GPU_debug_group_match(const char *ref) if (ctx == nullptr) { return false; } - DebugStack &stack = ctx->debug_stack; - for (StringRef &name : stack) { + const DebugStack &stack = ctx->debug_stack; + for (const StringRef &name : stack) { if (name == ref) { return true; } diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 1e3cf479462..1293cc0953d 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -71,6 +71,12 @@ FrameBuffer::~FrameBuffer() reinterpret_cast<Texture *>(attachment.tex)->detach_from(this); } } + +#ifndef GPU_NO_USE_PY_REFERENCES + if (this->py_ref) { + *this->py_ref = nullptr; + } +#endif } /** \} */ @@ -473,6 +479,19 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb, unwrap(gpu_fb)->recursive_downsample(max_lvl, callback, userData); } +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb) +{ + return unwrap(gpu_fb)->py_ref; +} + +void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref) +{ + BLI_assert(py_ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr); + unwrap(gpu_fb)->py_ref = py_ref; +} +#endif + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh index d63d72cf4f7..2b00c239af4 100644 --- a/source/blender/gpu/intern/gpu_framebuffer_private.hh +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -100,6 +100,20 @@ class FrameBuffer { bool scissor_test_ = false; bool dirty_state_ = true; +#ifndef GPU_NO_USE_PY_REFERENCES + public: + /** + * Reference of a pointer that needs to be cleaned when deallocating the frame-buffer. + * Points to #BPyGPUFrameBuffer.fb + */ + void **py_ref = nullptr; +#endif + + public: + /* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer. + * Points to #BPyGPUFrameBuffer::fb */ + void **ref = nullptr; + public: FrameBuffer(const char *name); virtual ~FrameBuffer(); diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 2ae50d913da..569b51a407a 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -543,7 +543,7 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, &precalc->dims.zmax); if (isinf(precalc->dims.zmax)) { /* We cannot retrieve the actual value of the clip_end. - * Use `FLT_MAX` to avoid nans. */ + * Use `FLT_MAX` to avoid NAN's. */ precalc->dims.zmax = FLT_MAX; } for (int i = 0; i < 4; i++) { diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 75fe7652715..aea27756708 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -723,7 +723,7 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons static int g_shader_builtin_srgb_transform = 0; static bool g_shader_builtin_srgb_is_dirty = false; -static bool gpu_shader_srgb_uniform_dirty_get(void) +static bool gpu_shader_srgb_uniform_dirty_get() { return g_shader_builtin_srgb_is_dirty; } diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index 782d25747bb..5c33066c720 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -248,7 +248,7 @@ eGPUWriteMask GPU_write_mask_get() uint GPU_stencil_mask_get() { - GPUStateMutable &state = Context::get()->state_manager->mutable_state; + const GPUStateMutable &state = Context::get()->state_manager->mutable_state; return state.stencil_write_mask; } @@ -267,7 +267,7 @@ eGPUStencilTest GPU_stencil_test_get() /* NOTE: Already premultiplied by U.pixelsize. */ float GPU_line_width_get() { - GPUStateMutable &state = Context::get()->state_manager->mutable_state; + const GPUStateMutable &state = Context::get()->state_manager->mutable_state; return state.line_width; } @@ -292,7 +292,7 @@ void GPU_viewport_size_get_i(int coords[4]) bool GPU_depth_mask_get() { - GPUState &state = Context::get()->state_manager->state; + const GPUState &state = Context::get()->state_manager->state; return (state.write_mask & GPU_WRITE_DEPTH) != 0; } diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index c3e9058c6c7..997064e82a2 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -60,6 +60,12 @@ Texture::~Texture() fb_[i]->attachment_remove(fb_attachment_[i]); } } + +#ifndef GPU_NO_USE_PY_REFERENCES + if (this->py_ref) { + *this->py_ref = nullptr; + } +#endif } bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) @@ -581,6 +587,19 @@ bool GPU_texture_array(const GPUTexture *tex) return (reinterpret_cast<const Texture *>(tex)->type_get() & GPU_TEXTURE_ARRAY) != 0; } +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_texture_py_reference_get(GPUTexture *tex) +{ + return unwrap(tex)->py_ref; +} + +void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref) +{ + BLI_assert(py_ref == nullptr || unwrap(tex)->py_ref == nullptr); + unwrap(tex)->py_ref = py_ref; +} +#endif + /* TODO remove */ int GPU_texture_opengl_bindcode(const GPUTexture *tex) { diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 3d808bce152..a8f2e482bdd 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -80,6 +80,13 @@ class Texture { int refcount = 1; /** Width & Height (of source data), optional. */ int src_w = 0, src_h = 0; +#ifndef GPU_NO_USE_PY_REFERENCES + /** + * Reference of a pointer that needs to be cleaned when deallocating the texture. + * Points to #BPyGPUTexture.tex + */ + void **py_ref = nullptr; +#endif protected: /* ---- Texture format (immutable after init). ---- */ diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc index 976a31ab5cb..fc1d1006d0d 100644 --- a/source/blender/gpu/opengl/gl_batch.cc +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -44,7 +44,7 @@ using namespace blender::gpu; /* -------------------------------------------------------------------- */ -/** \name Vao cache +/** \name VAO Cache * * Each #GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration. * TODO(fclem): Could be revisited to avoid so much cross references. diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh index 444ae1c0ab1..704b6471dd5 100644 --- a/source/blender/gpu/opengl/gl_batch.hh +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -43,7 +43,7 @@ class GLShaderInterface; #define GPU_VAO_STATIC_LEN 3 -/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) +/* VAO management: remembers all geometry state (vertex attribute bindings & element buffer) * for each shader interface. Start with a static number of vaos and fallback to dynamic count * if necessary. Once a batch goes dynamic it does not go back. */ class GLVaoCache { diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh index d0bf0fcdf1f..b42f439f9ec 100644 --- a/source/blender/gpu/opengl/gl_immediate.hh +++ b/source/blender/gpu/opengl/gl_immediate.hh @@ -38,7 +38,7 @@ namespace blender::gpu { class GLImmediate : public Immediate { private: - /* Use two buffers for strict and unstrict vertex count to + /* Use two buffers for strict and non-strict vertex count to * avoid some huge driver slowdown (see T70922). * Use accessor functions to get / modify. */ struct { diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 96cd1fb61a4..432b62d172a 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -817,7 +817,7 @@ static void ffmpeg_postprocess(struct anim *anim) # if defined(__x86_64__) || defined(_M_X64) /* Scale and flip image over Y axis in one go, using negative strides. - * This doesn't work with arm/powerpc though and may be misusing the API. + * This doesn't work with ARM/PowerPC though and may be misusing the API. * Limit it x86_64 where it appears to work. * http://trac.ffmpeg.org/ticket/9060 */ int *dstStride = anim->pFrameRGB->linesize; diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 001cd4e1575..12f7553d5e4 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -374,7 +374,7 @@ static void scanline_separate_32bit(float *rectf, const float *fbuf, int scanlin static void imb_read_tiff_resolution(ImBuf *ibuf, TIFF *image) { - uint16 unit; + uint16_t unit; float xres; float yres; @@ -569,7 +569,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem, TIFF *image = NULL; ImBuf *ibuf = NULL, *hbuf; ImbTIFFMemFile memFile; - uint32 width, height; + uint32_t width, height; char *format = NULL; int level; short spp; @@ -690,7 +690,7 @@ void imb_loadtiletiff( ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect) { TIFF *image = NULL; - uint32 width, height; + uint32_t width, height; ImbTIFFMemFile memFile; image = imb_tiff_client_open(&memFile, mem, size); @@ -761,7 +761,7 @@ void imb_loadtiletiff( bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) { TIFF *image = NULL; - uint16 samplesperpixel, bitspersample; + uint16_t samplesperpixel, bitspersample; size_t npixels; unsigned char *pixels = NULL; unsigned char *from = NULL, *to = NULL; @@ -774,7 +774,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) /* check for a valid number of bytes per pixel. Like the PNG writer, * the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding * to gray, RGB, RGBA respectively. */ - samplesperpixel = (uint16)((ibuf->planes + 7) >> 3); + samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3); if ((samplesperpixel > 4) || (samplesperpixel == 2)) { fprintf(stderr, "imb_savetiff: unsupported number of bytes per " diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h index 6db32e9f40a..886b6402267 100644 --- a/source/blender/io/gpencil/nanosvg/nanosvg.h +++ b/source/blender/io/gpencil/nanosvg/nanosvg.h @@ -1289,7 +1289,7 @@ static const char *nsvg__parseNumber(const char *s, char *it, const int size) return s; } -static const char *nsvg__getNextPathItem(const char *s, char *it) +static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs) { it[0] = '\0'; // Skip white spaces and commas @@ -1297,6 +1297,15 @@ static const char *nsvg__getNextPathItem(const char *s, char *it) s++; if (!*s) return s; + + /* Blender: Special case for arc command's 4th and 5th arguments. */ + if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) { + it[0] = s[0]; + it[1] = '\0'; + s++; + return s; + } + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { s = nsvg__parseNumber(s, it, 64); } @@ -1576,8 +1585,8 @@ static int nsvg__isCoordinate(const char *s) // optional sign if (*s == '-' || *s == '+') s++; - // must have at least one digit - return nsvg__isdigit(*s); + // must have at least one digit, or start by a dot + return (nsvg__isdigit(*s) || *s == '.'); } static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str) @@ -2413,7 +2422,7 @@ static void nsvg__parsePath(NSVGparser *p, const char **attr) nargs = 0; while (*s) { - s = nsvg__getNextPathItem(s, item); + s = nsvg__getNextPathItem(s, item, cmd, nargs); if (!*item) break; if (cmd != '\0' && nsvg__isCoordinate(item)) { @@ -2740,7 +2749,7 @@ static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag) s = attr[i + 1]; nargs = 0; while (*s) { - s = nsvg__getNextPathItem(s, item); + s = nsvg__getNextPathItem(s, item, '\0', nargs); args[nargs++] = (float)nsvg__atof(item); if (nargs >= 2) { if (npts == 0) diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index a11e7d77c67..986c009ac26 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -133,7 +133,8 @@ typedef struct BrushGpencilSettings { /** Factor to extend stroke extremes using fill tool. */ float fill_extend_fac; - char _pad3[4]; + /** Number of pixels to dilate fill area. */ + int dilate_pixels; struct CurveMapping *curve_sensitivity; struct CurveMapping *curve_strength; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index ea03789ddab..b816a45426b 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -184,6 +184,8 @@ .layer_pass = 0, \ .hardeness = 1.0f, \ .curve_intensity = NULL, \ + .fading_end = 10.0f, \ + .fading_end_factor = 0.2f, \ } #define _DNA_DEFAULT_SimplifyGpencilModifierData \ @@ -251,6 +253,8 @@ .thickness_fac = 1.0f, \ .thickness = 30, \ .layer_pass = 0, \ + .fading_end = 10.0f, \ + .fading_end_factor = 0.2f, \ } #define _DNA_DEFAULT_TimeGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 8d7b3896ef9..c03b358dc8a 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -187,7 +187,12 @@ typedef struct ThickGpencilModifierData { int thickness; /** Custom index for passes. */ int layer_pass; - char _pad[4]; + /** Start/end distances of the fading effect. */ + float fading_start; + float fading_end; + float fading_end_factor; + /** Fading reference object */ + struct Object *object; struct CurveMapping *curve_thickness; } ThickGpencilModifierData; @@ -199,6 +204,7 @@ typedef enum eThickGpencil_Flag { GP_THICK_NORMALIZE = (1 << 4), GP_THICK_INVERT_LAYERPASS = (1 << 5), GP_THICK_INVERT_MATERIAL = (1 << 6), + GP_THICK_FADING = (1 << 7), } eThickGpencil_Flag; typedef struct TimeGpencilModifierData { @@ -291,9 +297,16 @@ typedef struct OpacityGpencilModifierData { int flag; /** Main Opacity factor. */ float factor; + /** Fading controlling object */ + int _pad0; + struct Object *object; + /** Start/end distances of the fading effect. */ + float fading_start; + float fading_end; + float fading_end_factor; /** Modify stroke, fill or both. */ char modify_color; - char _pad[3]; + char _pad1[3]; /** Custom index for passes. */ int layer_pass; @@ -309,6 +322,7 @@ typedef enum eOpacityGpencil_Flag { GP_OPACITY_INVERT_MATERIAL = (1 << 5), GP_OPACITY_CUSTOM_CURVE = (1 << 6), GP_OPACITY_NORMALIZE = (1 << 7), + GP_OPACITY_FADING = (1 << 8), } eOpacityGpencil_Flag; typedef struct ArrayGpencilModifierData { @@ -616,6 +630,14 @@ typedef struct OffsetGpencilModifierData { float loc[3]; float rot[3]; float scale[3]; + /** Random Offset. */ + float rnd_offset[3]; + /** Random Rotation. */ + float rnd_rot[3]; + /** Random Scales. */ + float rnd_scale[3]; + /** (first element is the index) random values. */ + int seed; /** Custom index for passes. */ int layer_pass; } OffsetGpencilModifierData; @@ -626,6 +648,7 @@ typedef enum eOffsetGpencil_Flag { GP_OFFSET_INVERT_VGROUP = (1 << 2), GP_OFFSET_INVERT_LAYERPASS = (1 << 3), GP_OFFSET_INVERT_MATERIAL = (1 << 4), + GP_OFFSET_UNIFORM_RANDOM_SCALE = (1 << 5), } eOffsetGpencil_Flag; typedef struct SmoothGpencilModifierData { diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 8facdca2f9c..bf7b9d70fb6 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -47,7 +47,7 @@ struct Curve; #define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2 #define GPENCIL_MIN_FILL_FAC 0.05f -#define GPENCIL_MAX_FILL_FAC 5.0f +#define GPENCIL_MAX_FILL_FAC 8.0f /* ***************************************** */ /* GP Stroke Points */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 282d71f6a87..47f2c221279 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1280,10 +1280,9 @@ typedef struct NodeAttributeSeparateXYZ { typedef struct NodeAttributeConvert { /* CustomDataType. */ - uint8_t data_type; - char _pad[1]; + int8_t data_type; /* AttributeDomain. */ - int16_t domain; + int8_t domain; } NodeAttributeConvert; typedef struct NodeGeometryMeshCircle { @@ -1308,6 +1307,18 @@ typedef struct NodeGeometryMeshLine { uint8_t count_mode; } NodeGeometryMeshLine; +typedef struct NodeSwitch { + /* NodeSwitch. */ + uint8_t input_type; +} NodeSwitch; + +typedef struct NodeGeometryAttributeTransfer { + /* AttributeDomain. */ + int8_t domain; + /* GeometryNodeAttributeTransferMapMode. */ + uint8_t mapping; +} NodeGeometryAttributeTransfer; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1802,6 +1813,11 @@ typedef enum GeometryNodeMeshLineCountMode { GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1, } GeometryNodeMeshLineCountMode; +typedef enum GeometryNodeAttributeTransferMapMode { + GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, + GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, +} GeometryNodeAttributeTransferMapMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index ce3875dfbc7..9f3576a2cbe 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1909,7 +1909,7 @@ typedef struct SpaceSpreadsheet { /** * List of #SpreadsheetContext. * This is a path to the data that is displayed in the spreadsheet. - * It can be set explicitely by an action of the user (e.g. clicking the preview icon in a + * It can be set explicitly by an action of the user (e.g. clicking the preview icon in a * geometry node) or it can be derived from context automatically based on some heuristic. */ ListBase context_path; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index b0895609e9a..71af69f77a1 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -237,6 +237,7 @@ extern const EnumPropertyItem rna_enum_curveprofile_preset_items[]; extern const EnumPropertyItem rna_enum_preference_section_items[]; extern const EnumPropertyItem rna_enum_attribute_type_items[]; +extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[]; extern const EnumPropertyItem rna_enum_attribute_domain_items[]; extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[]; extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 657af98f5fe..4fe19493614 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -148,6 +148,8 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items # include "DEG_depsgraph_build.h" # include "DEG_depsgraph_query.h" +# include "ED_asset.h" + # include "WM_api.h" void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value) @@ -575,6 +577,22 @@ static ID *rna_ID_copy(ID *id, Main *bmain) return newid; } +static void rna_ID_asset_mark(ID *id, bContext *C) +{ + if (ED_asset_mark_id(C, id)) { + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL); + } +} + +static void rna_ID_asset_clear(ID *id) +{ + if (ED_asset_clear_id(id)) { + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL); + } +} + static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) { if (!ID_IS_OVERRIDABLE_LIBRARY(id)) { @@ -1716,6 +1734,18 @@ static void rna_def_ID(BlenderRNA *brna) parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "asset_mark", "rna_ID_asset_mark"); + RNA_def_function_ui_description( + func, + "Enable easier reuse of the data-block through the Asset Browser, with the help of " + "customizable metadata (like previews, descriptions and tags)"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + + func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear"); + RNA_def_function_ui_description( + func, + "Delete all asset metadata and turn the asset data-block back into a normal data-block"); + func = RNA_def_function(srna, "override_create", "rna_ID_override_create"); RNA_def_function_ui_description(func, "Create an overridden local copy of this linked data-block (not " diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 7976df3e4e4..a256002ffc1 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -49,6 +49,19 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { + {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""}, + {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, + {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"}, + {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point precisions"}, + {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"}, + {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, + {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, + {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_attribute_domain_items[] = { /* Not implement yet */ // {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"}, @@ -58,7 +71,7 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = { {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"}, /* Not implement yet */ // {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"}, - {ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"}, {0, NULL, 0, NULL, NULL}, }; @@ -68,6 +81,7 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"}, {ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"}, {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index e7daa55af6c..044e6ca2da1 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1617,6 +1617,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna) prop, "Stroke Extension", "Strokes end extension for closing gaps, use zero to disable"); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + /* Number of pixels to dilate fill area. */ + prop = RNA_def_property(srna, "dilate_pixels", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "dilate_pixels"); + RNA_def_property_range(prop, 0, 20); + RNA_def_property_int_default(prop, 1); + RNA_def_property_ui_text(prop, "Dilate", "Number of pixels to dilate fill area"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + /* Flags */ prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index d4697721bc2..1b89d866aba 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1160,6 +1160,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna) /* define common props */ prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Additive", "Values generated by this modifier are applied on top of " @@ -1168,12 +1169,14 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna) prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, generator_mode_items); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Mode", "Type of generator to use"); RNA_def_property_update( prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_verify_data_update"); /* order of the polynomial */ prop = RNA_def_property(srna, "poly_order", PROP_INT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Polynomial Order", @@ -1184,6 +1187,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna) /* coefficients array */ prop = RNA_def_property(srna, "coefficients", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_array(prop, 32); RNA_def_property_flag(prop, PROP_DYNAMIC); RNA_def_property_dynamic_array_funcs(prop, "rna_FModifierGenerator_coefficients_get_length"); @@ -1221,25 +1225,30 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna) /* coefficients */ prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Amplitude", "Scale factor determining the maximum/minimum values"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "phase_multiplier", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Phase Multiple", "Scale factor determining the 'speed' of the function"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "phase_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Phase Offset", "Constant factor to offset time by for function"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "value_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Value Offset", "Constant factor to offset values by"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* flags */ prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE); RNA_def_property_ui_text(prop, "Additive", @@ -1248,6 +1257,7 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna) RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "function_type", PROP_ENUM, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Type", "Type of built-in function to use"); @@ -1271,17 +1281,20 @@ static void rna_def_fmodifier_envelope_ctrl(BlenderRNA *brna) */ prop = RNA_def_property(srna, "min", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "min"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Minimum Value", "Lower bound of envelope at this control-point"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "max", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "max"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Maximum Value", "Upper bound of envelope at this control-point"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* Frame */ prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "time"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Frame", "Frame this control-point occurs on"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); @@ -1340,6 +1353,7 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna) /* Collections */ prop = RNA_def_property(srna, "control_points", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "data", "totvert"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_struct_type(prop, "FModifierEnvelopeControlPoint"); RNA_def_property_ui_text( prop, "Control Points", "Control points defining the shape of the envelope"); @@ -1348,18 +1362,21 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna) /* Range Settings */ prop = RNA_def_property(srna, "reference_value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "midval"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Reference Value", "Value that envelope's influence is centered around / based on"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "default_min", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "min"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Default Minimum", "Lower distance from Reference Value for 1:1 default influence"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "default_max", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "max"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Default Maximum", "Upper distance from Reference Value for 1:1 default influence"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); @@ -1395,12 +1412,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna) /* before */ prop = RNA_def_property(srna, "mode_before", PROP_ENUM, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_enum_sdna(prop, NULL, "before_mode"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Before Mode", "Cycling mode to use before first keyframe"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "cycles_before", PROP_INT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_int_sdna(prop, NULL, "before_cycles"); RNA_def_property_ui_text( prop, @@ -1410,12 +1429,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna) /* after */ prop = RNA_def_property(srna, "mode_after", PROP_ENUM, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_enum_sdna(prop, NULL, "after_mode"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "After Mode", "Cycling mode to use after last keyframe"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "cycles_after", PROP_INT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_int_sdna(prop, NULL, "after_cycles"); RNA_def_property_ui_text(prop, "After Cycles", @@ -1450,26 +1471,31 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "use_min_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMIN); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Minimum X", "Use the minimum X value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_min_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMIN); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Minimum Y", "Use the minimum Y value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_max_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Maximum X", "Use the maximum X value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_max_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Maximum Y", "Use the maximum Y value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "min_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmin"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_minx_set", "rna_FModifierLimits_minx_range"); RNA_def_property_ui_text(prop, "Minimum X", "Lowest X value to allow"); @@ -1477,6 +1503,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "min_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymin"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_miny_set", "rna_FModifierLimits_miny_range"); RNA_def_property_ui_text(prop, "Minimum Y", "Lowest Y value to allow"); @@ -1484,6 +1511,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "max_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmax"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_maxx_set", "rna_FModifierLimits_maxx_range"); RNA_def_property_ui_text(prop, "Maximum X", "Highest X value to allow"); @@ -1491,6 +1519,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "max_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymax"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_maxy_set", "rna_FModifierLimits_maxy_range"); RNA_def_property_ui_text(prop, "Maximum Y", "Highest Y value to allow"); @@ -1519,16 +1548,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna) prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "modification"); RNA_def_property_enum_items(prop, prop_modification_items); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Blend Type", "Method of modifying the existing F-Curve"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "size"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Scale", "Scaling (in time) of the noise"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "strength"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Strength", @@ -1537,16 +1569,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna) prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "phase"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Phase", "A random seed for the noise effect"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "offset"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Offset", "Time offset for the noise effect"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "depth", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "depth"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Depth", "Amount of fine level detail present in the noise"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } @@ -1569,11 +1604,13 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) /* properties */ prop = RNA_def_property(srna, "frame_step", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "step_size"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Step Size", "Number of frames to hold each value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "offset"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Offset", "Reference number of frames before frames get held " @@ -1582,18 +1619,21 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) prop = RNA_def_property(srna, "use_frame_start", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_BEFORE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Use Start Frame", "Restrict modifier to only act after its 'start' frame"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_frame_end", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_AFTER); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Use End Frame", "Restrict modifier to only act before its 'end' frame"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_frame"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs(prop, NULL, "rna_FModifierStepped_frame_start_set", @@ -1604,6 +1644,7 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_frame"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierStepped_frame_end_set", "rna_FModifierStepped_end_frame_range"); RNA_def_property_ui_text( @@ -1641,12 +1682,14 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0); + // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_show_expanded_set"); RNA_def_property_ui_text(prop, "Expanded", "F-Curve Modifier's panel is expanded in UI"); RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1); prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_MUTED); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Enabled", "Enable F-Curve modifier evaluation"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1); @@ -1654,6 +1697,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FMODIFIER_FLAG_DISABLED); + // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Disabled", "F-Curve Modifier has invalid settings and will not be evaluated"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); @@ -1661,6 +1705,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) /* TODO: setting this to true must ensure that all others in stack are turned off too... */ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_ACTIVE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Active", "F-Curve modifier will show settings in the editor"); RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_active_set"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_active_update"); @@ -1669,6 +1714,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) /* restricted range */ prop = RNA_def_property(srna, "use_restricted_range", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_RANGERESTRICT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Restrict Frame Range", @@ -1678,6 +1724,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "sfra"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifier_start_frame_set", "rna_FModifier_start_frame_range"); RNA_def_property_ui_text( @@ -1688,6 +1735,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "efra"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifier_end_frame_set", "rna_FModifier_end_frame_range"); RNA_def_property_ui_text( @@ -1698,6 +1746,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendin"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range"); RNA_def_property_ui_text( prop, "Blend In", "Number of frames from start frame for influence to take effect"); @@ -1705,6 +1754,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendout"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range"); RNA_def_property_ui_text( prop, "Blend Out", "Number of frames from end frame for influence to fade out"); @@ -1713,12 +1763,14 @@ static void rna_def_fmodifier(BlenderRNA *brna) /* influence */ prop = RNA_def_property(srna, "use_influence", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_USEINFLUENCE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Use Influence", "F-Curve Modifier's effects will be tempered by a default factor"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "influence"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_float_default(prop, 1.0f); RNA_def_property_ui_text( @@ -2162,6 +2214,7 @@ static void rna_def_fcurve_modifiers(BlenderRNA *brna, PropertyRNA *cprop) /* Collection active property */ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "FModifier"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_pointer_funcs( prop, "rna_FCurve_active_modifier_get", "rna_FCurve_active_modifier_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 1fbbffa99d5..5b3fa1f6aa2 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -2571,7 +2571,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Auto-Lock Layers", - "Lock automatically all layers except active one to avoid accidental changes"); + "Automatically lock all layers except the active one to avoid accidental changes"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_autolock"); prop = RNA_def_property(srna, "edit_line_color", PROP_FLOAT, PROP_COLOR_GAMMA); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index b118adea14f..2b4391fb822 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -351,6 +351,8 @@ static void greasepencil_modifier_object_set(Object *self, RNA_GP_MOD_OBJECT_SET(Armature, object, OB_ARMATURE); RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY); +RNA_GP_MOD_OBJECT_SET(Opacity, object, OB_EMPTY); +RNA_GP_MOD_OBJECT_SET(Thick, object, OB_EMPTY); # undef RNA_GP_MOD_OBJECT_SET @@ -1116,6 +1118,38 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Thickness Factor", "Factor to multiply the thickness with"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_FADING); + RNA_def_property_ui_text(prop, "Fading", "Fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Distance reference object */ + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Object used as distance reference"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_ThickGpencilModifier_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_start"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end_factor"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3); + RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); @@ -1256,6 +1290,34 @@ static void rna_def_modifier_gpenciloffset(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Scale", "Values for changes in scale"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "random_offset", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "rnd_offset"); + RNA_def_property_ui_text(prop, "Random Offset", "Value for changes in location"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, NULL, "rnd_rot"); + RNA_def_property_ui_text(prop, "Random Rotation", "Value for changes in rotation"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "random_scale", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "rnd_scale"); + RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "Seed", "Random seed"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OFFSET_UNIFORM_RANDOM_SCALE); + RNA_def_property_ui_text( + prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpenciltint(BlenderRNA *brna) @@ -1625,6 +1687,38 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Hardness", "Factor of stroke hardness"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_FADING); + RNA_def_property_ui_text(prop, "Fading", "Fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Distance reference object */ + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Object used as distance reference"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_OpacityGpencilModifier_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_start"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end_factor"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3); + RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index c9520c939f4..f8fa2aab5e7 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -402,7 +402,7 @@ static void rna_def_movieclip(BlenderRNA *brna) "Start Frame", "Global scene frame number at which this movie starts playing " "(affects all data associated with a clip)"); - RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); /* frame_offset */ prop = RNA_def_property(srna, "frame_offset", PROP_INT, PROP_NONE); @@ -412,7 +412,7 @@ static void rna_def_movieclip(BlenderRNA *brna) "Frame Offset", "Offset of footage first frame relative to its file name " "(affects only how footage is loading, does not change data associated with a clip)"); - RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); /* length */ prop = RNA_def_property(srna, "frame_duration", PROP_INT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b0254ce2ef3..128c65aecd9 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -65,6 +65,20 @@ const EnumPropertyItem rna_enum_node_socket_in_out_items[] = { {SOCK_IN, "IN", 0, "Input", ""}, {SOCK_OUT, "OUT", 0, "Output", ""}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem node_socket_data_type_items[] = { + {SOCK_FLOAT, "FLOAT", 0, "Float", ""}, + {SOCK_INT, "INT", 0, "Integer", ""}, + {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""}, + {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, + {SOCK_STRING, "STRING", 0, "String", ""}, + {SOCK_RGBA, "RGBA", 0, "Color", ""}, + {SOCK_OBJECT, "OBJECT", 0, "Object", ""}, + {SOCK_IMAGE, "IMAGE", 0, "Image", ""}, + {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""}, + {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = { {SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""}, @@ -78,7 +92,7 @@ static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = { static const EnumPropertyItem node_socket_type_items[] = { {SOCK_CUSTOM, "CUSTOM", 0, "Custom", ""}, {SOCK_FLOAT, "VALUE", 0, "Value", ""}, - {SOCK_INT, "INT", 0, "Int", ""}, + {SOCK_INT, "INT", 0, "Integer", ""}, {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""}, {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, {SOCK_STRING, "STRING", 0, "String", ""}, @@ -957,6 +971,32 @@ static void rna_NodeTree_get_from_context( RNA_parameter_list_free(&list); } +static bool rna_NodeTree_valid_socket_type(eNodeSocketDatatype socket_type, + bNodeTreeType *ntreetype) +{ + extern FunctionRNA rna_NodeTree_valid_socket_type_func; + + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + void *ret; + bool valid; + + RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */ + func = &rna_NodeTree_valid_socket_type_func; + + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "type", &socket_type); + ntreetype->rna_ext.call(NULL, &ptr, func, &list); + + RNA_parameter_get_lookup(&list, "valid", &ret); + valid = *(bool *)ret; + + RNA_parameter_list_free(&list); + + return valid; +} + static void rna_NodeTree_unregister(Main *UNUSED(bmain), StructRNA *type) { bNodeTreeType *nt = RNA_struct_blender_type_get(type); @@ -985,7 +1025,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain, bNodeTreeType *nt, dummynt; bNodeTree dummyntree; PointerRNA dummyptr; - int have_function[3]; + int have_function[4]; /* setup dummy tree & tree type to store static properties in */ memset(&dummynt, 0, sizeof(bNodeTreeType)); @@ -1031,6 +1071,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain, nt->poll = (have_function[0]) ? rna_NodeTree_poll : NULL; nt->update = (have_function[1]) ? rna_NodeTree_update_reg : NULL; nt->get_from_context = (have_function[2]) ? rna_NodeTree_get_from_context : NULL; + nt->valid_socket_type = (have_function[3]) ? rna_NodeTree_valid_socket_type : NULL; ntreeTypeAdd(nt); @@ -1913,6 +1954,29 @@ static const EnumPropertyItem *itemf_function_check( return item_array; } +static bool switch_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, + SOCK_FLOAT, + SOCK_INT, + SOCK_BOOLEAN, + SOCK_VECTOR, + SOCK_STRING, + SOCK_RGBA, + SOCK_GEOMETRY, + SOCK_OBJECT, + SOCK_COLLECTION); +} + +static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + *r_free = true; + return itemf_function_check(node_socket_data_type_items, switch_type_supported); +} + static bool attribute_clamp_type_supported(const EnumPropertyItem *item) { return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR); @@ -1983,6 +2047,7 @@ static void rna_GeometryNodeAttributeRandomize_data_type_update(Main *bmain, static bool attribute_convert_type_supported(const EnumPropertyItem *item) { return ELEM(item->value, + CD_AUTO_FROM_NAME, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, @@ -1994,7 +2059,8 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeConvert_type_itemf( bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { *r_free = true; - return itemf_function_check(rna_enum_attribute_type_items, attribute_convert_type_supported); + return itemf_function_check(rna_enum_attribute_type_with_auto_items, + attribute_convert_type_supported); } static bool attribute_fill_type_supported(const EnumPropertyItem *item) @@ -8953,9 +9019,9 @@ static void def_geo_attribute_convert(StructRNA *srna) RNA_def_struct_sdna_from(srna, "NodeAttributeConvert", "storage"); prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_with_auto_items); RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeConvert_type_itemf"); - RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_enum_default(prop, CD_AUTO_FROM_NAME); RNA_def_property_ui_text(prop, "Data Type", "The data type to save the result attribute with"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -9630,6 +9696,53 @@ static void def_geo_mesh_line(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_switch(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeSwitch", "storage"); + prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "input_type"); + RNA_def_property_enum_items(prop, node_socket_data_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeSwitch_type_itemf"); + RNA_def_property_ui_text(prop, "Input Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_attribute_transfer(StructRNA *srna) +{ + static EnumPropertyItem mapping_items[] = { + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, + "NEAREST_FACE_INTERPOLATED", + 0, + "Nearest Face Interpolated", + "Transfer the attribute from the nearest face on a surface (loose points and edges are " + "ignored)"}, + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST, + "NEAREST", + 0, + "Nearest", + "Transfer the element from the nearest element (using face and edge centers for the " + "distance computation)"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeTransfer", "storage"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO); + RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mapping_items); + RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) @@ -11330,6 +11443,14 @@ static void rna_def_nodetree(BlenderRNA *brna) parm = RNA_def_pointer( func, "result_3", "ID", "From ID", "Original ID data-block selected from the context"); RNA_def_function_output(func, parm); + + /* Check for support of a socket type. */ + func = RNA_def_function(srna, "valid_socket_type", NULL); + RNA_def_function_ui_description(func, "Check if the socket type is valid for the node tree"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL); + parm = RNA_def_enum(func, "type", node_socket_type_items, 0, "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + RNA_def_function_return(func, RNA_def_boolean(func, "valid", false, "", "")); } static void rna_def_composite_nodetree(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 6b93a1c223c..3edfd5c44f4 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -84,7 +84,7 @@ const EnumPropertyItem rna_enum_object_mode_items[] = { {OB_MODE_PAINT_GPENCIL, "PAINT_GPENCIL", ICON_GREASEPENCIL, - "Draw", + "Draw Mode", "Paint Grease Pencil Strokes"}, {OB_MODE_WEIGHT_GPENCIL, "WEIGHT_GPENCIL", diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 24d051fecc8..f411566b623 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -990,18 +990,14 @@ static int colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt) { SequenceSearchData *data = arg_pt; - if (seq->modifiers.first) { - SequenceModifierData *smd = seq->modifiers.first; - - for (smd = seq->modifiers.first; smd; smd = smd->next) { - if (smd->type == seqModifierType_ColorBalance) { - ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd; - - if (&cbmd->color_balance == data->data) { - data->seq = seq; - data->smd = smd; - return -1; /* done so bail out */ - } + for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) { + if (smd->type == seqModifierType_ColorBalance) { + ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd; + + if (&cbmd->color_balance == data->data) { + data->seq = seq; + data->smd = smd; + return -1; /* done so bail out */ } } } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 2a513691762..aa07ff4006a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3046,6 +3046,10 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) { sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; } + if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE && + !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } } const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C), @@ -3091,6 +3095,11 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UN continue; } } + if (component_type == GEO_COMPONENT_TYPE_CURVE) { + if (!ELEM(item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + continue; + } + } if (item->value == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH) { RNA_enum_item_add(&item_array, &items_len, &mesh_vertex_domain_item); } @@ -7485,6 +7494,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) ICON_POINTCLOUD_DATA, "Point Cloud", "Point cloud component containing only point data"}, + {GEO_COMPONENT_TYPE_CURVE, + "CURVE", + ICON_CURVE_DATA, + "Curve", + "Curve component containing spline and control point data"}, {GEO_COMPONENT_TYPE_INSTANCES, "INSTANCES", ICON_EMPTY_AXIS, diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 26887b51f81..bacd3943141 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6160,7 +6160,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) prop, "Python Scripts Directory", "Alternate script path, matching the default layout with subdirectories: " - "startup, add-ons and modules (requires restart)"); + "startup, addons, modules, and presets (requires restart)"); /* TODO, editing should reset sys.path! */ prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 54caaed9231..6ac2629c006 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -79,6 +79,7 @@ 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 @@ -118,6 +119,7 @@ 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_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 6769f14f88f..649d36e3d57 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -296,7 +296,6 @@ ModifierTypeInfo modifierType_Armature = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 0282f0d2934..93a9e76ffe4 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -1021,7 +1021,6 @@ ModifierTypeInfo modifierType_Array = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index a94411d897e..8fdd222402e 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -448,7 +448,6 @@ ModifierTypeInfo modifierType_Bevel = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, /* freeData */ freeData, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 40d5386c2fa..9b8782737c3 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_Boolean = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 0b1c661baed..c38e5126f6b 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -347,7 +347,6 @@ ModifierTypeInfo modifierType_Build = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index f905a38ae12..715bc26e5d3 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -591,7 +591,6 @@ ModifierTypeInfo modifierType_Cast = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index a25d65347c5..40d027f3044 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -311,7 +311,6 @@ ModifierTypeInfo modifierType_Cloth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index e72e0279263..5dd57469914 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -317,7 +317,6 @@ ModifierTypeInfo modifierType_Collision = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 001c7d8d098..fef235b456b 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -852,7 +852,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index d5d53edfd54..20dbb299767 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -236,7 +236,6 @@ ModifierTypeInfo modifierType_Curve = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index 8b299a82f95..dbdc76f0edc 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -495,7 +495,6 @@ ModifierTypeInfo modifierType_DataTransfer = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 03db09a5aec..faad1175f3a 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -300,7 +300,6 @@ ModifierTypeInfo modifierType_Decimate = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index abe78943508..a7ac9f618af 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -507,7 +507,6 @@ ModifierTypeInfo modifierType_Displace = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 3e607e88cdd..8b1d541d45d 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_DynamicPaint = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index e02befd7efa..2874bebe13a 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -187,7 +187,6 @@ ModifierTypeInfo modifierType_EdgeSplit = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index c12019a325e..e1197439c7c 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -1255,7 +1255,6 @@ ModifierTypeInfo modifierType_Explode = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index 8a8d6f2305f..36d2ab2a11a 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Fluid = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 2fa05a319d5..ff581e92cdd 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -574,7 +574,6 @@ ModifierTypeInfo modifierType_Hook = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index bda0f9ba5a4..6efeec1970f 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -889,7 +889,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index fc527304e76..78e0bf3fa8f 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ init_data, /* requiredDataMask */ required_data_mask, diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index e3c42e39dda..29d1ecf6050 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -193,7 +193,6 @@ ModifierTypeInfo modifierType_Lattice = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 191d39d9fce..812bfe3b375 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -463,7 +463,6 @@ ModifierTypeInfo modifierType_Mask = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index cc007651733..778b5746471 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -20,6 +20,7 @@ #include <vector> +#include "BKE_geometry_set.hh" #include "BKE_lib_query.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" @@ -204,7 +205,9 @@ static float compute_voxel_size(const ModifierEvalContext *ctx, } #endif -static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *input_volume) +static Volume *mesh_to_volume(ModifierData *md, + const ModifierEvalContext *ctx, + Volume *input_volume) { #ifdef WITH_OPENVDB using namespace blender; @@ -278,6 +281,17 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo #endif } +static void modifyGeometrySet(ModifierData *md, + const ModifierEvalContext *ctx, + GeometrySet *geometry_set) +{ + Volume *input_volume = geometry_set->get_volume_for_write(); + Volume *result_volume = mesh_to_volume(md, ctx, input_volume); + if (result_volume != input_volume) { + geometry_set->replace_volume(result_volume); + } +} + ModifierTypeInfo modifierType_MeshToVolume = { /* name */ "Mesh to Volume", /* structName */ "MeshToVolumeModifierData", @@ -295,8 +309,7 @@ ModifierTypeInfo modifierType_MeshToVolume = { /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, /* modifyHair */ nullptr, - /* modifyGeometrySet */ nullptr, - /* modifyVolume */ modifyVolume, + /* modifyGeometrySet */ modifyGeometrySet, /* initData */ initData, /* requiredDataMask */ nullptr, diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 6d2e0f242d7..361454120ca 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -389,7 +389,6 @@ ModifierTypeInfo modifierType_MeshCache = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index a94dd6da477..8e1e03570b5 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -644,7 +644,6 @@ ModifierTypeInfo modifierType_MeshDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 2c01857adb1..c2f9cd8c867 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -271,7 +271,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index b800ce7f803..6116cf8146a 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Mirror = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c index 1182c8db093..c3b34f3cd23 100644 --- a/source/blender/modifiers/intern/MOD_multires.c +++ b/source/blender/modifiers/intern/MOD_multires.c @@ -520,7 +520,6 @@ ModifierTypeInfo modifierType_Multires = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 607b05d39b0..63541329563 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -75,16 +75,14 @@ #include "MOD_modifiertypes.h" #include "MOD_nodes.h" +#include "MOD_nodes_evaluator.hh" #include "MOD_ui_common.h" #include "ED_spreadsheet.h" #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" -#include "NOD_geometry_exec.hh" #include "NOD_node_tree_multi_function.hh" -#include "NOD_type_callbacks.hh" -#include "NOD_type_conversions.hh" using blender::float3; using blender::FunctionRef; @@ -100,7 +98,6 @@ using blender::bke::PersistentDataHandleMap; using blender::bke::PersistentObjectHandle; using blender::fn::GMutablePointer; using blender::fn::GPointer; -using blender::fn::GValueMap; using blender::nodes::GeoNodeExecParams; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; @@ -185,7 +182,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec if (object.type == OB_EMPTY && object.instance_collection != nullptr) { add_collection_relation(ctx, *object.instance_collection); } - else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { + else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE)) { DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier"); DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask); } @@ -268,368 +265,6 @@ static bool logging_enabled(const ModifierEvalContext *ctx) return true; } -class GeometryNodesEvaluator { - public: - using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>; - - private: - blender::LinearAllocator<> allocator_; - Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_; - Vector<DInputSocket> group_outputs_; - blender::nodes::MultiFunctionByNode &mf_by_node_; - const blender::nodes::DataTypeConversions &conversions_; - const PersistentDataHandleMap &handle_map_; - const Object *self_object_; - const ModifierData *modifier_; - Depsgraph *depsgraph_; - LogSocketValueFn log_socket_value_fn_; - - public: - GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data, - Vector<DInputSocket> group_outputs, - blender::nodes::MultiFunctionByNode &mf_by_node, - const PersistentDataHandleMap &handle_map, - const Object *self_object, - const ModifierData *modifier, - Depsgraph *depsgraph, - LogSocketValueFn log_socket_value_fn) - : group_outputs_(std::move(group_outputs)), - mf_by_node_(mf_by_node), - conversions_(blender::nodes::get_implicit_type_conversions()), - handle_map_(handle_map), - self_object_(self_object), - modifier_(modifier), - depsgraph_(depsgraph), - log_socket_value_fn_(std::move(log_socket_value_fn)) - { - for (auto item : group_input_data.items()) { - this->log_socket_value(item.key, item.value); - this->forward_to_inputs(item.key, item.value); - } - } - - Vector<GMutablePointer> execute() - { - Vector<GMutablePointer> results; - for (const DInputSocket &group_output : group_outputs_) { - Vector<GMutablePointer> result = this->get_input_values(group_output); - this->log_socket_value(group_output, result); - results.append(result[0]); - } - for (GMutablePointer value : value_by_input_.values()) { - value.destruct(); - } - return results; - } - - private: - Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute) - { - Vector<DSocket> from_sockets; - socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); }); - - if (from_sockets.is_empty()) { - /* The input is not connected, use the value from the socket itself. */ - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); - return {get_unlinked_input_value(socket_to_compute, type)}; - } - - /* Multi-input sockets contain a vector of inputs. */ - if (socket_to_compute->is_multi_input_socket()) { - return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets); - } - - const DSocket from_socket = from_sockets[0]; - GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket); - return {value}; - } - - Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute, - const Span<DSocket> from_sockets) - { - Vector<GMutablePointer> values; - for (const int i : from_sockets.index_range()) { - const DSocket from_socket = from_sockets[i]; - const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket); - if (first_occurence == -1) { - values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket)); - } - else { - /* If the same from-socket occurs more than once, we make a copy of the first value. This - * can happen when a node linked to a multi-input-socket is muted. */ - GMutablePointer value = values[first_occurence]; - const CPPType *type = value.type(); - void *copy_buffer = allocator_.allocate(type->size(), type->alignment()); - type->copy_to_uninitialized(value.get(), copy_buffer); - values.append({type, copy_buffer}); - } - } - return values; - } - - GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute, - const DSocket from_socket) - { - if (from_socket->is_output()) { - const DOutputSocket from_output_socket{from_socket}; - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute, - from_output_socket); - std::optional<GMutablePointer> value = value_by_input_.pop_try(key); - if (value.has_value()) { - /* This input has been computed before, return it directly. */ - return {*value}; - } - - /* Compute the socket now. */ - this->compute_output_and_forward(from_output_socket); - return {value_by_input_.pop(key)}; - } - - /* Get value from an unlinked input socket. */ - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); - const DInputSocket from_input_socket{from_socket}; - return {get_unlinked_input_value(from_input_socket, type)}; - } - - void compute_output_and_forward(const DOutputSocket socket_to_compute) - { - const DNode node{socket_to_compute.context(), &socket_to_compute->node()}; - - if (!socket_to_compute->is_available()) { - /* If the output is not available, use a default value. */ - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - type.copy_to_uninitialized(type.default_value(), buffer); - this->forward_to_inputs(socket_to_compute, {type, buffer}); - return; - } - - /* Prepare inputs required to execute the node. */ - GValueMap<StringRef> node_inputs_map{allocator_}; - for (const InputSocketRef *input_socket : node->inputs()) { - if (input_socket->is_available()) { - DInputSocket dsocket{node.context(), input_socket}; - Vector<GMutablePointer> values = this->get_input_values(dsocket); - this->log_socket_value(dsocket, values); - for (int i = 0; i < values.size(); ++i) { - /* Values from Multi Input Sockets are stored in input map with the format - * <identifier>[<index>]. */ - blender::StringRefNull key = allocator_.copy_string( - input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : "")); - node_inputs_map.add_new_direct(key, std::move(values[i])); - } - } - } - - /* Execute the node. */ - GValueMap<StringRef> node_outputs_map{allocator_}; - GeoNodeExecParams params{ - node, node_inputs_map, node_outputs_map, handle_map_, self_object_, modifier_, depsgraph_}; - this->execute_node(node, params); - - /* Forward computed outputs to linked input sockets. */ - for (const OutputSocketRef *output_socket : node->outputs()) { - if (output_socket->is_available()) { - const DOutputSocket dsocket{node.context(), output_socket}; - GMutablePointer value = node_outputs_map.extract(output_socket->identifier()); - this->log_socket_value(dsocket, value); - this->forward_to_inputs(dsocket, value); - } - } - } - - void log_socket_value(const DSocket socket, Span<GPointer> values) - { - if (log_socket_value_fn_) { - log_socket_value_fn_(socket, values); - } - } - - void log_socket_value(const DSocket socket, Span<GMutablePointer> values) - { - this->log_socket_value(socket, values.cast<GPointer>()); - } - - void log_socket_value(const DSocket socket, GPointer value) - { - this->log_socket_value(socket, Span<GPointer>(&value, 1)); - } - - void execute_node(const DNode node, GeoNodeExecParams params) - { - const bNode &bnode = params.node(); - - /* Use the geometry-node-execute callback if it exists. */ - if (bnode.typeinfo->geometry_node_execute != nullptr) { - bnode.typeinfo->geometry_node_execute(params); - return; - } - - /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr); - if (multi_function != nullptr) { - this->execute_multi_function_node(node, params, *multi_function); - return; - } - - /* Just output default values if no implementation exists. */ - this->execute_unknown_node(node, params); - } - - void execute_multi_function_node(const DNode node, - GeoNodeExecParams params, - const MultiFunction &fn) - { - MFContextBuilder fn_context; - MFParamsBuilder fn_params{fn, 1}; - Vector<GMutablePointer> input_data; - for (const InputSocketRef *socket_ref : node->inputs()) { - if (socket_ref->is_available()) { - GMutablePointer data = params.extract_input(socket_ref->identifier()); - fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1)); - input_data.append(data); - } - } - Vector<GMutablePointer> output_data; - for (const OutputSocketRef *socket_ref : node->outputs()) { - if (socket_ref->is_available()) { - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo()); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1)); - output_data.append(GMutablePointer(type, buffer)); - } - } - fn.call(IndexRange(1), fn_params, fn_context); - for (GMutablePointer value : input_data) { - value.destruct(); - } - int output_index = 0; - for (const int i : node->outputs().index_range()) { - if (node->output(i).is_available()) { - GMutablePointer value = output_data[output_index]; - params.set_output_by_move(node->output(i).identifier(), value); - value.destruct(); - output_index++; - } - } - } - - void execute_unknown_node(const DNode node, GeoNodeExecParams params) - { - for (const OutputSocketRef *socket : node->outputs()) { - if (socket->is_available()) { - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); - params.set_output_by_copy(socket->identifier(), {type, type.default_value()}); - } - } - } - - void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward) - { - /* For all sockets that are linked with the from_socket push the value to their node. */ - Vector<DInputSocket> to_sockets_all; - - auto handle_target_socket_fn = [&](DInputSocket to_socket) { - to_sockets_all.append_non_duplicates(to_socket); - }; - auto handle_skipped_socket_fn = [&, this](DSocket socket) { - this->log_socket_value(socket, value_to_forward); - }; - - from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); - - const CPPType &from_type = *value_to_forward.type(); - Vector<DInputSocket> to_sockets_same_type; - for (const DInputSocket &to_socket : to_sockets_all) { - const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo()); - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); - if (from_type == to_type) { - to_sockets_same_type.append(to_socket); - } - else { - void *buffer = allocator_.allocate(to_type.size(), to_type.alignment()); - if (conversions_.is_convertible(from_type, to_type)) { - conversions_.convert_to_uninitialized( - from_type, to_type, value_to_forward.get(), buffer); - } - else { - to_type.copy_to_uninitialized(to_type.default_value(), buffer); - } - add_value_to_input_socket(key, GMutablePointer{to_type, buffer}); - } - } - - if (to_sockets_same_type.size() == 0) { - /* This value is not further used, so destruct it. */ - value_to_forward.destruct(); - } - else if (to_sockets_same_type.size() == 1) { - /* This value is only used on one input socket, no need to copy it. */ - const DInputSocket to_socket = to_sockets_same_type[0]; - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); - - add_value_to_input_socket(key, value_to_forward); - } - else { - /* Multiple inputs use the value, make a copy for every input except for one. */ - const DInputSocket first_to_socket = to_sockets_same_type[0]; - Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1); - const CPPType &type = *value_to_forward.type(); - const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket, - from_socket); - add_value_to_input_socket(first_key, value_to_forward); - for (const DInputSocket &to_socket : other_to_sockets) { - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - type.copy_to_uninitialized(value_to_forward.get(), buffer); - add_value_to_input_socket(key, GMutablePointer{type, buffer}); - } - } - } - - void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key, - GMutablePointer value) - { - value_by_input_.add_new(key, value); - } - - GMutablePointer get_unlinked_input_value(const DInputSocket &socket, - const CPPType &required_type) - { - bNodeSocket *bsocket = socket->bsocket(); - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - - if (bsocket->type == SOCK_OBJECT) { - Object *object = socket->default_value<bNodeSocketValueObject>()->value; - PersistentObjectHandle object_handle = handle_map_.lookup(object); - new (buffer) PersistentObjectHandle(object_handle); - } - else if (bsocket->type == SOCK_COLLECTION) { - Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value; - PersistentCollectionHandle collection_handle = handle_map_.lookup(collection); - new (buffer) PersistentCollectionHandle(collection_handle); - } - else { - blender::nodes::socket_cpp_value_get(*bsocket, buffer); - } - - if (type == required_type) { - return {type, buffer}; - } - if (conversions_.is_convertible(type, required_type)) { - void *converted_buffer = allocator_.allocate(required_type.size(), - required_type.alignment()); - conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer); - type.destruct(buffer); - return {required_type, converted_buffer}; - } - void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment()); - required_type.copy_to_uninitialized(required_type.default_value(), default_buffer); - return {required_type, default_buffer}; - } -}; - /** * This code is responsible for creating the new property and also creating the group of * properties in the prop_ui_container group for the UI info, the mapping for which is @@ -1297,21 +932,20 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, log_ui_hints(socket, values, ctx->object, nmd); }; - GeometryNodesEvaluator evaluator{group_inputs, - group_outputs, - mf_by_node, - handle_map, - ctx->object, - (ModifierData *)nmd, - ctx->depsgraph, - log_socket_value}; - - Vector<GMutablePointer> results = evaluator.execute(); - BLI_assert(results.size() == 1); - GMutablePointer result = results[0]; - - GeometrySet output_geometry = std::move(*(GeometrySet *)result.get()); - return output_geometry; + blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params; + eval_params.input_values = group_inputs; + eval_params.output_sockets = group_outputs; + eval_params.mf_by_node = &mf_by_node; + eval_params.handle_map = &handle_map; + eval_params.modifier_ = nmd; + eval_params.depsgraph = ctx->depsgraph; + eval_params.self_object = ctx->object; + eval_params.log_socket_value_fn = log_socket_value; + blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); + + BLI_assert(eval_params.r_output_values.size() == 1); + GMutablePointer result = eval_params.r_output_values[0]; + return result.relocate_out<GeometrySet>(); } /** @@ -1450,36 +1084,37 @@ static void draw_property_for_socket(uiLayout *layout, /* IDProperties can be removed with python, so there could be a situation where * there isn't a property for a socket or it doesn't have the correct type. */ - if (property != nullptr && property_type->is_correct_type(*property)) { + if (property == nullptr || !property_type->is_correct_type(*property)) { + return; + } - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); - char rna_path[sizeof(socket_id_esc) + 4]; - BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc); + char rna_path[sizeof(socket_id_esc) + 4]; + BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc); - /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough - * information about what type of ID to select for editing the values. This is because - * pointer IDProperties contain no information about their type. */ - switch (socket.type) { - case SOCK_OBJECT: { - uiItemPointerR( - layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); - break; - } - case SOCK_COLLECTION: { - uiItemPointerR(layout, - md_ptr, - rna_path, - bmain_ptr, - "collections", - socket.name, - ICON_OUTLINER_COLLECTION); - break; - } - default: - uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough + * information about what type of ID to select for editing the values. This is because + * pointer IDProperties contain no information about their type. */ + switch (socket.type) { + case SOCK_OBJECT: { + uiItemPointerR( + layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); + break; } + case SOCK_COLLECTION: { + uiItemPointerR(layout, + md_ptr, + rna_path, + bmain_ptr, + "collections", + socket.name, + ICON_OUTLINER_COLLECTION); + break; + } + default: + uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); } } @@ -1591,7 +1226,6 @@ ModifierTypeInfo modifierType_Nodes = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ modifyGeometrySet, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc new file mode 100644 index 00000000000..aa35f5c540f --- /dev/null +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -0,0 +1,452 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "MOD_nodes_evaluator.hh" + +#include "NOD_geometry_exec.hh" +#include "NOD_type_conversions.hh" + +#include "DEG_depsgraph_query.h" + +#include "FN_generic_value_map.hh" +#include "FN_multi_function.hh" + +namespace blender::modifiers::geometry_nodes { + +using bke::PersistentCollectionHandle; +using bke::PersistentObjectHandle; +using fn::CPPType; +using fn::GValueMap; +using nodes::GeoNodeExecParams; +using namespace fn::multi_function_types; + +class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider { + public: + LinearAllocator<> *allocator; + GValueMap<StringRef> *input_values; + GValueMap<StringRef> *output_values; + + bool can_get_input(StringRef identifier) const override + { + return input_values->contains(identifier); + } + + bool can_set_output(StringRef identifier) const override + { + return !output_values->contains(identifier); + } + + GMutablePointer extract_input(StringRef identifier) override + { + return this->input_values->extract(identifier); + } + + Vector<GMutablePointer> extract_multi_input(StringRef identifier) override + { + Vector<GMutablePointer> values; + int index = 0; + while (true) { + std::string sub_identifier = identifier; + if (index > 0) { + sub_identifier += "[" + std::to_string(index) + "]"; + } + if (!this->input_values->contains(sub_identifier)) { + break; + } + values.append(input_values->extract(sub_identifier)); + index++; + } + return values; + } + + GPointer get_input(StringRef identifier) const override + { + return this->input_values->lookup(identifier); + } + + GMutablePointer alloc_output_value(StringRef identifier, const CPPType &type) override + { + void *buffer = this->allocator->allocate(type.size(), type.alignment()); + GMutablePointer ptr{&type, buffer}; + this->output_values->add_new_direct(identifier, ptr); + return ptr; + } +}; + +class GeometryNodesEvaluator { + public: + using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>; + + private: + blender::LinearAllocator<> &allocator_; + Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_; + Vector<DInputSocket> group_outputs_; + blender::nodes::MultiFunctionByNode &mf_by_node_; + const blender::nodes::DataTypeConversions &conversions_; + const PersistentDataHandleMap &handle_map_; + const Object *self_object_; + const ModifierData *modifier_; + Depsgraph *depsgraph_; + LogSocketValueFn log_socket_value_fn_; + + public: + GeometryNodesEvaluator(GeometryNodesEvaluationParams ¶ms) + : allocator_(params.allocator), + group_outputs_(std::move(params.output_sockets)), + mf_by_node_(*params.mf_by_node), + conversions_(blender::nodes::get_implicit_type_conversions()), + handle_map_(*params.handle_map), + self_object_(params.self_object), + modifier_(¶ms.modifier_->modifier), + depsgraph_(params.depsgraph), + log_socket_value_fn_(std::move(params.log_socket_value_fn)) + { + for (auto item : params.input_values.items()) { + this->log_socket_value(item.key, item.value); + this->forward_to_inputs(item.key, item.value); + } + } + + Vector<GMutablePointer> execute() + { + Vector<GMutablePointer> results; + for (const DInputSocket &group_output : group_outputs_) { + Vector<GMutablePointer> result = this->get_input_values(group_output); + this->log_socket_value(group_output, result); + results.append(result[0]); + } + for (GMutablePointer value : value_by_input_.values()) { + value.destruct(); + } + return results; + } + + private: + Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute) + { + Vector<DSocket> from_sockets; + socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); }); + + if (from_sockets.is_empty()) { + /* The input is not connected, use the value from the socket itself. */ + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); + return {get_unlinked_input_value(socket_to_compute, type)}; + } + + /* Multi-input sockets contain a vector of inputs. */ + if (socket_to_compute->is_multi_input_socket()) { + return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets); + } + + const DSocket from_socket = from_sockets[0]; + GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket); + return {value}; + } + + Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute, + const Span<DSocket> from_sockets) + { + Vector<GMutablePointer> values; + for (const int i : from_sockets.index_range()) { + const DSocket from_socket = from_sockets[i]; + const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket); + if (first_occurence == -1) { + values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket)); + } + else { + /* If the same from-socket occurs more than once, we make a copy of the first value. This + * can happen when a node linked to a multi-input-socket is muted. */ + GMutablePointer value = values[first_occurence]; + const CPPType *type = value.type(); + void *copy_buffer = allocator_.allocate(type->size(), type->alignment()); + type->copy_to_uninitialized(value.get(), copy_buffer); + values.append({type, copy_buffer}); + } + } + return values; + } + + GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute, + const DSocket from_socket) + { + if (from_socket->is_output()) { + const DOutputSocket from_output_socket{from_socket}; + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute, + from_output_socket); + std::optional<GMutablePointer> value = value_by_input_.pop_try(key); + if (value.has_value()) { + /* This input has been computed before, return it directly. */ + return {*value}; + } + + /* Compute the socket now. */ + this->compute_output_and_forward(from_output_socket); + return {value_by_input_.pop(key)}; + } + + /* Get value from an unlinked input socket. */ + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); + const DInputSocket from_input_socket{from_socket}; + return {get_unlinked_input_value(from_input_socket, type)}; + } + + void compute_output_and_forward(const DOutputSocket socket_to_compute) + { + const DNode node{socket_to_compute.context(), &socket_to_compute->node()}; + + if (!socket_to_compute->is_available()) { + /* If the output is not available, use a default value. */ + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); + void *buffer = allocator_.allocate(type.size(), type.alignment()); + type.copy_to_uninitialized(type.default_value(), buffer); + this->forward_to_inputs(socket_to_compute, {type, buffer}); + return; + } + + /* Prepare inputs required to execute the node. */ + GValueMap<StringRef> node_inputs_map{allocator_}; + for (const InputSocketRef *input_socket : node->inputs()) { + if (input_socket->is_available()) { + DInputSocket dsocket{node.context(), input_socket}; + Vector<GMutablePointer> values = this->get_input_values(dsocket); + this->log_socket_value(dsocket, values); + for (int i = 0; i < values.size(); ++i) { + /* Values from Multi Input Sockets are stored in input map with the format + * <identifier>[<index>]. */ + blender::StringRefNull key = allocator_.copy_string( + input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : "")); + node_inputs_map.add_new_direct(key, std::move(values[i])); + } + } + } + + /* Execute the node. */ + GValueMap<StringRef> node_outputs_map{allocator_}; + NodeParamsProvider params_provider; + params_provider.dnode = node; + params_provider.handle_map = &handle_map_; + params_provider.self_object = self_object_; + params_provider.depsgraph = depsgraph_; + params_provider.allocator = &allocator_; + params_provider.input_values = &node_inputs_map; + params_provider.output_values = &node_outputs_map; + params_provider.modifier = modifier_; + this->execute_node(node, params_provider); + + /* Forward computed outputs to linked input sockets. */ + for (const OutputSocketRef *output_socket : node->outputs()) { + if (output_socket->is_available()) { + const DOutputSocket dsocket{node.context(), output_socket}; + GMutablePointer value = node_outputs_map.extract(output_socket->identifier()); + this->log_socket_value(dsocket, value); + this->forward_to_inputs(dsocket, value); + } + } + } + + void log_socket_value(const DSocket socket, Span<GPointer> values) + { + if (log_socket_value_fn_) { + log_socket_value_fn_(socket, values); + } + } + + void log_socket_value(const DSocket socket, Span<GMutablePointer> values) + { + this->log_socket_value(socket, values.cast<GPointer>()); + } + + void log_socket_value(const DSocket socket, GPointer value) + { + this->log_socket_value(socket, Span<GPointer>(&value, 1)); + } + + void execute_node(const DNode node, NodeParamsProvider ¶ms_provider) + { + const bNode &bnode = *params_provider.dnode->bnode(); + + /* Use the geometry-node-execute callback if it exists. */ + if (bnode.typeinfo->geometry_node_execute != nullptr) { + GeoNodeExecParams params{params_provider}; + bnode.typeinfo->geometry_node_execute(params); + return; + } + + /* Use the multi-function implementation if it exists. */ + const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr); + if (multi_function != nullptr) { + this->execute_multi_function_node(node, params_provider, *multi_function); + return; + } + + /* Just output default values if no implementation exists. */ + this->execute_unknown_node(node, params_provider); + } + + void execute_multi_function_node(const DNode node, + NodeParamsProvider ¶ms_provider, + const MultiFunction &fn) + { + MFContextBuilder fn_context; + MFParamsBuilder fn_params{fn, 1}; + Vector<GMutablePointer> input_data; + for (const InputSocketRef *socket_ref : node->inputs()) { + if (socket_ref->is_available()) { + GMutablePointer data = params_provider.extract_input(socket_ref->identifier()); + fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1)); + input_data.append(data); + } + } + Vector<GMutablePointer> output_data; + for (const OutputSocketRef *socket_ref : node->outputs()) { + if (socket_ref->is_available()) { + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo()); + GMutablePointer output_value = params_provider.alloc_output_value(socket_ref->identifier(), + type); + fn_params.add_uninitialized_single_output(GMutableSpan{type, output_value.get(), 1}); + output_data.append(output_value); + } + } + fn.call(IndexRange(1), fn_params, fn_context); + for (GMutablePointer value : input_data) { + value.destruct(); + } + } + + void execute_unknown_node(const DNode node, NodeParamsProvider ¶ms_provider) + { + for (const OutputSocketRef *socket : node->outputs()) { + if (socket->is_available()) { + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); + params_provider.output_values->add_new_by_copy(socket->identifier(), + {type, type.default_value()}); + } + } + } + + void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward) + { + /* For all sockets that are linked with the from_socket push the value to their node. */ + Vector<DInputSocket> to_sockets_all; + + auto handle_target_socket_fn = [&](DInputSocket to_socket) { + to_sockets_all.append_non_duplicates(to_socket); + }; + auto handle_skipped_socket_fn = [&, this](DSocket socket) { + this->log_socket_value(socket, value_to_forward); + }; + + from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); + + const CPPType &from_type = *value_to_forward.type(); + Vector<DInputSocket> to_sockets_same_type; + for (const DInputSocket &to_socket : to_sockets_all) { + const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo()); + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); + if (from_type == to_type) { + to_sockets_same_type.append(to_socket); + } + else { + void *buffer = allocator_.allocate(to_type.size(), to_type.alignment()); + if (conversions_.is_convertible(from_type, to_type)) { + conversions_.convert_to_uninitialized( + from_type, to_type, value_to_forward.get(), buffer); + } + else { + to_type.copy_to_uninitialized(to_type.default_value(), buffer); + } + add_value_to_input_socket(key, GMutablePointer{to_type, buffer}); + } + } + + if (to_sockets_same_type.size() == 0) { + /* This value is not further used, so destruct it. */ + value_to_forward.destruct(); + } + else if (to_sockets_same_type.size() == 1) { + /* This value is only used on one input socket, no need to copy it. */ + const DInputSocket to_socket = to_sockets_same_type[0]; + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); + + add_value_to_input_socket(key, value_to_forward); + } + else { + /* Multiple inputs use the value, make a copy for every input except for one. */ + const DInputSocket first_to_socket = to_sockets_same_type[0]; + Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1); + const CPPType &type = *value_to_forward.type(); + const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket, + from_socket); + add_value_to_input_socket(first_key, value_to_forward); + for (const DInputSocket &to_socket : other_to_sockets) { + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); + void *buffer = allocator_.allocate(type.size(), type.alignment()); + type.copy_to_uninitialized(value_to_forward.get(), buffer); + add_value_to_input_socket(key, GMutablePointer{type, buffer}); + } + } + } + + void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key, + GMutablePointer value) + { + value_by_input_.add_new(key, value); + } + + GMutablePointer get_unlinked_input_value(const DInputSocket &socket, + const CPPType &required_type) + { + bNodeSocket *bsocket = socket->bsocket(); + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); + void *buffer = allocator_.allocate(type.size(), type.alignment()); + + if (bsocket->type == SOCK_OBJECT) { + Object *object = socket->default_value<bNodeSocketValueObject>()->value; + PersistentObjectHandle object_handle = handle_map_.lookup(object); + new (buffer) PersistentObjectHandle(object_handle); + } + else if (bsocket->type == SOCK_COLLECTION) { + Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value; + PersistentCollectionHandle collection_handle = handle_map_.lookup(collection); + new (buffer) PersistentCollectionHandle(collection_handle); + } + else { + blender::nodes::socket_cpp_value_get(*bsocket, buffer); + } + + if (type == required_type) { + return {type, buffer}; + } + if (conversions_.is_convertible(type, required_type)) { + void *converted_buffer = allocator_.allocate(required_type.size(), + required_type.alignment()); + conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer); + type.destruct(buffer); + return {required_type, converted_buffer}; + } + void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment()); + required_type.copy_to_uninitialized(required_type.default_value(), default_buffer); + return {required_type, default_buffer}; + } +}; + +void evaluate_geometry_nodes(GeometryNodesEvaluationParams ¶ms) +{ + GeometryNodesEvaluator evaluator{params}; + params.r_output_values = 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 new file mode 100644 index 00000000000..3d9a03350f7 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BLI_map.hh" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_tree_multi_function.hh" + +#include "FN_generic_pointer.hh" + +#include "BKE_persistent_data_handle.hh" + +#include "DNA_modifier_types.h" + +namespace blender::modifiers::geometry_nodes { + +using namespace nodes::derived_node_tree_types; +using bke::PersistentDataHandleMap; +using fn::GMutablePointer; +using fn::GPointer; + +using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>; + +struct GeometryNodesEvaluationParams { + blender::LinearAllocator<> allocator; + + Map<DOutputSocket, GMutablePointer> input_values; + Vector<DInputSocket> output_sockets; + nodes::MultiFunctionByNode *mf_by_node; + const PersistentDataHandleMap *handle_map; + const NodesModifierData *modifier_; + Depsgraph *depsgraph; + Object *self_object; + LogSocketValueFn log_socket_value_fn; + + Vector<GMutablePointer> r_output_values; +}; + +void evaluate_geometry_nodes(GeometryNodesEvaluationParams ¶ms); + +} // namespace blender::modifiers::geometry_nodes diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c index 4daa527577b..a01f63be791 100644 --- a/source/blender/modifiers/intern/MOD_none.c +++ b/source/blender/modifiers/intern/MOD_none.c @@ -60,7 +60,6 @@ ModifierTypeInfo modifierType_None = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ NULL, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index ec10b18665e..4bfd6aba4b2 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -805,7 +805,6 @@ ModifierTypeInfo modifierType_NormalEdit = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 9c940745920..f7ac59f9e4b 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -737,7 +737,6 @@ ModifierTypeInfo modifierType_Ocean = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index e7f1fa9943e..60c5667472e 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -679,7 +679,6 @@ ModifierTypeInfo modifierType_ParticleInstance = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index 4c1179af431..38cce5e6a50 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -337,7 +337,6 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index 175435fcd44..88851f91337 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -301,7 +301,6 @@ ModifierTypeInfo modifierType_Remesh = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 84360caa345..b236e0896b7 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1258,7 +1258,6 @@ ModifierTypeInfo modifierType_Screw = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c index 81a0ee72496..b517bc102f8 100644 --- a/source/blender/modifiers/intern/MOD_shapekey.c +++ b/source/blender/modifiers/intern/MOD_shapekey.c @@ -141,7 +141,6 @@ ModifierTypeInfo modifierType_ShapeKey = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ NULL, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index 93626309727..a12724ec23c 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -293,7 +293,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 951eacdecda..db01dec4d19 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -21,10 +21,9 @@ * \ingroup modifiers */ -#include "BLI_utildefines.h" - #include "BLI_math.h" - +#include "BLI_task.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" #include "DNA_defaults.h" @@ -57,6 +56,21 @@ #define BEND_EPS 0.000001f +ALIGN_STRUCT struct DeformUserData { + bool invert_vgroup; + char mode; + char deform_axis; + int lock_axis; + int vgroup; + int limit_axis; + float weight; + float smd_factor; + float smd_limit[2]; + float (*vertexCos)[3]; + const SpaceTransform *transf; + const MDeformVert *dvert; +}; + /* Re-maps the indices for X Y Z by shifting them up and wrapping, such that * X = Y, Y = Z, Z = X (for X axis), and X = Z, Y = X, Z = Y (for Y axis). This * exists because the deformations (excluding bend) are based on the Z axis. @@ -205,6 +219,88 @@ static void simpleDeform_bend(const float factor, } } +static void simple_helper(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + const struct DeformUserData *curr_deform_data = userdata; + float weight = BKE_defvert_array_find_weight_safe( + curr_deform_data->dvert, iter, curr_deform_data->vgroup); + const uint *axis_map = axis_map_table[(curr_deform_data->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? + curr_deform_data->deform_axis : + 2]; + const float base_limit[2] = {0.0f, 0.0f}; + + if (curr_deform_data->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight != 0.0f) { + float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; + + if (curr_deform_data->transf) { + BLI_space_transform_apply(curr_deform_data->transf, curr_deform_data->vertexCos[iter]); + } + + copy_v3_v3(co, curr_deform_data->vertexCos[iter]); + + /* Apply axis limits, and axis mappings */ + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) { + axis_limit(0, base_limit, co, dcut); + } + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) { + axis_limit(1, base_limit, co, dcut); + } + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) { + axis_limit(2, base_limit, co, dcut); + } + axis_limit(curr_deform_data->limit_axis, curr_deform_data->smd_limit, co, dcut); + + /* apply the deform to a mapped copy of the vertex, and then re-map it back. */ + float co_remap[3]; + float dcut_remap[3]; + copy_v3_v3_map(co_remap, co, axis_map); + copy_v3_v3_map(dcut_remap, dcut, axis_map); + switch (curr_deform_data->mode) { + case MOD_SIMPLEDEFORM_MODE_TWIST: + simpleDeform_twist(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_BEND: + simpleDeform_bend(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_TAPER: + simpleDeform_taper(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_STRETCH: + simpleDeform_stretch(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + default: + return; /* No simple-deform mode? */ + } + copy_v3_v3_unmap(co, co_remap, axis_map); + + /* Use vertex weight has coef of linear interpolation */ + interp_v3_v3v3( + curr_deform_data->vertexCos[iter], curr_deform_data->vertexCos[iter], co, weight); + + if (curr_deform_data->transf) { + BLI_space_transform_invert(curr_deform_data->transf, curr_deform_data->vertexCos[iter]); + } + } +} + /* simple deform modifier */ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, const ModifierEvalContext *UNUSED(ctx), @@ -213,14 +309,9 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, float (*vertexCos)[3], int numVerts) { - const float base_limit[2] = {0.0f, 0.0f}; int i; float smd_limit[2], smd_factor; SpaceTransform *transf = NULL, tmp_transf; - void (*simpleDeform_callback)(const float factor, - const int axis, - const float dcut[3], - float co[3]) = NULL; /* Mode callback */ int vgroup; MDeformVert *dvert; @@ -302,23 +393,6 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); } - switch (smd->mode) { - case MOD_SIMPLEDEFORM_MODE_TWIST: - simpleDeform_callback = simpleDeform_twist; - break; - case MOD_SIMPLEDEFORM_MODE_BEND: - simpleDeform_callback = simpleDeform_bend; - break; - case MOD_SIMPLEDEFORM_MODE_TAPER: - simpleDeform_callback = simpleDeform_taper; - break; - case MOD_SIMPLEDEFORM_MODE_STRETCH: - simpleDeform_callback = simpleDeform_stretch; - break; - default: - return; /* No simple-deform mode? */ - } - if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { if (fabsf(smd_factor) < BEND_EPS) { return; @@ -327,53 +401,26 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, MOD_get_vgroup(ob, mesh, smd->vgroup_name, &dvert, &vgroup); const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0; - const uint *axis_map = - axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2]; - - for (i = 0; i < numVerts; i++) { - float weight = BKE_defvert_array_find_weight_safe(dvert, i, vgroup); - - if (invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight != 0.0f) { - float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; - if (transf) { - BLI_space_transform_apply(transf, vertexCos[i]); - } - - copy_v3_v3(co, vertexCos[i]); - - /* Apply axis limits, and axis mappings */ - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) { - axis_limit(0, base_limit, co, dcut); - } - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) { - axis_limit(1, base_limit, co, dcut); - } - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) { - axis_limit(2, base_limit, co, dcut); - } - axis_limit(limit_axis, smd_limit, co, dcut); - - /* apply the deform to a mapped copy of the vertex, and then re-map it back. */ - float co_remap[3]; - float dcut_remap[3]; - copy_v3_v3_map(co_remap, co, axis_map); - copy_v3_v3_map(dcut_remap, dcut, axis_map); - simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */ - copy_v3_v3_unmap(co, co_remap, axis_map); - - /* Use vertex weight has coef of linear interpolation */ - interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); - - if (transf) { - BLI_space_transform_invert(transf, vertexCos[i]); - } - } - } + /* Build our data. */ + const struct DeformUserData deform_pool_data = { + .mode = smd->mode, + .smd_factor = smd_factor, + .deform_axis = deform_axis, + .transf = transf, + .vertexCos = vertexCos, + .invert_vgroup = invert_vgroup, + .lock_axis = lock_axis, + .vgroup = vgroup, + .smd_limit[0] = smd_limit[0], + .smd_limit[1] = smd_limit[1], + .dvert = dvert, + .limit_axis = limit_axis, + }; + /* Do deformation. */ + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, numVerts, (void *)&deform_pool_data, simple_helper, &settings); } /* SimpleDeform */ @@ -555,7 +602,6 @@ ModifierTypeInfo modifierType_SimpleDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 5e412185cea..e2d18cf1790 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -2044,7 +2044,6 @@ ModifierTypeInfo modifierType_Skin = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index c5011ed15c1..97027e2ecff 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -286,7 +286,6 @@ ModifierTypeInfo modifierType_Smooth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 9a657c44fca..d7d2f948955 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -120,7 +120,6 @@ ModifierTypeInfo modifierType_Softbody = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ NULL, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 8e519a72df1..736dd08a713 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -276,7 +276,6 @@ ModifierTypeInfo modifierType_Solidify = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index c3611488f5f..4dc45ad0324 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -509,7 +509,6 @@ ModifierTypeInfo modifierType_Subsurf = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 7416a4baf38..d2c011a21d3 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -241,7 +241,6 @@ ModifierTypeInfo modifierType_Surface = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index 99011c5e351..0cc68c2c4a3 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1646,7 +1646,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index 04d24ac0883..ef633494c7b 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -178,7 +178,6 @@ ModifierTypeInfo modifierType_Triangulate = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, // requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 3162a33edc2..724d1370a47 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -385,7 +385,6 @@ ModifierTypeInfo modifierType_UVProject = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index 77b79167c2f..5742144b6dd 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -342,7 +342,6 @@ ModifierTypeInfo modifierType_UVWarp = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index e7c4004853e..af4b31d6bfc 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -18,6 +18,7 @@ * \ingroup modifiers */ +#include "BKE_geometry_set.hh" #include "BKE_lib_query.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" @@ -284,7 +285,7 @@ struct DisplaceGridOp { #endif -static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume) +static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume) { #ifdef WITH_OPENVDB VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md); @@ -303,14 +304,22 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo BKE_volume_grid_type_operation(grid_type, displace_grid_op); } - return volume; #else - UNUSED_VARS(md, ctx); + UNUSED_VARS(md, volume, ctx); BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB"); - return volume; #endif } +static void modifyGeometrySet(ModifierData *md, + const ModifierEvalContext *ctx, + GeometrySet *geometry_set) +{ + Volume *input_volume = geometry_set->get_volume_for_write(); + if (input_volume != nullptr) { + displace_volume(md, ctx, input_volume); + } +} + ModifierTypeInfo modifierType_VolumeDisplace = { /* name */ "Volume Displace", /* structName */ "VolumeDisplaceModifierData", @@ -328,8 +337,7 @@ ModifierTypeInfo modifierType_VolumeDisplace = { /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, /* modifyHair */ nullptr, - /* modifyGeometrySet */ nullptr, - /* modifyVolume */ modifyVolume, + /* modifyGeometrySet */ modifyGeometrySet, /* initData */ initData, /* requiredDataMask */ nullptr, diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index 3bf5cf6ab5b..c0bf07b8eec 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -223,7 +223,6 @@ ModifierTypeInfo modifierType_VolumeToMesh = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ nullptr, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 9d3d5b0658c..3bebc52c503 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -539,7 +539,6 @@ ModifierTypeInfo modifierType_Warp = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 863656b85a5..c6bab89247e 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -492,7 +492,6 @@ ModifierTypeInfo modifierType_Wave = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index 40265e37db9..2f2da7d6554 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -764,7 +764,6 @@ ModifierTypeInfo modifierType_WeightedNormal = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 915adccc745..b5f72c88800 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -428,7 +428,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 52cee7ce7e5..a71a2f3b480 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -514,7 +514,6 @@ ModifierTypeInfo modifierType_WeightVGMix = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index aac29cabf0f..cd03175f16c 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -768,7 +768,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index fd1254fc948..1590f342666 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -2053,7 +2053,6 @@ ModifierTypeInfo modifierType_Weld = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 3d8e74d2cf5..16bf1f7d763 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -196,7 +196,6 @@ ModifierTypeInfo modifierType_Wireframe = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 8168f0dba73..c116ceb8968 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -154,11 +154,13 @@ set(SRC geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_sample_texture.cc geometry/nodes/node_geo_attribute_separate_xyz.cc + geometry/nodes/node_geo_attribute_transfer.cc geometry/nodes/node_geo_attribute_vector_math.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_bounding_box.cc geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc + geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc @@ -180,6 +182,7 @@ set(SRC geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_subdivide.cc geometry/nodes/node_geo_subdivision_surface.cc + geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc geometry/nodes/node_geo_volume_to_mesh.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index b84c80e916c..14c9e235bb6 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -42,11 +42,13 @@ void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_proximity(void); void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_separate_xyz(void); +void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); +void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); @@ -69,6 +71,7 @@ void register_node_type_geo_points_to_volume(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_subdivide(void); void register_node_type_geo_subdivision_surface(void); +void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); void register_node_type_geo_triangulate(void); void register_node_type_geo_volume_to_mesh(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 5d1a217db9b..83f084c2f25 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -33,55 +33,82 @@ struct ModifierData; namespace blender::nodes { -using bke::BooleanReadAttribute; -using bke::BooleanWriteAttribute; -using bke::Color4fReadAttribute; -using bke::Color4fWriteAttribute; -using bke::Float2ReadAttribute; -using bke::Float2WriteAttribute; -using bke::Float3ReadAttribute; -using bke::Float3WriteAttribute; -using bke::FloatReadAttribute; -using bke::FloatWriteAttribute; using bke::geometry_set_realize_instances; -using bke::Int32ReadAttribute; -using bke::Int32WriteAttribute; +using bke::OutputAttribute; +using bke::OutputAttribute_Typed; using bke::PersistentDataHandleMap; using bke::PersistentObjectHandle; -using bke::ReadAttribute; -using bke::ReadAttributePtr; -using bke::WriteAttribute; -using bke::WriteAttributePtr; +using bke::ReadAttributeLookup; +using bke::WriteAttributeLookup; using fn::CPPType; using fn::GMutablePointer; +using fn::GMutableSpan; using fn::GPointer; +using fn::GSpan; using fn::GValueMap; +using fn::GVArray; +using fn::GVArray_GSpan; +using fn::GVArray_Span; +using fn::GVArray_Typed; +using fn::GVArrayPtr; +using fn::GVMutableArray; +using fn::GVMutableArray_GSpan; +using fn::GVMutableArray_Typed; +using fn::GVMutableArrayPtr; + +/** + * 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 PersistentDataHandleMap *handle_map = nullptr; + const Object *self_object = nullptr; + const ModifierData *modifier = nullptr; + Depsgraph *depsgraph = 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(StringRef identifier, const CPPType &type) = 0; +}; class GeoNodeExecParams { private: - const DNode node_; - GValueMap<StringRef> &input_values_; - GValueMap<StringRef> &output_values_; - const PersistentDataHandleMap &handle_map_; - const Object *self_object_; - const ModifierData *modifier_; - Depsgraph *depsgraph_; + GeoNodeExecParamsProvider *provider_; public: - GeoNodeExecParams(const DNode node, - GValueMap<StringRef> &input_values, - GValueMap<StringRef> &output_values, - const PersistentDataHandleMap &handle_map, - const Object *self_object, - const ModifierData *modifier, - Depsgraph *depsgraph) - : node_(node), - input_values_(input_values), - output_values_(output_values), - handle_map_(handle_map), - self_object_(self_object), - modifier_(modifier), - depsgraph_(depsgraph) + GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider) { } @@ -94,9 +121,9 @@ class GeoNodeExecParams { GMutablePointer extract_input(StringRef identifier) { #ifdef DEBUG - this->check_extract_input(identifier); + this->check_input_access(identifier); #endif - return input_values_.extract(identifier); + return provider_->extract_input(identifier); } /** @@ -107,9 +134,10 @@ class GeoNodeExecParams { template<typename T> T extract_input(StringRef identifier) { #ifdef DEBUG - this->check_extract_input(identifier, &CPPType::get<T>()); + this->check_input_access(identifier, &CPPType::get<T>()); #endif - return input_values_.extract<T>(identifier); + GMutablePointer gvalue = this->extract_input(identifier); + return gvalue.relocate_out<T>(); } /** @@ -119,18 +147,10 @@ class GeoNodeExecParams { */ template<typename T> Vector<T> extract_multi_input(StringRef identifier) { + Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier); Vector<T> values; - int index = 0; - while (true) { - std::string sub_identifier = identifier; - if (index > 0) { - sub_identifier += "[" + std::to_string(index) + "]"; - } - if (!input_values_.contains(sub_identifier)) { - break; - } - values.append(input_values_.extract<T, StringRef>(sub_identifier)); - index++; + for (GMutablePointer gvalue : gvalues) { + values.append(gvalue.relocate_out<T>()); } return values; } @@ -141,33 +161,11 @@ class GeoNodeExecParams { template<typename T> const T &get_input(StringRef identifier) const { #ifdef DEBUG - this->check_extract_input(identifier, &CPPType::get<T>()); + this->check_input_access(identifier, &CPPType::get<T>()); #endif - return input_values_.lookup<T>(identifier); - } - - /** - * Move-construct a new value based on the given value and store it for the given socket - * identifier. - */ - void set_output_by_move(StringRef identifier, GMutablePointer value) - { -#ifdef DEBUG - BLI_assert(value.type() != nullptr); - BLI_assert(value.get() != nullptr); - this->check_set_output(identifier, *value.type()); -#endif - output_values_.add_new_by_move(identifier, value); - } - - void set_output_by_copy(StringRef identifier, GPointer value) - { -#ifdef DEBUG - BLI_assert(value.type() != nullptr); - BLI_assert(value.get() != nullptr); - this->check_set_output(identifier, *value.type()); -#endif - output_values_.add_new_by_copy(identifier, value); + GPointer gvalue = provider_->get_input(identifier); + BLI_assert(gvalue.is_type<T>()); + return *(const T *)gvalue.get(); } /** @@ -175,10 +173,13 @@ class GeoNodeExecParams { */ template<typename T> void set_output(StringRef identifier, T &&value) { + using StoredT = std::decay_t<T>; + const CPPType &type = CPPType::get<std::decay_t<T>>(); #ifdef DEBUG - this->check_set_output(identifier, CPPType::get<std::decay_t<T>>()); + this->check_output_access(identifier, type); #endif - output_values_.add_new(identifier, std::forward<T>(value)); + GMutablePointer gvalue = provider_->alloc_output_value(identifier, type); + new (gvalue.get()) StoredT(std::forward<T>(value)); } /** @@ -186,22 +187,22 @@ class GeoNodeExecParams { */ const bNode &node() const { - return *node_->bnode(); + return *provider_->dnode->bnode(); } const PersistentDataHandleMap &handle_map() const { - return handle_map_; + return *provider_->handle_map; } const Object *self_object() const { - return self_object_; + return provider_->self_object; } Depsgraph *depsgraph() const { - return depsgraph_; + return provider_->depsgraph; } /** @@ -217,20 +218,21 @@ class GeoNodeExecParams { * \note This will add an error message if the string socket is active and * the input attribute does not exist. */ - ReadAttributePtr get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const; + GVArrayPtr get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const CustomDataType type, + const void *default_value) const; template<typename T> - bke::TypedReadAttribute<T> get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const T &default_value) const + GVArray_Typed<T> get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const T &default_value) const { const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>()); - return this->get_input_attribute(name, component, domain, type, &default_value); + GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value); + return GVArray_Typed<T>(std::move(varray)); } /** @@ -247,8 +249,8 @@ class GeoNodeExecParams { private: /* Utilities for detecting common errors at when using this class. */ - void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const; - void check_set_output(StringRef identifier, const CPPType &value_type) const; + void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const; + void check_output_access(StringRef identifier, const CPPType &value_type) const; /* Find the active socket socket with the input name (not the identifier). */ const bNodeSocket *find_available_socket(const StringRef name) const; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index bf6bf34325a..2e4187229f4 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -310,6 +310,9 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") +DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/NOD_type_conversions.hh b/source/blender/nodes/NOD_type_conversions.hh index 34225208fe6..ec4859f0657 100644 --- a/source/blender/nodes/NOD_type_conversions.hh +++ b/source/blender/nodes/NOD_type_conversions.hh @@ -72,6 +72,10 @@ class DataTypeConversions { const CPPType &to_type, const void *from_value, void *to_value) const; + + fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const; + + fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const; }; const DataTypeConversions &get_implicit_type_conversions(); diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index 085b5c463b9..19815d01278 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -205,6 +205,12 @@ static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode) } } +static bool composite_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); +} + bNodeTreeType *ntreeType_Composite; void register_node_tree_type_cmp(void) @@ -227,6 +233,7 @@ void register_node_tree_type_cmp(void) tt->update = update; tt->get_from_context = composite_get_from_context; tt->node_add_init = composite_node_add_init; + tt->valid_socket_type = composite_node_tree_socket_type_valid; tt->rna_ext.srna = &RNA_CompositorNodeTree; diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 6d3b1d55005..afb86ff57b8 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -84,6 +84,21 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); } +static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_BOOLEAN, + SOCK_INT, + SOCK_STRING, + SOCK_OBJECT, + SOCK_GEOMETRY, + SOCK_COLLECTION); +} + void register_node_tree_type_geo(void) { bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>( @@ -97,6 +112,7 @@ void register_node_tree_type_geo(void) tt->update = geometry_node_tree_update; tt->get_from_context = geometry_node_tree_get_from_context; tt->foreach_nodeclass = foreach_nodeclass; + tt->valid_socket_type = geometry_node_tree_socket_type_valid; ntreeTypeAdd(tt); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc index f10b81a33b4..720ca9731a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc @@ -15,6 +15,7 @@ */ #include "BLI_math_rotation.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -50,91 +51,95 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, namespace blender::nodes { -static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors, - const FloatReadAttribute &factors, +static void align_rotations_auto_pivot(const VArray<float3> &vectors, + const VArray<float> &factors, const float3 local_main_axis, - MutableSpan<float3> rotations) + const MutableSpan<float3> rotations) { - for (const int i : IndexRange(vectors.size())) { - const float3 vector = vectors[i]; - if (is_zero_v3(vector)) { - continue; - } + parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { + for (const int i : range) { + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + continue; + } - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float3 old_axis; - mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); - const float3 new_axis = vector.normalized(); - float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis); - if (is_zero_v3(rotation_axis)) { - /* The vectors are linearly dependent, so we fall back to another axis. */ - rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0)); + const float3 new_axis = vector.normalized(); + float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis); if (is_zero_v3(rotation_axis)) { - /* This is now guaranteed to not be zero. */ - rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0)); + /* The vectors are linearly dependent, so we fall back to another axis. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0)); + if (is_zero_v3(rotation_axis)) { + /* This is now guaranteed to not be zero. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0)); + } } - } - const float full_angle = angle_normalized_v3v3(old_axis, new_axis); - const float angle = factors[i] * full_angle; + const float full_angle = angle_normalized_v3v3(old_axis, new_axis); + const float angle = factors[i] * full_angle; - float rotation[3][3]; - axis_angle_to_mat3(rotation, rotation_axis, angle); + float rotation[3][3]; + axis_angle_to_mat3(rotation, rotation_axis, angle); - float new_rotation_matrix[3][3]; - mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); - float3 new_rotation; - mat3_to_eul(new_rotation, new_rotation_matrix); + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); - rotations[i] = new_rotation; - } + rotations[i] = new_rotation; + } + }); } -static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors, - const FloatReadAttribute &factors, +static void align_rotations_fixed_pivot(const VArray<float3> &vectors, + const VArray<float> &factors, const float3 local_main_axis, const float3 local_pivot_axis, - MutableSpan<float3> rotations) + const MutableSpan<float3> rotations) { if (local_main_axis == local_pivot_axis) { /* Can't compute any meaningful rotation angle in this case. */ return; } - for (const int i : IndexRange(vectors.size())) { - const float3 vector = vectors[i]; - if (is_zero_v3(vector)) { - continue; - } + parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { + for (const int i : range) { + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + continue; + } - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float3 old_axis; - mul_v3_m3v3(old_axis, old_rotation, local_main_axis); - float3 pivot_axis; - mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); - - float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); - if (full_angle > M_PI) { - /* Make sure the point is rotated as little as possible. */ - full_angle -= 2.0f * M_PI; - } - const float angle = factors[i] * full_angle; + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + float3 pivot_axis; + mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); + + float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); + if (full_angle > M_PI) { + /* Make sure the point is rotated as little as possible. */ + full_angle -= 2.0f * M_PI; + } + const float angle = factors[i] * full_angle; - float rotation[3][3]; - axis_angle_to_mat3(rotation, pivot_axis, angle); + float rotation[3][3]; + axis_angle_to_mat3(rotation, pivot_axis, angle); - float new_rotation_matrix[3][3]; - mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); - float3 new_rotation; - mat3_to_eul(new_rotation, new_rotation_matrix); + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); - rotations[i] = new_rotation; - } + rotations[i] = new_rotation; + } + }); } static void align_rotations_on_component(GeometryComponent &component, @@ -144,30 +149,30 @@ static void align_rotations_on_component(GeometryComponent &component, const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *) node.storage; - OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( - "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - if (!rotation_attribute) { + OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>( + "rotation", ATTR_DOMAIN_POINT, {0, 0, 0}); + if (!rotations) { return; } - MutableSpan<float3> rotations = rotation_attribute->get_span<float3>(); - FloatReadAttribute factors = params.get_input_attribute<float>( + GVArray_Typed<float> factors = params.get_input_attribute<float>( "Factor", component, ATTR_DOMAIN_POINT, 1.0f); - Float3ReadAttribute vectors = params.get_input_attribute<float3>( + GVArray_Typed<float3> vectors = params.get_input_attribute<float3>( "Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1}); float3 local_main_axis{0, 0, 0}; local_main_axis[storage.axis] = 1; if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) { - align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations); + align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span()); } else { float3 local_pivot_axis{0, 0, 0}; local_pivot_axis[storage.pivot_axis - 1] = 1; - align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations); + align_rotations_fixed_pivot( + vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span()); } - rotation_attribute.apply_span_and_save(); + rotations.save(); } static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) @@ -183,6 +188,9 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc index 95fa24c8bac..21538db5455 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -112,10 +112,13 @@ template<> inline Color4f clamp_value(const Color4f val, const Color4f min, cons } template<typename T> -static void clamp_attribute(Span<T> read_span, MutableSpan<T> span, const T min, const T max) +static void clamp_attribute(const VArray<T> &inputs, + const MutableSpan<T> outputs, + const T min, + const T max) { - for (const int i : span.index_range()) { - span[i] = clamp_value<T>(read_span[i], min, max); + for (const int i : IndexRange(outputs.size())) { + outputs[i] = clamp_value<T>(inputs[i], min, max); } } @@ -123,13 +126,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef source_name, StringRef result_name) { - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); - if (source_attribute) { - return source_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); + if (source_info) { + return source_info->domain; } return ATTR_DOMAIN_POINT; } @@ -154,10 +157,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam const AttributeDomain domain = get_result_domain(component, attribute_name, result_name); const int operation = static_cast<int>(storage.operation); - ReadAttributePtr attribute_input = component.attribute_try_get_for_read( + GVArrayPtr attribute_input = component.attribute_try_get_for_read( attribute_name, domain, data_type); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, domain, data_type); if (!attribute_result) { @@ -169,8 +172,6 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam switch (data_type) { case CD_PROP_FLOAT3: { - Span<float3> read_span = attribute_input->get_span<float3>(); - MutableSpan<float3> span = attribute_result->get_span_for_write_only<float3>(); float3 min = params.get_input<float3>("Min"); float3 max = params.get_input<float3>("Max"); if (operation == NODE_CLAMP_RANGE) { @@ -184,38 +185,35 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.z, max.z); } } - clamp_attribute<float3>(read_span, span, min, max); + MutableSpan<float3> results = attribute_result.as_span<float3>(); + clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max); break; } case CD_PROP_FLOAT: { - Span<float> read_span = attribute_input->get_span<float>(); - MutableSpan<float> span = attribute_result->get_span_for_write_only<float>(); const float min = params.get_input<float>("Min_001"); const float max = params.get_input<float>("Max_001"); + MutableSpan<float> results = attribute_result.as_span<float>(); if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<float>(read_span, span, max, min); + clamp_attribute<float>(attribute_input->typed<float>(), results, max, min); } else { - clamp_attribute<float>(read_span, span, min, max); + clamp_attribute<float>(attribute_input->typed<float>(), results, min, max); } break; } case CD_PROP_INT32: { - Span<int> read_span = attribute_input->get_span<int>(); - MutableSpan<int> span = attribute_result->get_span_for_write_only<int>(); const int min = params.get_input<int>("Min_002"); const int max = params.get_input<int>("Max_002"); + MutableSpan<int> results = attribute_result.as_span<int>(); if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<int>(read_span, span, max, min); + clamp_attribute<int>(attribute_input->typed<int>(), results, max, min); } else { - clamp_attribute<int>(read_span, span, min, max); + clamp_attribute<int>(attribute_input->typed<int>(), results, min, max); } break; } case CD_PROP_COLOR: { - Span<Color4f> read_span = attribute_input->get_span<Color4f>(); - MutableSpan<Color4f> span = attribute_result->get_span_for_write_only<Color4f>(); Color4f min = params.get_input<Color4f>("Min_003"); Color4f max = params.get_input<Color4f>("Max_003"); if (operation == NODE_CLAMP_RANGE) { @@ -232,7 +230,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.a, max.a); } } - clamp_attribute<Color4f>(read_span, span, min, max); + MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); + clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max); break; } default: { @@ -241,7 +240,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam } } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_clamp_exec(GeoNodeExecParams params) @@ -256,6 +255,9 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index 2b913beb670..26ddb0da515 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "BKE_colorband.h" #include "UI_interface.h" @@ -47,15 +49,15 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the input attribute's domain if it exists. */ - ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name); - if (input_attribute) { - return input_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name); + if (source_info) { + return source_info->domain; } return ATTR_DOMAIN_POINT; @@ -71,27 +73,27 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon /* Always output a color attribute for now. We might want to allow users to customize. * Using the type of an existing attribute could work, but does not have a real benefit * currently. */ - const CustomDataType result_type = CD_PROP_COLOR; const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, result_type); + OutputAttribute_Typed<Color4f> attribute_result = + component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain); if (!attribute_result) { return; } - FloatReadAttribute attribute_in = component.attribute_get_for_read<float>( + GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, 0.0f); - Span<float> data_in = attribute_in.get_span(); - MutableSpan<Color4f> data_out = attribute_result->get_span_for_write_only<Color4f>(); + MutableSpan<Color4f> results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; - for (const int i : data_in.index_range()) { - BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]); - } + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]); + } + }); - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) @@ -106,6 +108,9 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); } + if (geometry_set.has<CurveComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); + } params.set_output("Geometry", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc index e9e07d34c17..d8c52d16f41 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc @@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -94,27 +94,25 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa } const AttributeDomain result_domain = get_result_domain(component, params, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> attribute_result = + component.attribute_try_get_for_output_only<float3>(result_name, result_domain); if (!attribute_result) { return; } - FloatReadAttribute attribute_x = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_x = params.get_input_attribute<float>( "X", component, result_domain, 0.0f); - FloatReadAttribute attribute_y = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_y = params.get_input_attribute<float>( "Y", component, result_domain, 0.0f); - FloatReadAttribute attribute_z = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_z = params.get_input_attribute<float>( "Z", component, result_domain, 0.0f); - MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>(); - for (const int i : results.index_range()) { + for (const int i : IndexRange(attribute_result->size())) { const float x = attribute_x[i]; const float y = attribute_y[i]; const float z = attribute_z[i]; - const float3 result = float3(x, y, z); - results[i] = result; + attribute_result->set(i, {x, y, z}); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params) @@ -129,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index fe4045c39a6..a2ff1668a06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -81,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage)); } -static void do_math_operation(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, +static void do_math_operation(const VArray<float> &input_a, + const VArray<float> &input_b, const FloatCompareOperation operation, MutableSpan<bool> span_result) { const int size = input_a.size(); - Span<float> span_a = input_a.get_span(); - Span<float> span_b = input_b.get_span(); - if (try_dispatch_float_math_fl_fl_to_bool( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { for (const int i : IndexRange(size)) { - const float a = span_a[i]; - const float b = span_b[i]; + const float a = input_a[i]; + const float b = input_b[i]; const bool out = math_function(a, b); span_result[i] = out; } @@ -107,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a, BLI_assert(false); } -static void do_equal_operation_float(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, +static void do_equal_operation_float(const VArray<float> &input_a, + const VArray<float> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -120,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a, } } -static void do_equal_operation_float3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, +static void do_equal_operation_float3(const VArray<float3> &input_a, + const VArray<float3> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -134,8 +131,8 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a, } } -static void do_equal_operation_color4f(const Color4fReadAttribute &input_a, - const Color4fReadAttribute &input_b, +static void do_equal_operation_color4f(const VArray<Color4f> &input_a, + const VArray<Color4f> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -148,8 +145,8 @@ static void do_equal_operation_color4f(const Color4fReadAttribute &input_a, } } -static void do_equal_operation_bool(const BooleanReadAttribute &input_a, - const BooleanReadAttribute &input_b, +static void do_equal_operation_bool(const VArray<bool> &input_a, + const VArray<bool> &input_b, const float UNUSED(threshold), MutableSpan<bool> span_result) { @@ -161,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a, } } -static void do_not_equal_operation_float(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, +static void do_not_equal_operation_float(const VArray<float> &input_a, + const VArray<float> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -174,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a, } } -static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, +static void do_not_equal_operation_float3(const VArray<float3> &input_a, + const VArray<float3> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -188,8 +185,8 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a, } } -static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a, - const Color4fReadAttribute &input_b, +static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a, + const VArray<Color4f> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -202,8 +199,8 @@ static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a, } } -static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a, - const BooleanReadAttribute &input_b, +static void do_not_equal_operation_bool(const VArray<bool> &input_a, + const VArray<bool> &input_b, const float UNUSED(threshold), MutableSpan<bool> span_result) { @@ -237,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -254,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx node_storage->operation); const std::string result_name = params.get_input<std::string>("Result"); - const CustomDataType result_type = CD_PROP_BOOL; const AttributeDomain result_domain = get_result_domain(component, params, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, result_type); + OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>( + result_name, result_domain); if (!attribute_result) { return; } const CustomDataType input_data_type = get_data_type(component, params, *node_storage); - ReadAttributePtr attribute_a = params.get_input_attribute( + GVArrayPtr attribute_a = params.get_input_attribute( "A", component, result_domain, input_data_type, nullptr); - ReadAttributePtr attribute_b = params.get_input_attribute( + GVArrayPtr attribute_b = params.get_input_attribute( "B", component, result_domain, input_data_type, nullptr); if (!attribute_a || !attribute_b) { @@ -275,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx return; } - MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>(); + MutableSpan<bool> result_span = attribute_result.as_span(); /* Use specific types for correct equality operations, but for other operations we use implicit * conversions and float comparison. In other words, the comparison is not element-wise. */ @@ -283,38 +279,47 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx const float threshold = params.get_input<float>("Threshold"); if (operation == NODE_FLOAT_COMPARE_EQUAL) { if (input_data_type == CD_PROP_FLOAT) { - do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_float( + attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span); } else if (input_data_type == CD_PROP_FLOAT3) { - do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_float3( + attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_color4f( + attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { - do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_bool( + attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span); } } else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) { if (input_data_type == CD_PROP_FLOAT) { - do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_float( + attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span); } else if (input_data_type == CD_PROP_FLOAT3) { - do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_float3( + attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_color4f( + attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { - do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_bool( + attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span); } } } else { - do_math_operation(*attribute_a, *attribute_b, operation, result_span); + do_math_operation( + attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_compare_exec(GeoNodeExecParams params) @@ -329,6 +334,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc index 19c3aaa9c85..7b40456b180 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc @@ -35,8 +35,10 @@ static void geo_node_attribute_convert_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE); } static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node) @@ -44,26 +46,27 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert), __func__); - data->data_type = CD_PROP_FLOAT; + data->data_type = CD_AUTO_FROM_NAME; data->domain = ATTR_DOMAIN_AUTO; node->storage = data; } namespace blender::nodes { -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef source_name, - StringRef result_name) +static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component, + const StringRef source_name, + const StringRef result_name) { - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return *result_info; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); - if (source_attribute) { - return source_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); + if (source_info) { + return *source_info; } - return ATTR_DOMAIN_POINT; + /* The node won't do anything in this case, but we still have to return a value. */ + return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL}; } static bool conversion_can_be_skipped(const GeometryComponent &component, @@ -75,14 +78,14 @@ static bool conversion_can_be_skipped(const GeometryComponent &component, if (source_name != result_name) { return false; } - ReadAttributePtr read_attribute = component.attribute_try_get_for_read(source_name); - if (!read_attribute) { + std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name); + if (!info) { return false; } - if (read_attribute->domain() != result_domain) { + if (info->domain != result_domain) { return false; } - if (read_attribute->cpp_type() != *bke::custom_data_type_to_cpp_type(result_type)) { + if (info->data_type != result_type) { return false; } return true; @@ -92,19 +95,20 @@ static void attribute_convert_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms, const StringRef source_name, const StringRef result_name, - const CustomDataType result_type, + const CustomDataType data_type, const AttributeDomain domain) { - const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? - get_result_domain( - component, source_name, result_name) : - domain; + const AttributeMetaData auto_info = get_result_domain_and_type( + component, source_name, result_name); + const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain; + const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type : + data_type; if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) { return; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + GVArrayPtr source_attribute = component.attribute_try_get_for_read( source_name, result_domain, result_type); if (!source_attribute) { params.error_message_add(NodeWarningType::Error, @@ -112,25 +116,22 @@ static void attribute_convert_calc(GeometryComponent &component, return; } - OutputAttributePtr result_attribute = component.attribute_try_get_for_output( + OutputAttribute result_attribute = component.attribute_try_get_for_output_only( result_name, result_domain, result_type); if (!result_attribute) { return; } - fn::GSpan source_span = source_attribute->get_span(); - fn::GMutableSpan result_span = result_attribute->get_span_for_write_only(); - if (source_span.is_empty() || result_span.is_empty()) { - return; - } + GVArray_GSpan source_span{*source_attribute}; + GMutableSpan result_span = result_attribute.as_span(); + BLI_assert(source_span.size() == result_span.size()); const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type); BLI_assert(cpp_type != nullptr); cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size()); - - result_attribute.apply_span_and_save(); + result_attribute.save(); } static void geo_node_attribute_convert_exec(GeoNodeExecParams params) @@ -166,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params) data_type, domain); } + if (geometry_set.has<CurveComponent>()) { + attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(), + params, + source_name, + result_name, + data_type, + domain); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index 7b4483a31a1..60522fd0f72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -68,13 +68,12 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node namespace blender::nodes { -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef attribute_name) +static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name); + if (result_info) { + return result_info->domain; } return ATTR_DOMAIN_POINT; } @@ -93,7 +92,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams get_result_domain(component, attribute_name) : domain; - OutputAttributePtr attribute = component.attribute_try_get_for_output( + OutputAttribute attribute = component.attribute_try_get_for_output_only( attribute_name, result_domain, data_type); if (!attribute) { return; @@ -102,38 +101,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams switch (data_type) { case CD_PROP_FLOAT: { const float value = params.get_input<float>("Value_001"); - MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_FLOAT3: { const float3 value = params.get_input<float3>("Value"); - MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_COLOR: { const Color4f value = params.get_input<Color4f>("Value_002"); - MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_BOOL: { const bool value = params.get_input<bool>("Value_003"); - MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_INT32: { const int value = params.get_input<int>("Value_004"); - MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>(); - attribute_span.fill(value); + attribute->fill(&value); + break; } default: break; } - attribute.apply_span_and_save(); + attribute.save(); } static void geo_node_attribute_fill_exec(GeoNodeExecParams params) @@ -148,6 +143,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc index fdbdadf90b6..40fe675bd6c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc @@ -15,6 +15,7 @@ */ #include "BLI_math_base_safe.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -192,8 +193,8 @@ static float map_smootherstep(const float value, return min_to + factor_mapped * (max_to - min_to); } -static void map_range_float(FloatReadAttribute attribute_input, - FloatWriteAttribute attribute_result, +static void map_range_float(const VArray<float> &attribute_input, + MutableSpan<float> results, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); @@ -204,33 +205,40 @@ static void map_range_float(FloatReadAttribute attribute_input, const float min_to = params.get_input<float>("To Min"); const float max_to = params.get_input<float>("To Max"); - Span<float> span = attribute_input.get_span(); - MutableSpan<float> result_span = attribute_result.get_span(); + VArray_Span<float> span{attribute_input}; switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { - for (int i : span.index_range()) { - result_span[i] = map_linear(span[i], min_from, max_from, min_to, max_to); - } + parallel_for(span.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_linear(span[i], min_from, max_from, min_to, max_to); + } + }); break; } case NODE_MAP_RANGE_STEPPED: { const float steps = params.get_input<float>("Steps"); - for (int i : span.index_range()) { - result_span[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps); + } + }); break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - for (int i : span.index_range()) { - result_span[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to); + } + }); break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - for (int i : span.index_range()) { - result_span[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to); + } + }); break; } } @@ -241,14 +249,16 @@ static void map_range_float(FloatReadAttribute attribute_input, const float clamp_min = min_to < max_to ? min_to : max_to; const float clamp_max = min_to < max_to ? max_to : min_to; - for (int i : result_span.index_range()) { - result_span[i] = std::clamp(result_span[i], clamp_min, clamp_max); - } + parallel_for(results.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + results[i] = std::clamp(results[i], clamp_min, clamp_max); + } + }); } } -static void map_range_float3(Float3ReadAttribute attribute_input, - Float3WriteAttribute attribute_result, +static void map_range_float3(const VArray<float3> &attribute_input, + const MutableSpan<float3> results, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); @@ -259,44 +269,51 @@ static void map_range_float3(Float3ReadAttribute attribute_input, const float3 min_to = params.get_input<float3>("To Min_001"); const float3 max_to = params.get_input<float3>("To Max_001"); - Span<float3> span = attribute_input.get_span(); - MutableSpan<float3> result_span = attribute_result.get_span(); + VArray_Span<float3> span{attribute_input}; switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { - for (int i : span.index_range()) { - result_span[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - result_span[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - result_span[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); + results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); + results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); + } + }); break; } case NODE_MAP_RANGE_STEPPED: { const float3 steps = params.get_input<float3>("Steps_001"); - for (int i : span.index_range()) { - result_span[i].x = map_stepped( - span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x); - result_span[i].y = map_stepped( - span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y); - result_span[i].z = map_stepped( - span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_stepped( + span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x); + results[i].y = map_stepped( + span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y); + results[i].z = map_stepped( + span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z); + } + }); break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - for (int i : span.index_range()) { - result_span[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - result_span[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - result_span[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); + results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); + results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); + } + }); break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - for (int i : span.index_range()) { - result_span[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - result_span[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - result_span[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); + results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); + results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); + } + }); break; } } @@ -313,8 +330,8 @@ static void map_range_float3(Float3ReadAttribute attribute_input, clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z; clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z; - for (int i : result_span.index_range()) { - clamp_v3_v3v3(result_span[i], clamp_min, clamp_max); + for (int i : results.index_range()) { + clamp_v3_v3v3(results[i], clamp_min, clamp_max); } } } @@ -323,13 +340,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef source_name, StringRef result_name) { - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); - if (source_attribute) { - return source_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); + if (source_info) { + return source_info->domain; } return ATTR_DOMAIN_POINT; } @@ -349,8 +366,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP const AttributeDomain domain = get_result_domain(component, input_name, result_name); - ReadAttributePtr attribute_input = component.attribute_try_get_for_read( - input_name, domain, data_type); + GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type); if (!attribute_input) { params.error_message_add(NodeWarningType::Error, @@ -358,7 +374,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP return; } - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, domain, data_type); if (!attribute_result) { params.error_message_add(NodeWarningType::Error, @@ -369,18 +385,19 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP switch (data_type) { case CD_PROP_FLOAT: { - map_range_float(*attribute_input, *attribute_result, params); + map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params); break; } case CD_PROP_FLOAT3: { - map_range_float3(*attribute_input, *attribute_result, params); + map_range_float3( + attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params); break; } default: BLI_assert_unreachable(); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_map_range_exec(GeoNodeExecParams params) @@ -393,6 +410,9 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index 5ee31e78be2..ce0ca31cc2b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -149,46 +151,52 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node operation_use_input_c(operation)); } -static void do_math_operation(Span<float> span_a, - Span<float> span_b, - Span<float> span_c, +static void do_math_operation(const VArray<float> &span_a, + const VArray<float> &span_b, + const VArray<float> &span_c, MutableSpan<float> span_result, const NodeMathOperation operation) { bool success = try_dispatch_float_math_fl_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(span_result.size())) { - span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); - } + parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) { + for (const int i : range) { + span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); + } + }); }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation(Span<float> span_a, - Span<float> span_b, +static void do_math_operation(const VArray<float> &span_a, + const VArray<float> &span_b, MutableSpan<float> span_result, const NodeMathOperation operation) { bool success = try_dispatch_float_math_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(span_result.size())) { - span_result[i] = math_function(span_a[i], span_b[i]); - } + parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + span_result[i] = math_function(span_a[i], span_b[i]); + } + }); }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation(Span<float> span_input, +static void do_math_operation(const VArray<float> &span_input, MutableSpan<float> span_result, const NodeMathOperation operation) { bool success = try_dispatch_float_math_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(span_result.size())) { - span_result[i] = math_function(span_input[i]); - } + parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + span_result[i] = math_function(span_input[i]); + } + }); }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); @@ -200,9 +208,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -224,56 +232,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP const std::string result_name = params.get_input<std::string>("Result"); /* The result type of this node is always float. */ - const CustomDataType result_type = CD_PROP_FLOAT; const AttributeDomain result_domain = get_result_domain( component, params, operation, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, result_type); + OutputAttribute_Typed<float> attribute_result = + component.attribute_try_get_for_output_only<float>(result_name, result_domain); if (!attribute_result) { return; } - ReadAttributePtr attribute_a = params.get_input_attribute( - "A", component, result_domain, result_type, nullptr); - if (!attribute_a) { - return; - } + GVArray_Typed<float> attribute_a = params.get_input_attribute<float>( + "A", component, result_domain, 0.0f); - /* Note that passing the data with `get_span<float>()` works + MutableSpan<float> result_span = attribute_result.as_span(); + + /* Note that passing the data with `get_internal_span<float>()` works * because the attributes were accessed with #CD_PROP_FLOAT. */ if (operation_use_input_b(operation)) { - ReadAttributePtr attribute_b = params.get_input_attribute( - "B", component, result_domain, result_type, nullptr); - if (!attribute_b) { - return; - } + GVArray_Typed<float> attribute_b = params.get_input_attribute<float>( + "B", component, result_domain, 0.0f); if (operation_use_input_c(operation)) { - ReadAttributePtr attribute_c = params.get_input_attribute( - "C", component, result_domain, result_type, nullptr); - if (!attribute_c) { - return; - } - do_math_operation(attribute_a->get_span<float>(), - attribute_b->get_span<float>(), - attribute_c->get_span<float>(), - attribute_result->get_span_for_write_only<float>(), - operation); + GVArray_Typed<float> attribute_c = params.get_input_attribute<float>( + "C", component, result_domain, 0.0f); + do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation); } else { - do_math_operation(attribute_a->get_span<float>(), - attribute_b->get_span<float>(), - attribute_result->get_span_for_write_only<float>(), - operation); + do_math_operation(attribute_a, attribute_b, result_span, operation); } } else { - do_math_operation(attribute_a->get_span<float>(), - attribute_result->get_span_for_write_only<float>(), - operation); + do_math_operation(attribute_a, result_span, operation); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_math_exec(GeoNodeExecParams params) @@ -288,6 +279,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index 9d8cd3dfa82..3e5326edbf6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "BKE_material.h" #include "DNA_material_types.h" @@ -58,72 +60,87 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), namespace blender::nodes { static void do_mix_operation_float(const int blend_mode, - const FloatReadAttribute &factors, - const FloatReadAttribute &inputs_a, - const FloatReadAttribute &inputs_b, - FloatWriteAttribute results) + const VArray<float> &factors, + const VArray<float> &inputs_a, + const VArray<float> &inputs_b, + VMutableArray<float> &results) { const int size = results.size(); - for (const int i : IndexRange(size)) { - const float factor = factors[i]; - float3 a{inputs_a[i]}; - const float3 b{inputs_b[i]}; - ramp_blend(blend_mode, a, factor, b); - const float result = a.x; - results.set(i, result); - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float factor = factors[i]; + float3 a{inputs_a[i]}; + const float3 b{inputs_b[i]}; + ramp_blend(blend_mode, a, factor, b); + const float result = a.x; + results.set(i, result); + } + }); } static void do_mix_operation_float3(const int blend_mode, - const FloatReadAttribute &factors, - const Float3ReadAttribute &inputs_a, - const Float3ReadAttribute &inputs_b, - Float3WriteAttribute results) + const VArray<float> &factors, + const VArray<float3> &inputs_a, + const VArray<float3> &inputs_b, + VMutableArray<float3> &results) { const int size = results.size(); - for (const int i : IndexRange(size)) { - const float factor = factors[i]; - float3 a = inputs_a[i]; - const float3 b = inputs_b[i]; - ramp_blend(blend_mode, a, factor, b); - results.set(i, a); - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float factor = factors[i]; + float3 a = inputs_a[i]; + const float3 b = inputs_b[i]; + ramp_blend(blend_mode, a, factor, b); + results.set(i, a); + } + }); } static void do_mix_operation_color4f(const int blend_mode, - const FloatReadAttribute &factors, - const Color4fReadAttribute &inputs_a, - const Color4fReadAttribute &inputs_b, - Color4fWriteAttribute results) + const VArray<float> &factors, + const VArray<Color4f> &inputs_a, + const VArray<Color4f> &inputs_b, + VMutableArray<Color4f> &results) { const int size = results.size(); - for (const int i : IndexRange(size)) { - const float factor = factors[i]; - Color4f a = inputs_a[i]; - const Color4f b = inputs_b[i]; - ramp_blend(blend_mode, a, factor, b); - results.set(i, a); - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float factor = factors[i]; + Color4f a = inputs_a[i]; + const Color4f b = inputs_b[i]; + ramp_blend(blend_mode, a, factor, b); + results.set(i, a); + } + }); } static void do_mix_operation(const CustomDataType result_type, int blend_mode, - const FloatReadAttribute &attribute_factor, - const ReadAttribute &attribute_a, - const ReadAttribute &attribute_b, - WriteAttribute &attribute_result) + const VArray<float> &attribute_factor, + const GVArray &attribute_a, + const GVArray &attribute_b, + GVMutableArray &attribute_result) { if (result_type == CD_PROP_FLOAT) { - do_mix_operation_float( - blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result); + do_mix_operation_float(blend_mode, + attribute_factor, + attribute_a.typed<float>(), + attribute_b.typed<float>(), + attribute_result.typed<float>()); } else if (result_type == CD_PROP_FLOAT3) { - do_mix_operation_float3( - blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result); + do_mix_operation_float3(blend_mode, + attribute_factor, + attribute_a.typed<float3>(), + attribute_b.typed<float3>(), + attribute_result.typed<float3>()); } else if (result_type == CD_PROP_COLOR) { - do_mix_operation_color4f( - blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result); + do_mix_operation_color4f(blend_mode, + attribute_factor, + attribute_a.typed<Color4f>(), + attribute_b.typed<Color4f>(), + attribute_result.typed<Color4f>()); } } @@ -132,9 +149,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -158,17 +175,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa const AttributeDomain result_domain = get_result_domain(component, params, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, result_domain, result_type); if (!attribute_result) { return; } - FloatReadAttribute attribute_factor = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>( "Factor", component, result_domain, 0.5f); - ReadAttributePtr attribute_a = params.get_input_attribute( + GVArrayPtr attribute_a = params.get_input_attribute( "A", component, result_domain, result_type, nullptr); - ReadAttributePtr attribute_b = params.get_input_attribute( + GVArrayPtr attribute_b = params.get_input_attribute( "B", component, result_domain, result_type, nullptr); do_mix_operation(result_type, @@ -192,6 +209,9 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc index f09a9bf056e..9c22b7fa87f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -62,7 +62,7 @@ namespace blender::nodes { static void proximity_calc(MutableSpan<float> distance_span, MutableSpan<float3> location_span, - Span<float3> positions, + const VArray<float3> &positions, BVHTreeFromMesh &tree_data_mesh, BVHTreeFromPointCloud &tree_data_pointcloud, const bool bvh_mesh_success, @@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component, const AttributeDomain result_domain = ATTR_DOMAIN_POINT; const std::string distance_attribute_name = params.get_input<std::string>("Distance"); - OutputAttributePtr distance_attribute = component.attribute_try_get_for_output( - distance_attribute_name, result_domain, CD_PROP_FLOAT); + OutputAttribute_Typed<float> distance_attribute = + component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain); const std::string location_attribute_name = params.get_input<std::string>("Position"); - OutputAttributePtr location_attribute = component.attribute_try_get_for_output( - location_attribute_name, result_domain, CD_PROP_FLOAT3); - - ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position"); - BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> location_attribute = + component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain); + ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position"); if (!position_attribute || (!distance_attribute && !location_attribute)) { return; } + BLI_assert(position_attribute.varray->type().is<float3>()); const bNode &node = params.node(); const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *) @@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component, tree_data_pointcloud); } - Span<float3> position_span = position_attribute->get_span<float3>(); - - MutableSpan<float> distance_span = distance_attribute ? - distance_attribute->get_span_for_write_only<float>() : - MutableSpan<float>(); - MutableSpan<float3> location_span = location_attribute ? - location_attribute->get_span_for_write_only<float3>() : - MutableSpan<float3>(); + GVArray_Typed<float3> positions{*position_attribute.varray}; + MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() : + MutableSpan<float>(); + MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() : + MutableSpan<float3>(); proximity_calc(distance_span, location_span, - position_span, + positions, tree_data_mesh, tree_data_pointcloud, bvh_mesh_success, @@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component, } if (distance_attribute) { - distance_attribute.apply_span_and_save(); + distance_attribute.save(); } if (location_attribute) { - location_attribute.apply_span_and_save(); + location_attribute.save(); } } @@ -257,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params) attribute_calc_proximity( geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params); } + if (geometry_set.has<CurveComponent>()) { + attribute_calc_proximity( + geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 28263287a10..286411b7d28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -16,6 +16,7 @@ #include "BLI_hash.h" #include "BLI_rand.hh" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -125,28 +126,36 @@ static void randomize_attribute(MutableSpan<T> span, /* The operations could be templated too, but it doesn't make the code much shorter. */ switch (operation) { case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = random_value; + } + }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] + random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = span[i] + random_value; + } + }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] - random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = span[i] - random_value; + } + }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] * random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = span[i] * random_value; + } + }); break; default: BLI_assert(false); @@ -161,10 +170,12 @@ static void randomize_attribute_bool(MutableSpan<bool> span, { BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE); UNUSED_VARS_NDEBUG(operation); - for (const int i : span.index_range()) { - const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f; - span[i] = random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f; + span[i] = random_value; + } + }); } Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, @@ -173,15 +184,17 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo const int domain_size = component.attribute_domain_size(domain); /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */ - ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain); + GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain); Array<uint32_t> hashes(domain_size); if (hash_attribute) { BLI_assert(hashes.size() == hash_attribute->size()); - const CPPType &cpp_type = hash_attribute->cpp_type(); - fn::GSpan items = hash_attribute->get_span(); - for (const int i : hashes.index_range()) { - hashes[i] = cpp_type.hash(items[i]); - } + const CPPType &cpp_type = hash_attribute->type(); + GVArray_GSpan items{*hash_attribute}; + parallel_for(hashes.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + hashes[i] = cpp_type.hash(items[i]); + } + }); } else { /* If there is no "id" attribute for per-point variation, just create it here. */ @@ -196,12 +209,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo static AttributeDomain get_result_domain(const GeometryComponent &component, const GeoNodeExecParams ¶ms, - StringRef attribute_name) + const StringRef name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name); + if (result_info) { + return result_info->domain; } /* Otherwise use the input domain chosen in the interface. */ @@ -228,15 +241,13 @@ static void randomize_attribute_on_component(GeometryComponent &component, const AttributeDomain domain = get_result_domain(component, params, attribute_name); - OutputAttributePtr attribute = component.attribute_try_get_for_output( + OutputAttribute attribute = component.attribute_try_get_for_output( attribute_name, domain, data_type); if (!attribute) { return; } - fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ? - attribute->get_span_for_write_only() : - attribute->get_span(); + GMutableSpan span = attribute.as_span(); Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain); @@ -269,8 +280,8 @@ static void randomize_attribute_on_component(GeometryComponent &component, } } - attribute.apply_span_and_save(); -} // namespace blender::nodes + attribute.save(); +} static void geo_node_random_attribute_exec(GeoNodeExecParams params) { @@ -304,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) operation, seed); } + if (geometry_set.has<CurveComponent>()) { + randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(), + params, + attribute_name, + data_type, + operation, + seed); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 837f0c3629a..e4f3230ebb9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) remove_attribute( geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names); } + if (geometry_set.has<CurveComponent>()) { + remove_attribute( + geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index d0b2595b5b9..59790e5e46c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -15,6 +15,7 @@ */ #include "BLI_compiler_attrs.h" +#include "BLI_task.hh" #include "DNA_texture_types.h" @@ -49,19 +50,19 @@ static void geo_node_attribute_sample_texture_layout(uiLayout *layout, namespace blender::nodes { static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef result_attribute_name, - StringRef map_attribute_name) + const StringRef result_name, + const StringRef map_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the name of the map attribute. */ - ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name); - if (map_attribute) { - return map_attribute->domain(); + std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name); + if (map_info) { + return map_info->domain; } /* The node won't execute in this case, but we still have to return a value. */ @@ -85,25 +86,28 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec const AttributeDomain result_domain = get_result_domain( component, result_attribute_name, mapping_name); - OutputAttributePtr attribute_out = component.attribute_try_get_for_output( - result_attribute_name, result_domain, CD_PROP_COLOR); + OutputAttribute_Typed<Color4f> attribute_out = + component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain); if (!attribute_out) { return; } - Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>( + GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>( mapping_name, result_domain, {0, 0, 0}); - MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>(); - for (const int i : IndexRange(mapping_attribute.size())) { - TexResult texture_result = {0}; - const float3 position = mapping_attribute[i]; - /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */ - const float3 remapped_position = position * 2.0f - float3(1.0f); - BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false); - colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta}; - } - attribute_out.apply_span_and_save(); + MutableSpan<Color4f> colors = attribute_out.as_span(); + parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { + for (const int i : range) { + TexResult texture_result = {0}; + const float3 position = mapping_attribute[i]; + /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */ + const float3 remapped_position = position * 2.0f - float3(1.0f); + BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false); + colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta}; + } + }); + + attribute_out.save(); } static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) @@ -118,6 +122,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc index 656dc51149e..137a72bb707 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc @@ -71,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa static AttributeDomain get_result_domain(const GeometryComponent &component, const GeoNodeExecParams ¶ms, - StringRef result_name_x, - StringRef result_name_y, - StringRef result_name_z) + const StringRef name_x, + const StringRef name_y, + const StringRef name_z) { /* Use the highest priority domain from any existing attribute outputs. */ Vector<AttributeDomain, 3> output_domains; - ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x); - ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y); - ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z); - if (attribute_x) { - output_domains.append(attribute_x->domain()); + std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x); + std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y); + std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z); + if (info_x) { + output_domains.append(info_x->domain); } - if (attribute_y) { - output_domains.append(attribute_y->domain()); + if (info_y) { + output_domains.append(info_y->domain); } - if (attribute_z) { - output_domains.append(attribute_z->domain()); + if (info_z) { + output_domains.append(info_z->domain); } if (output_domains.size() > 0) { return bke::attribute_domain_highest_priority(output_domains); @@ -107,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa } /* The node is only for float3 to float conversions. */ - const CustomDataType input_type = CD_PROP_FLOAT3; - const CustomDataType result_type = CD_PROP_FLOAT; const AttributeDomain result_domain = get_result_domain( component, params, result_name_x, result_name_y, result_name_z); - ReadAttributePtr attribute_input = params.get_input_attribute( - "Vector", component, result_domain, input_type, nullptr); - if (!attribute_input) { - return; - } - const Span<float3> input_span = attribute_input->get_span<float3>(); + GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>( + "Vector", component, result_domain, {0, 0, 0}); + VArray_Span<float3> input_span{*attribute_input}; - OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output( - result_name_x, result_domain, result_type); - OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output( - result_name_y, result_domain, result_type); - OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output( - result_name_z, result_domain, result_type); + OutputAttribute_Typed<float> attribute_result_x = + component.attribute_try_get_for_output_only<float>(result_name_x, result_domain); + OutputAttribute_Typed<float> attribute_result_y = + component.attribute_try_get_for_output_only<float>(result_name_y, result_domain); + OutputAttribute_Typed<float> attribute_result_z = + component.attribute_try_get_for_output_only<float>(result_name_z, result_domain); /* Only extract the components for the outputs with a given attribute. */ if (attribute_result_x) { - extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>()); - attribute_result_x.apply_span_and_save(); + extract_input(0, input_span, attribute_result_x.as_span()); + attribute_result_x.save(); } if (attribute_result_y) { - extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>()); - attribute_result_y.apply_span_and_save(); + extract_input(1, input_span, attribute_result_y.as_span()); + attribute_result_y.save(); } if (attribute_result_z) { - extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>()); - attribute_result_z.apply_span_and_save(); + extract_input(2, input_span, attribute_result_z.as_span()); + attribute_result_z.save(); } } @@ -153,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc new file mode 100644 index 00000000000..4b677dc5c82 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -0,0 +1,597 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_kdopbvh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_transfer_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_GEOMETRY, N_("Source Geometry")}, + {SOCK_STRING, N_("Source")}, + {SOCK_STRING, N_("Destination")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_transfer_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_transfer_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN( + sizeof(NodeGeometryAttributeTransfer), __func__); + data->domain = ATTR_DOMAIN_AUTO; + node->storage = data; +} + +static void get_result_domain_and_data_type(const GeometrySet &src_geometry, + const GeometryComponent &dst_component, + const StringRef attribute_name, + CustomDataType *r_data_type, + AttributeDomain *r_domain) +{ + Vector<CustomDataType> data_types; + Vector<AttributeDomain> domains; + + const PointCloudComponent *pointcloud_component = + src_geometry.get_component_for_read<PointCloudComponent>(); + if (pointcloud_component != nullptr) { + std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data( + attribute_name); + if (meta_data.has_value()) { + data_types.append(meta_data->data_type); + domains.append(meta_data->domain); + } + } + + const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>(); + if (mesh_component != nullptr) { + std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data( + attribute_name); + if (meta_data.has_value()) { + data_types.append(meta_data->data_type); + domains.append(meta_data->domain); + } + } + + *r_data_type = bke::attribute_data_type_highest_complexity(data_types); + + if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) { + *r_domain = ATTR_DOMAIN_POINT; + } + else { + *r_domain = bke::attribute_domain_highest_priority(domains); + } +} + +static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) +{ + /* This only updates a cache and can be considered to be logically const. */ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); + const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); + return {looptris, looptris_len}; +} + +static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray<float3> &positions, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty()); + BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty()); + BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty()); + + for (const int i : positions.index_range()) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + if (!r_indices.is_empty()) { + r_indices[i] = nearest.index; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + if (!r_positions.is_empty()) { + r_positions[i] = nearest.co; + } + } +} + +static void get_closest_pointcloud_points(const PointCloud &pointcloud, + const VArray<float3> &positions, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq) +{ + BLI_assert(positions.size() == r_indices.size()); + BLI_assert(pointcloud.totpoint > 0); + + BVHTreeFromPointCloud tree_data; + BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); + + for (const int i : positions.index_range()) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + r_indices[i] = nearest.index; + r_distances_sq[i] = nearest.dist_sq; + } + + free_bvhtree_from_pointcloud(&tree_data); +} + +static void get_closest_mesh_points(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_point_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totvert > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2); + get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_edges(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_edge_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totedge > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2); + get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_looptri_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_polygons(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_poly_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + + Array<int> looptri_indices(positions.size()); + get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions); + + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + for (const int i : positions.index_range()) { + const MLoopTri &looptri = looptris[looptri_indices[i]]; + r_poly_indices[i] = looptri.poly; + } +} + +/* The closest corner is defined to be the closest corner on the closest face. */ +static void get_closest_mesh_corners(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_corner_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totloop > 0); + Array<int> poly_indices(positions.size()); + get_closest_mesh_polygons(mesh, positions, poly_indices, {}, {}); + + for (const int i : positions.index_range()) { + const float3 position = positions[i]; + const int poly_index = poly_indices[i]; + const MPoly &poly = mesh.mpoly[poly_index]; + + /* Find the closest vertex in the polygon. */ + float min_distance_sq = FLT_MAX; + const MVert *closest_mvert; + int closest_loop_index = 0; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vertex_index = loop.v; + const MVert &mvert = mesh.mvert[vertex_index]; + const float distance_sq = float3::distance_squared(position, mvert.co); + if (distance_sq < min_distance_sq) { + min_distance_sq = distance_sq; + closest_loop_index = loop_index; + closest_mvert = &mvert; + } + } + if (!r_corner_indices.is_empty()) { + r_corner_indices[i] = closest_loop_index; + } + if (!r_positions.is_empty()) { + r_positions[i] = closest_mvert->co; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = min_distance_sq; + } + } +} + +static void get_barycentric_coords(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> positions, + const MutableSpan<float3> r_bary_coords) +{ + BLI_assert(r_bary_coords.size() == positions.size()); + BLI_assert(r_bary_coords.size() == looptri_indices.size()); + + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : r_bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + + interp_weights_tri_v3(r_bary_coords[i], + mesh.mvert[v0_index].co, + mesh.mvert[v1_index].co, + mesh.mvert[v2_index].co, + positions[i]); + } +} + +static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const VArray<float3> &dst_positions, + const AttributeDomain dst_domain, + const CustomDataType data_type, + const StringRef src_name, + const StringRef dst_name) +{ + const int tot_samples = dst_positions.size(); + const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>(); + if (component == nullptr) { + return; + } + const Mesh *mesh = component->get_for_read(); + if (mesh == nullptr) { + return; + } + if (mesh->totpoly == 0) { + return; + } + ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type); + if (!src_attribute) { + return; + } + + /* Find closest points on the mesh surface. */ + Array<int> looptri_indices(tot_samples); + Array<float3> positions(tot_samples); + get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); + + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + dst_name, dst_domain, data_type); + if (!dst_attribute) { + return; + } + GMutableSpan dst_span = dst_attribute.as_span(); + Array<float3> bary_coords; + + /* Compute barycentric coordinates only when they are needed. */ + if (src_attribute.domain != ATTR_DOMAIN_FACE) { + bary_coords.reinitialize(tot_samples); + get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords); + } + /* Interpolate the source attribute on the surface. */ + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: { + bke::mesh_surface_sample::sample_point_attribute( + *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); + break; + } + case ATTR_DOMAIN_FACE: { + bke::mesh_surface_sample::sample_face_attribute( + *mesh, looptri_indices, *src_attribute.varray, dst_span); + break; + } + case ATTR_DOMAIN_CORNER: { + bke::mesh_surface_sample::sample_corner_attribute( + *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); + break; + } + case ATTR_DOMAIN_EDGE: { + /* Not yet supported. */ + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + dst_attribute.save(); +} + +static void transfer_attribute_nearest(const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const VArray<float3> &dst_positions, + const AttributeDomain dst_domain, + const CustomDataType data_type, + const StringRef src_name, + const StringRef dst_name) +{ + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + /* Get pointcloud data from geometry. */ + const PointCloudComponent *pointcloud_component = + src_geometry.get_component_for_read<PointCloudComponent>(); + const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() : + nullptr; + + /* Get mesh data from geometry. */ + const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>(); + const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr; + + const int tot_samples = dst_positions.size(); + + Array<int> pointcloud_indices; + Array<float> pointcloud_distances_sq; + bool use_pointcloud = false; + + /* Depending on where what domain the source attribute lives, these indices are either vertex, + * corner, edge or polygon indices. */ + Array<int> mesh_indices; + Array<float> mesh_distances_sq; + bool use_mesh = false; + + /* If there is a pointcloud, find the closest points. */ + if (pointcloud != nullptr && pointcloud->totpoint > 0) { + if (pointcloud_component->attribute_exists(src_name)) { + use_pointcloud = true; + pointcloud_indices.reinitialize(tot_samples); + pointcloud_distances_sq.reinitialize(tot_samples); + get_closest_pointcloud_points( + *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq); + } + } + + /* If there is a mesh, find the closest mesh elements. */ + if (mesh != nullptr) { + ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name); + if (src_attribute) { + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: { + if (mesh->totvert > 0) { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + } + break; + } + case ATTR_DOMAIN_EDGE: { + if (mesh->totedge > 0) { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + } + break; + } + case ATTR_DOMAIN_FACE: { + if (mesh->totpoly > 0) { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + } + break; + } + case ATTR_DOMAIN_CORNER: { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + break; + } + default: { + break; + } + } + } + } + + if (!use_pointcloud && !use_mesh) { + return; + } + + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + dst_name, dst_domain, data_type); + if (!dst_attribute) { + return; + } + + /* Create a buffer for intermediate values. */ + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + + if (use_mesh && use_pointcloud) { + /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or + * a mesh element is closer to every point. */ + ReadAttributeLookup pointcloud_src_attribute = + pointcloud_component->attribute_try_get_for_read(src_name, data_type); + ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name, + data_type); + for (const int i : IndexRange(tot_samples)) { + if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) { + /* Pointcloud point is closer. */ + const int index = pointcloud_indices[i]; + pointcloud_src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + else { + /* Mesh element is closer. */ + const int index = mesh_indices[i]; + mesh_src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + } + } + else if (use_pointcloud) { + /* The source geometry only has a pointcloud. */ + ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read( + src_name, data_type); + for (const int i : IndexRange(tot_samples)) { + const int index = pointcloud_indices[i]; + src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + } + else if (use_mesh) { + /* The source geometry only has a mesh. */ + ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name, + data_type); + for (const int i : IndexRange(tot_samples)) { + const int index = mesh_indices[i]; + src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + } + + dst_attribute.save(); +} + +static void transfer_attribute(const GeoNodeExecParams ¶ms, + const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const StringRef src_name, + const StringRef dst_name) +{ + const NodeGeometryAttributeTransfer &storage = + *(const NodeGeometryAttributeTransfer *)params.node().storage; + const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode) + storage.mapping; + const AttributeDomain input_domain = (AttributeDomain)storage.domain; + + CustomDataType data_type; + AttributeDomain auto_domain; + get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain); + const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain : + input_domain; + + GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>( + "position", dst_domain, {0, 0, 0}); + + switch (mapping) { + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + transfer_attribute_nearest_face_interpolated( + src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + transfer_attribute_nearest( + src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); + break; + } + } +} + +static void geo_node_attribute_transfer_exec(GeoNodeExecParams params) +{ + GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry"); + const std::string src_attribute_name = params.extract_input<std::string>("Source"); + const std::string dst_attribute_name = params.extract_input<std::string>("Destination"); + + if (src_attribute_name.empty() || dst_attribute_name.empty()) { + params.set_output("Geometry", dst_geometry_set); + return; + } + + dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set); + src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set); + + if (dst_geometry_set.has<MeshComponent>()) { + transfer_attribute(params, + src_geometry_set, + dst_geometry_set.get_component_for_write<MeshComponent>(), + src_attribute_name, + dst_attribute_name); + } + if (dst_geometry_set.has<PointCloudComponent>()) { + transfer_attribute(params, + src_geometry_set, + dst_geometry_set.get_component_for_write<PointCloudComponent>(), + src_attribute_name, + dst_attribute_name); + } + + params.set_output("Geometry", dst_geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_transfer() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out); + node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init); + node_type_storage(&ntype, + "NodeGeometryAttributeTransfer", + node_free_standard_storage, + node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec; + ntype.draw_buttons = geo_node_attribute_transfer_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index 1ae095a27d2..8877af445f9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -15,6 +15,7 @@ */ #include "BLI_math_base_safe.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -168,196 +169,210 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod operation_use_input_c(operation)); } -static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a, + const VArray<float3> &input_b, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float3 out = math_function(a, b); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float3 out = math_function(a, b); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - const Float3ReadAttribute &input_c, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a, + const VArray<float3> &input_b, + const VArray<float3> &input_c, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - Span<float3> span_c = input_c.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VArray_Span<float3> span_c{input_c}; + VMutableArray_Span<float3> span_result{result}; bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float3 c = span_c[i]; - const float3 out = math_function(a, b, c); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float3 c = span_c[i]; + const float3 out = math_function(a, b, c); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - const FloatReadAttribute &input_c, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a, + const VArray<float3> &input_b, + const VArray<float> &input_c, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - Span<float> span_c = input_c.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VArray_Span<float> span_c{input_c}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float c = span_c[i]; - const float3 out = math_function(a, b, c); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float c = span_c[i]; + const float3 out = math_function(a, b, c); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - FloatWriteAttribute result, +static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a, + const VArray<float3> &input_b, + VMutableArray<float> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - MutableSpan<float> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VMutableArray_Span<float> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl3_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float out = math_function(a, b); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float out = math_function(a, b); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a, - const FloatReadAttribute &input_b, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a, + const VArray<float> &input_b, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float> span_b = input_b.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float> span_b{input_b}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float b = span_b[i]; - const float3 out = math_function(a, b); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float b = span_b[i]; + const float3 out = math_function(a, b); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a, - Float3WriteAttribute result, +static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 in = span_a[i]; - const float3 out = math_function(in); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 in = span_a[i]; + const float3 out = math_function(in); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a, - FloatWriteAttribute result, +static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a, + VMutableArray<float> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - MutableSpan<float> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VMutableArray_Span<float> span_result{result, false}; bool success = try_dispatch_float_math_fl3_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 in = span_a[i]; - const float out = math_function(in); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 in = span_a[i]; + const float out = math_function(in); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); @@ -370,9 +385,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name); if (result_attribute) { - return result_attribute->domain(); + return result_attribute.domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -406,13 +421,13 @@ static void attribute_vector_math_calc(GeometryComponent &component, const AttributeDomain result_domain = get_result_domain( component, params, operation, result_name); - ReadAttributePtr attribute_a = params.get_input_attribute( + GVArrayPtr attribute_a = params.get_input_attribute( "A", component, result_domain, read_type_a, nullptr); if (!attribute_a) { return; } - ReadAttributePtr attribute_b; - ReadAttributePtr attribute_c; + GVArrayPtr attribute_b; + GVArrayPtr attribute_c; if (use_input_b) { attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr); if (!attribute_b) { @@ -427,7 +442,7 @@ static void attribute_vector_math_calc(GeometryComponent &component, } /* Get result attribute first, in case it has to overwrite one of the existing attributes. */ - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, result_domain, result_type); if (!attribute_result) { return; @@ -445,17 +460,27 @@ static void attribute_vector_math_calc(GeometryComponent &component, case NODE_VECTOR_MATH_MODULO: case NODE_VECTOR_MATH_MINIMUM: case NODE_VECTOR_MATH_MAXIMUM: - do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation); + do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_result->typed<float3>(), + operation); break; case NODE_VECTOR_MATH_DOT_PRODUCT: case NODE_VECTOR_MATH_DISTANCE: - do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation); + do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_result->typed<float>(), + operation); break; case NODE_VECTOR_MATH_LENGTH: - do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation); + do_math_operation_fl3_to_fl( + attribute_a->typed<float3>(), attribute_result->typed<float>(), operation); break; case NODE_VECTOR_MATH_SCALE: - do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation); + do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float>(), + attribute_result->typed<float3>(), + operation); break; case NODE_VECTOR_MATH_NORMALIZE: case NODE_VECTOR_MATH_FLOOR: @@ -465,16 +490,23 @@ static void attribute_vector_math_calc(GeometryComponent &component, case NODE_VECTOR_MATH_SINE: case NODE_VECTOR_MATH_COSINE: case NODE_VECTOR_MATH_TANGENT: - do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation); + do_math_operation_fl3_to_fl3( + attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation); break; case NODE_VECTOR_MATH_WRAP: case NODE_VECTOR_MATH_FACEFORWARD: - do_math_operation_fl3_fl3_fl3_to_fl3( - *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation); + do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_c->typed<float3>(), + attribute_result->typed<float3>(), + operation); break; case NODE_VECTOR_MATH_REFRACT: - do_math_operation_fl3_fl3_fl_to_fl3( - *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation); + do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_c->typed<float>(), + attribute_result->typed<float3>(), + operation); break; } attribute_result.save(); @@ -493,6 +525,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params) attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 96455f080e7..b6fa4c0d48f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -39,15 +39,12 @@ static void compute_min_max_from_position_and_transform(const GeometryComponent float3 &r_min, float3 &r_max) { - ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position"); - if (!position_attribute) { - BLI_assert(component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0); - return; - } - Span<float3> positions = position_attribute->get_span<float3>(); + GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); for (const float4x4 &transform : transforms) { - for (const float3 &position : positions) { + for (const int i : positions.index_range()) { + const float3 position = positions[i]; const float3 transformed_position = transform * position; minmax_v3v3_v3(r_min, r_max, transformed_position); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index 0ad495aa4db..5800d46b70d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -58,10 +58,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) const bool transform_space_relative = (node_storage->transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_COLLECTION; - instance.data.collection = collection; - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); float transform_mat[4][4]; @@ -73,7 +69,9 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) mul_m4_m4_pre(transform_mat, self_object->imat); } - instances.add_instance(instance, transform_mat, -1); + + const int handle = instances.add_reference(*collection); + instances.add_instance(handle, transform_mat, -1); params.set_output("Geometry", geometry_set_out); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc new file mode 100644 index 00000000000..071504ad8df --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -0,0 +1,312 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_float4x4.hh" +#include "BLI_timeit.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_GEOMETRY, N_("Profile Curve")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void vert_extrude_to_mesh_data(const Spline &spline, + const float3 profile_vert, + MutableSpan<MVert> r_verts, + MutableSpan<MEdge> r_edges, + int &vert_offset, + int &edge_offset) +{ + Span<float3> positions = spline.evaluated_positions(); + + for (const int i : IndexRange(positions.size() - 1)) { + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = vert_offset + i; + edge.v2 = vert_offset + i + 1; + edge.flag = ME_LOOSEEDGE; + } + + if (spline.is_cyclic()) { + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = vert_offset; + edge.v2 = vert_offset + positions.size() - 1; + edge.flag = ME_LOOSEEDGE; + } + + for (const int i : positions.index_range()) { + MVert &vert = r_verts[vert_offset++]; + copy_v3_v3(vert.co, positions[i] + profile_vert); + } +} + +static void mark_edges_sharp(MutableSpan<MEdge> edges) +{ + for (MEdge &edge : edges) { + edge.flag |= ME_SHARP; + } +} + +static void spline_extrude_to_mesh_data(const Spline &spline, + const Spline &profile_spline, + MutableSpan<MVert> r_verts, + MutableSpan<MEdge> r_edges, + MutableSpan<MLoop> r_loops, + MutableSpan<MPoly> r_polys, + int &vert_offset, + int &edge_offset, + int &loop_offset, + int &poly_offset) +{ + const int spline_vert_len = spline.evaluated_points_size(); + const int spline_edge_len = spline.evaluated_edges_size(); + const int profile_vert_len = profile_spline.evaluated_points_size(); + const int profile_edge_len = profile_spline.evaluated_edges_size(); + if (spline_vert_len == 0) { + return; + } + + if (profile_vert_len == 1) { + vert_extrude_to_mesh_data(spline, + profile_spline.evaluated_positions()[0], + r_verts, + r_edges, + vert_offset, + edge_offset); + return; + } + + /* Add the edges running along the length of the curve, starting at each profile vertex. */ + const int spline_edges_start = edge_offset; + for (const int i_profile : IndexRange(profile_vert_len)) { + for (const int i_ring : IndexRange(spline_edge_len)) { + const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring; + + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = next_ring_vert_offset + i_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Add the edges running along each profile ring. */ + const int profile_edges_start = edge_offset; + for (const int i_ring : IndexRange(spline_vert_len)) { + const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + + for (const int i_profile : IndexRange(profile_edge_len)) { + const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; + + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = ring_vert_offset + i_next_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Calculate poly and corner indices. */ + for (const int i_ring : IndexRange(spline_edge_len)) { + const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring; + + const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring; + const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring; + + for (const int i_profile : IndexRange(profile_edge_len)) { + const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; + + const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile; + const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile; + + MPoly &poly = r_polys[poly_offset++]; + poly.loopstart = loop_offset; + poly.totloop = 4; + poly.flag = ME_SMOOTH; + + MLoop &loop_a = r_loops[loop_offset++]; + loop_a.v = ring_vert_offset + i_profile; + loop_a.e = ring_edge_start + i_profile; + MLoop &loop_b = r_loops[loop_offset++]; + loop_b.v = ring_vert_offset + i_next_profile; + loop_b.e = next_spline_edge_start + i_ring; + MLoop &loop_c = r_loops[loop_offset++]; + loop_c.v = next_ring_vert_offset + i_next_profile; + loop_c.e = next_ring_edge_offset + i_profile; + MLoop &loop_d = r_loops[loop_offset++]; + loop_d.v = next_ring_vert_offset + i_profile; + loop_d.e = spline_edge_start + i_ring; + } + } + + /* Calculate the positions of each profile ring profile along the spline. */ + Span<float3> positions = spline.evaluated_positions(); + Span<float3> tangents = spline.evaluated_tangents(); + Span<float3> normals = spline.evaluated_normals(); + Span<float3> profile_positions = profile_spline.evaluated_positions(); + + GVArray_Typed<float> radii{ + spline.interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(spline.radii()))}; + for (const int i_ring : IndexRange(spline_vert_len)) { + float4x4 point_matrix = float4x4::from_normalized_axis_data( + positions[i_ring], normals[i_ring], tangents[i_ring]); + + point_matrix.apply_scale(radii[i_ring]); + + for (const int i_profile : IndexRange(profile_vert_len)) { + MVert &vert = r_verts[vert_offset++]; + copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); + } + } + + /* Mark edge loops from sharp vector control points sharp. */ + if (profile_spline.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline); + Span<int> control_point_offsets = bezier_spline.control_point_offsets(); + for (const int i : control_point_offsets.index_range()) { + if (bezier_spline.point_is_sharp(i)) { + mark_edges_sharp(r_edges.slice( + spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len)); + } + } + } +} + +static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve) +{ + int profile_vert_total = 0; + int profile_edge_total = 0; + for (const SplinePtr &profile_spline : profile_curve.splines) { + profile_vert_total += profile_spline->evaluated_points_size(); + profile_edge_total += profile_spline->evaluated_edges_size(); + } + + int vert_total = 0; + int edge_total = 0; + int poly_total = 0; + for (const SplinePtr &spline : curve.splines) { + const int spline_vert_len = spline->evaluated_points_size(); + const int spline_edge_len = spline->evaluated_edges_size(); + vert_total += spline_vert_len * profile_vert_total; + poly_total += spline_edge_len * profile_edge_total; + + /* Add the ring edges, with one ring for every curve vertex, and the edge loops + * that run along the length of the curve, starting on the first profile. */ + edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len; + } + const int corner_total = poly_total * 4; + + if (vert_total == 0) { + return nullptr; + } + + Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + mesh->flag |= ME_AUTOSMOOTH; + mesh->smoothresh = DEG2RADF(180.0f); + + int vert_offset = 0; + int edge_offset = 0; + int loop_offset = 0; + int poly_offset = 0; + for (const SplinePtr &spline : curve.splines) { + for (const SplinePtr &profile_spline : profile_curve.splines) { + spline_extrude_to_mesh_data(*spline, + *profile_spline, + verts, + edges, + loops, + polys, + vert_offset, + edge_offset, + loop_offset, + poly_offset); + } + } + + BKE_mesh_calc_normals(mesh); + + return mesh; +} + +static CurveEval get_curve_single_vert() +{ + CurveEval curve; + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->add_point(float3(0), 0, 0.0f); + curve.splines.append(std::move(spline)); + + return curve; +} + +static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); + GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); + + curve_set = bke::geometry_set_realize_instances(curve_set); + profile_set = bke::geometry_set_realize_instances(profile_set); + + if (!curve_set.has_curve()) { + params.set_output("Mesh", GeometrySet()); + return; + } + + const CurveEval *profile_curve = profile_set.get_curve_for_read(); + + static const CurveEval vert_curve = get_curve_single_vert(); + + Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(), + (profile_curve == nullptr) ? vert_curve : *profile_curve); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_to_mesh() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec; + nodeRegisterType(&ntype); +} 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 77d2b0df2a9..68bb3614751 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -17,6 +17,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_pointcloud.h" +#include "BKE_spline.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -149,10 +150,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *> Vector<CustomDataType> data_types; Vector<AttributeDomain> domains; for (const GeometryComponent *component : components) { - ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name); + ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name); if (attribute) { - data_types.append(attribute->custom_data_type()); - domains.append(attribute->domain()); + data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type())); + domains.append(attribute.domain); } } @@ -164,7 +165,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, StringRef attribute_name, const CustomDataType data_type, const AttributeDomain domain, - fn::GMutableSpan dst_span) + GMutableSpan dst_span) { const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type); BLI_assert(cpp_type != nullptr); @@ -175,10 +176,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, if (domain_size == 0) { continue; } - ReadAttributePtr read_attribute = component->attribute_get_for_read( + GVArrayPtr read_attribute = component->attribute_get_for_read( attribute_name, domain, data_type, nullptr); - fn::GSpan src_span = read_attribute->get_span(); + GVArray_GSpan src_span{*read_attribute}; const void *src_buffer = src_span.data(); void *dst_buffer = dst_span[offset]; cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size); @@ -201,16 +202,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components, AttributeDomain domain; determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain); - OutputAttributePtr write_attribute = result.attribute_try_get_for_output( + OutputAttribute write_attribute = result.attribute_try_get_for_output_only( attribute_name, domain, data_type); - if (!write_attribute || - &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) || - write_attribute->domain() != domain) { + if (!write_attribute) { continue; } - fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); + GMutableSpan dst_span = write_attribute.as_span(); fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span); - write_attribute.apply_span_and_save(); + write_attribute.save(); } } @@ -244,13 +243,30 @@ static void join_components(Span<const PointCloudComponent *> src_components, Ge static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result) { InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); - for (const InstancesComponent *component : src_components) { - const int size = component->instances_amount(); - Span<InstancedData> instanced_data = component->instanced_data(); - Span<float4x4> transforms = component->transforms(); - Span<int> ids = component->ids(); - for (const int i : IndexRange(size)) { - dst_component.add_instance(instanced_data[i], transforms[i], ids[i]); + + int tot_instances = 0; + for (const InstancesComponent *src_component : src_components) { + tot_instances += src_component->instances_amount(); + } + dst_component.reserve(tot_instances); + + for (const InstancesComponent *src_component : src_components) { + Span<InstanceReference> src_references = src_component->references(); + Array<int> handle_map(src_references.size()); + for (const int src_handle : src_references.index_range()) { + handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]); + } + + Span<float4x4> src_transforms = src_component->instance_transforms(); + Span<int> src_ids = src_component->instance_ids(); + Span<int> src_reference_handles = src_component->instance_reference_handles(); + + for (const int i : src_transforms.index_range()) { + const int src_handle = src_reference_handles[i]; + const int dst_handle = handle_map[src_handle]; + const float4x4 &transform = src_transforms[i]; + const int id = src_ids[i]; + dst_component.add_instance(dst_handle, transform, id); } } } @@ -263,6 +279,40 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet UNUSED_VARS(src_components, dst_component); } +static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result) +{ + Vector<CurveComponent *> src_components; + for (GeometrySet &geometry_set : src_geometry_sets) { + if (geometry_set.has_curve()) { + /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy + * in the case where the input spline has no other users, because the splines can be + * moved from the source curve rather than copied from a read-only source. Retrieving + * the curve for write will make a copy only when it has a user elsewhere. */ + CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); + src_components.append(&component); + } + } + + if (src_components.size() == 0) { + return; + } + if (src_components.size() == 1) { + result.add(*src_components[0]); + return; + } + + CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); + CurveEval *dst_curve = new CurveEval(); + for (CurveComponent *component : src_components) { + CurveEval *src_curve = component->get_for_write(); + for (SplinePtr &spline : src_curve->splines) { + dst_curve->splines.append(std::move(spline)); + } + } + + dst_component.replace(dst_curve); +} + template<typename Component> static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result) { @@ -293,6 +343,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params) join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result); join_component_type<InstancesComponent>(geometry_sets, geometry_set_result); join_component_type<VolumeComponent>(geometry_sets, geometry_set_result); + join_curve_components(geometry_sets, geometry_set_result); params.set_output("Geometry", std::move(geometry_set_result)); } 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 761d5d6c388..2806472286e 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 @@ -194,9 +194,9 @@ static void calculate_uvs(Mesh *mesh, { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( - "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); - MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); Array<float2> circle(verts_num); float angle = 0.0f; @@ -271,7 +271,7 @@ static void calculate_uvs(Mesh *mesh, } } - uv_attribute.apply_span_and_save(); + uv_attribute.save(); } Mesh *create_cylinder_or_cone_mesh(const float radius_top, 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 14c57bc7135..5a4bab86421 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 @@ -44,9 +44,9 @@ static void calculate_uvs( { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( - "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); - MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x; const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y; @@ -56,7 +56,7 @@ static void calculate_uvs( uvs[i].y = (co.y + size_y * 0.5f) * dy; } - uv_attribute.apply_span_and_save(); + uv_attribute.save(); } static Mesh *create_grid_mesh(const int verts_x, 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 fd95cdc81f7..cc93e71a5dd 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 @@ -224,9 +224,9 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( - "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); - MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); int loop_index = 0; const float dy = 1.0f / rings; @@ -256,7 +256,7 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r uvs[loop_index++] = float2(segment / segments, 1.0f - dy); } - uv_attribute.apply_span_and_save(); + uv_attribute.save(); } static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings) diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index bd42b4c11d6..de099b8062f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -73,14 +73,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) if (object != self_object) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + const int handle = instances.add_reference(*object); if (transform_space_relative) { - instances.add_instance(object, transform); + instances.add_instance(handle, transform); } else { float unit_transform[4][4]; unit_m4(unit_transform); - instances.add_instance(object, unit_transform); + instances.add_instance(handle, unit_transform); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 74cca8a2f3c..772bd8a1080 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -28,6 +28,7 @@ #include "BKE_geometry_set_instances.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" #include "BKE_pointcloud.h" #include "UI_interface.h" @@ -91,7 +92,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) static void sample_mesh_surface(const Mesh &mesh, const float4x4 &transform, const float base_density, - const FloatReadAttribute *density_factors, + const VArray<float> *density_factors, const int seed, Vector<float3> &r_positions, Vector<float3> &r_bary_coords, @@ -113,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh, float looptri_density_factor = 1.0f; if (density_factors != nullptr) { - const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]); - const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]); - const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]); + const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop)); + const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop)); + const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop)); looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f; } const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); @@ -203,7 +204,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const Mesh &mesh, - const FloatReadAttribute &density_factors, + const VArray<float> &density_factors, Span<float3> bary_coords, Span<int> looptri_indices, MutableSpan<bool> elimination_mask) @@ -249,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m } } -template<typename T> -BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh, - const Span<float3> bary_coords, - const Span<int> looptri_indices, - const Span<T> data_in, - MutableSpan<T> data_out) -{ - BLI_assert(data_in.size() == mesh.totvert); - Span<MLoopTri> looptris = get_mesh_looptris(mesh); - - for (const int i : bary_coords.index_range()) { - const int looptri_index = looptri_indices[i]; - const MLoopTri &looptri = looptris[looptri_index]; - const float3 &bary_coord = bary_coords[i]; - - const int v0_index = mesh.mloop[looptri.tri[0]].v; - const int v1_index = mesh.mloop[looptri.tri[1]].v; - const int v2_index = mesh.mloop[looptri.tri[2]].v; - - const T &v0 = data_in[v0_index]; - const T &v1 = data_in[v1_index]; - const T &v2 = data_in[v2_index]; - - const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); - data_out[i] = interpolated_value; - } -} - -template<typename T> -BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh, - const Span<float3> bary_coords, - const Span<int> looptri_indices, - const Span<T> data_in, - MutableSpan<T> data_out) -{ - BLI_assert(data_in.size() == mesh.totloop); - Span<MLoopTri> looptris = get_mesh_looptris(mesh); - - for (const int i : bary_coords.index_range()) { - const int looptri_index = looptri_indices[i]; - const MLoopTri &looptri = looptris[looptri_index]; - const float3 &bary_coord = bary_coords[i]; - - const int loop_index_0 = looptri.tri[0]; - const int loop_index_1 = looptri.tri[1]; - const int loop_index_2 = looptri.tri[2]; - - const T &v0 = data_in[loop_index_0]; - const T &v1 = data_in[loop_index_1]; - const T &v2 = data_in[loop_index_2]; - - const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); - data_out[i] = interpolated_value; - } -} - -template<typename T> -BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh, - const Span<int> looptri_indices, - const Span<T> data_in, - MutableSpan<T> data_out) -{ - BLI_assert(data_in.size() == mesh.totpoly); - Span<MLoopTri> looptris = get_mesh_looptris(mesh); - - for (const int i : data_out.index_range()) { - const int looptri_index = looptri_indices[i]; - const MLoopTri &looptri = looptris[looptri_index]; - const int poly_index = looptri.poly; - data_out[i] = data_in[poly_index]; - } -} - -template<typename T> BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, Span<float3> bary_coords, Span<int> looptri_indices, const AttributeDomain source_domain, - Span<T> source_span, - MutableSpan<T> output_span) + const GVArray &source_data, + GMutableSpan output_data) { switch (source_domain) { case ATTR_DOMAIN_POINT: { - interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span); + bke::mesh_surface_sample::sample_point_attribute( + mesh, looptri_indices, bary_coords, source_data, output_data); break; } case ATTR_DOMAIN_CORNER: { - interpolate_attribute_corner<T>( - mesh, bary_coords, looptri_indices, source_span, output_span); + bke::mesh_surface_sample::sample_corner_attribute( + mesh, looptri_indices, bary_coords, source_data, output_data); break; } case ATTR_DOMAIN_FACE: { - interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span); + bke::mesh_surface_sample::sample_face_attribute( + mesh, looptri_indices, source_data, output_data); break; } default: { @@ -363,13 +292,13 @@ BLI_NOINLINE static void interpolate_existing_attributes( StringRef attribute_name = entry.key; const CustomDataType output_data_type = entry.value.data_type; /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ - OutputAttributePtr attribute_out = component.attribute_try_get_for_output( + OutputAttribute attribute_out = component.attribute_try_get_for_output_only( attribute_name, ATTR_DOMAIN_POINT, output_data_type); if (!attribute_out) { continue; } - fn::GMutableSpan out_span = attribute_out->get_span_for_write_only(); + GMutableSpan out_span = attribute_out.as_span(); int i_instance = 0; for (const GeometryInstanceGroup &set_group : set_groups) { @@ -377,47 +306,41 @@ BLI_NOINLINE static void interpolate_existing_attributes( const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *source_component.get_for_read(); - /* Use a dummy read without specifying a domain or data type in order to - * get the existing attribute's domain. Interpolation is done manually based - * on the bary coords in #interpolate_attribute. */ - ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read( + std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data( attribute_name); - if (!dummy_attribute) { + if (!attribute_info) { i_instance += set_group.transforms.size(); continue; } - const AttributeDomain source_domain = dummy_attribute->domain(); - ReadAttributePtr source_attribute = source_component.attribute_get_for_read( + const AttributeDomain source_domain = attribute_info->domain; + GVArrayPtr source_attribute = source_component.attribute_get_for_read( attribute_name, source_domain, output_data_type, nullptr); if (!source_attribute) { i_instance += set_group.transforms.size(); continue; } - fn::GSpan source_span = source_attribute->get_span(); + + for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { + const int offset = instance_start_offsets[i_instance]; + Span<float3> bary_coords = bary_coords_array[i_instance]; + Span<int> looptri_indices = looptri_indices_array[i_instance]; + + GMutableSpan instance_span = out_span.slice(offset, bary_coords.size()); + interpolate_attribute( + mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span); + + i_instance++; + } attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { using T = decltype(dummy); - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { - const int offset = instance_start_offsets[i_instance]; - Span<float3> bary_coords = bary_coords_array[i_instance]; - Span<int> looptri_indices = looptri_indices_array[i_instance]; - - MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size()); - interpolate_attribute<T>(mesh, - bary_coords, - looptri_indices, - source_domain, - source_span.typed<T>(), - instance_span); - - i_instance++; - } + GVArray_Span<T> source_span{*source_attribute}; }); } - attribute_out.apply_span_and_save(); + attribute_out.save(); } } @@ -427,16 +350,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> Span<Vector<float3>> bary_coords_array, Span<Vector<int>> looptri_indices_array) { - OutputAttributePtr id_attribute = component.attribute_try_get_for_output( - "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); - OutputAttributePtr normal_attribute = component.attribute_try_get_for_output( - "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( - "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + OutputAttribute_Typed<float3> normal_attribute = + component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT); + OutputAttribute_Typed<float3> rotation_attribute = + component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT); - MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>(); - MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>(); - MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>(); + MutableSpan<int> result_ids = id_attribute.as_span(); + MutableSpan<float3> result_normals = normal_attribute.as_span(); + MutableSpan<float3> result_rotations = rotation_attribute.as_span(); int i_instance = 0; for (const GeometryInstanceGroup &set_group : sets) { @@ -480,9 +403,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> } } - id_attribute.apply_span_and_save(); - normal_attribute.apply_span_and_save(); - rotation_attribute.apply_span_and_save(); + id_attribute.save(); + normal_attribute.save(); + rotation_attribute.save(); } BLI_NOINLINE static void add_remaining_point_attributes( @@ -520,7 +443,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( + GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>( density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); const Mesh &mesh = *component.get_for_read(); for (const float4x4 &transform : set_group.transforms) { @@ -530,7 +453,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, sample_mesh_surface(mesh, transform, density, - &density_factors, + &*density_factors, seed, positions, bary_coords, @@ -589,7 +512,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group const GeometrySet &set = set_group.geometry_set; const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *component.get_for_read(); - const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( + const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>( density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 20022e8d29d..7929a5a1546 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -65,8 +65,8 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection); } -static void get_instanced_data__object(const GeoNodeExecParams ¶ms, - MutableSpan<std::optional<InstancedData>> r_instances_data) +static void get_instance_references__object(const GeoNodeExecParams ¶ms, + MutableSpan<InstanceReference> r_references) { bke::PersistentObjectHandle object_handle = params.get_input<bke::PersistentObjectHandle>( "Object"); @@ -75,17 +75,13 @@ static void get_instanced_data__object(const GeoNodeExecParams ¶ms, object = nullptr; } if (object != nullptr) { - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_OBJECT; - instance.data.object = object; - r_instances_data.fill(instance); + r_references.fill(*object); } } -static void get_instanced_data__collection( - const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - MutableSpan<std::optional<InstancedData>> r_instances_data) +static void get_instance_references__collection(const GeoNodeExecParams ¶ms, + const GeometryComponent &component, + MutableSpan<InstanceReference> r_references) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; @@ -106,62 +102,51 @@ static void get_instanced_data__collection( const bool use_whole_collection = (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; if (use_whole_collection) { - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_COLLECTION; - instance.data.collection = collection; - r_instances_data.fill(instance); + r_references.fill(*collection); } else { - Vector<InstancedData> possible_instances; + Vector<InstanceReference> possible_references; /* Direct child objects are instanced as objects. */ LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - Object *object = cob->ob; - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_OBJECT; - instance.data.object = object; - possible_instances.append(instance); + possible_references.append(*cob->ob); } /* Direct child collections are instanced as collections. */ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - Collection *child_collection = child->collection; - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_COLLECTION; - instance.data.collection = child_collection; - possible_instances.append(instance); + possible_references.append(*child->collection); } - if (!possible_instances.is_empty()) { + if (!possible_references.is_empty()) { const int seed = params.get_input<int>("Seed"); Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT); - for (const int i : r_instances_data.index_range()) { - const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size(); - r_instances_data[i] = possible_instances[index]; + for (const int i : r_references.index_range()) { + const int index = BLI_hash_int_2d(ids[i], seed) % possible_references.size(); + r_references[i] = possible_references[index]; } } } } -static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - const int amount) +static Array<InstanceReference> get_instance_references(const GeoNodeExecParams ¶ms, + const GeometryComponent &component, + const int amount) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType) node_storage->instance_type; - Array<std::optional<InstancedData>> instances_data(amount); + Array<InstanceReference> references(amount); switch (type) { case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: { - get_instanced_data__object(params, instances_data); + get_instance_references__object(params, references); break; } case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: { - get_instanced_data__collection(params, component, instances_data); + get_instance_references__collection(params, component, references); break; } } - return instances_data; + return references; } static void add_instances_from_geometry_component(InstancesComponent &instances, @@ -171,21 +156,22 @@ static void add_instances_from_geometry_component(InstancesComponent &instances, const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_geometry.attribute_domain_size(domain); - Array<std::optional<InstancedData>> instances_data = get_instanced_data( - params, src_geometry, domain_size); + Array<InstanceReference> references = get_instance_references(params, src_geometry, domain_size); - Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>( + GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>( "position", domain, {0, 0, 0}); - Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>( + GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>( "rotation", domain, {0, 0, 0}); - Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>( + GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>( "scale", domain, {1, 1, 1}); - Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1); + GVArray_Typed<int> ids = src_geometry.attribute_get_for_read<int>("id", domain, -1); for (const int i : IndexRange(domain_size)) { - if (instances_data[i].has_value()) { + const InstanceReference &reference = references[i]; + if (reference.type() != InstanceReference::Type::None) { const float4x4 matrix = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); - instances.add_instance(*instances_data[i], matrix, ids[i]); + const int handle = instances.add_reference(reference); + instances.add_instance(handle, matrix, ids[i]); } } } @@ -209,6 +195,11 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params) instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + add_instances_from_geometry_component( + instances, *geometry_set.get_component_for_read<CurveComponent>(), params); + } + params.set_output("Geometry", std::move(geometry_set_out)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc index 2e7fce6ea30..73d489949ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc @@ -60,8 +60,8 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), namespace blender::nodes { static void point_rotate__axis_angle__object_space(const int domain_size, - const Float3ReadAttribute &axis, - const FloatReadAttribute &angles, + const VArray<float3> &axis, + const VArray<float> &angles, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -76,8 +76,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size, } static void point_rotate__axis_angle__point_space(const int domain_size, - const Float3ReadAttribute &axis, - const FloatReadAttribute &angles, + const VArray<float3> &axis, + const VArray<float> &angles, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -92,7 +92,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size, } static void point_rotate__euler__object_space(const int domain_size, - const Float3ReadAttribute &eulers, + const VArray<float3> &eulers, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -107,7 +107,7 @@ static void point_rotate__euler__object_space(const int domain_size, } static void point_rotate__euler__point_space(const int domain_size, - const Float3ReadAttribute &eulers, + const VArray<float3> &eulers, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -127,19 +127,19 @@ static void point_rotate_on_component(GeometryComponent &component, const bNode &node = params.node(); const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage; - OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( - "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> rotation_attribute = + component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0}); if (!rotation_attribute) { return; } - MutableSpan<float3> rotations = rotation_attribute->get_span<float3>(); + MutableSpan<float3> rotations = rotation_attribute.as_span(); const int domain_size = rotations.size(); if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) { - Float3ReadAttribute axis = params.get_input_attribute<float3>( + GVArray_Typed<float3> axis = params.get_input_attribute<float3>( "Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1}); - FloatReadAttribute angles = params.get_input_attribute<float>( + GVArray_Typed<float> angles = params.get_input_attribute<float>( "Angle", component, ATTR_DOMAIN_POINT, 0); if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { @@ -150,7 +150,7 @@ static void point_rotate_on_component(GeometryComponent &component, } } else { - Float3ReadAttribute eulers = params.get_input_attribute<float3>( + GVArray_Typed<float3> eulers = params.get_input_attribute<float3>( "Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { @@ -161,7 +161,7 @@ static void point_rotate_on_component(GeometryComponent &component, } } - rotation_attribute.apply_span_and_save(); + rotation_attribute.save(); } static void geo_node_point_rotate_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc index 113e2c620f6..2ef21fb085b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc @@ -50,7 +50,7 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co * for the factor. But for it's simpler to simply always use float3, since that is usually * expected anyway. */ static const float3 scale_default = float3(1.0f); - OutputAttributePtr scale_attribute = component.attribute_try_get_for_output( + OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output( "scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default); if (!scale_attribute) { return; @@ -63,27 +63,27 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT : CD_PROP_FLOAT3; - ReadAttributePtr attribute = params.get_input_attribute( + GVArrayPtr attribute = params.get_input_attribute( "Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr); if (!attribute) { return; } - MutableSpan<float3> scale_span = scale_attribute->get_span<float3>(); + MutableSpan<float3> scale_span = scale_attribute.as_span(); if (data_type == CD_PROP_FLOAT) { - Span<float> factors = attribute->get_span<float>(); + GVArray_Typed<float> factors{*attribute}; for (const int i : scale_span.index_range()) { scale_span[i] = scale_span[i] * factors[i]; } } else if (data_type == CD_PROP_FLOAT3) { - Span<float3> factors = attribute->get_span<float3>(); + GVArray_Typed<float3> factors{*attribute}; for (const int i : scale_span.index_range()) { scale_span[i] = scale_span[i] * factors[i]; } } - scale_attribute.apply_span_and_save(); + scale_attribute.save(); } static void geo_node_point_scale_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index 522dea4aa0e..6541d982629 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -58,27 +58,27 @@ static void copy_attributes_based_on_mask(const GeometryComponent &in_component, const bool invert) { for (const std::string &name : in_component.attribute_names()) { - ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name); - const CustomDataType data_type = attribute->custom_data_type(); + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name); + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); /* Only copy point attributes. Theoretically this could interpolate attributes on other * domains to the point domain, but that would conflict with attributes that are built-in * on other domains, which causes creating the attributes to fail. */ - if (attribute->domain() != ATTR_DOMAIN_POINT) { + if (attribute.domain != ATTR_DOMAIN_POINT) { continue; } - OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output( + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( name, ATTR_DOMAIN_POINT, data_type); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); - Span<T> span = attribute->get_span<T>(); - MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>(); + GVArray_Span<T> span{*attribute.varray}; + MutableSpan<T> out_span = result_attribute.as_span<T>(); copy_data_based_on_mask(span, masks, invert, out_span); }); - result_attribute.apply_span_and_save(); + result_attribute.save(); } } @@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component return; } - const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>( + const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>( mask_name, ATTR_DOMAIN_POINT, false); - Span<bool> masks = mask_attribute.get_span(); + VArray_Span<bool> masks{mask_attribute}; const int total = masks.count(!invert); if (total == 0) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc index 8c7387f7d9b..44203228899 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc @@ -42,24 +42,19 @@ namespace blender::nodes { static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component) { - OutputAttributePtr position_attribute = component.attribute_try_get_for_output( - "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> position_attribute = + component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0}); if (!position_attribute) { return; } - ReadAttributePtr attribute = params.get_input_attribute( - "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr); - if (!attribute) { - return; - } + GVArray_Typed<float3> attribute = params.get_input_attribute<float3>( + "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); - Span<float3> data = attribute->get_span<float3>(); - MutableSpan<float3> scale_span = position_attribute->get_span<float3>(); - for (const int i : scale_span.index_range()) { - scale_span[i] = scale_span[i] + data[i]; + for (const int i : IndexRange(attribute.size())) { + position_attribute->set(i, position_attribute->get(i) + attribute[i]); } - position_attribute.apply_span_and_save(); + position_attribute.save(); } static void geo_node_point_translate_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index a9eb136597e..e85f6aaee54 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -147,13 +147,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams ¶ms, Vector<float3> &r_positions, Vector<float> &r_radii) { - Float3ReadAttribute positions = component.attribute_get_for_read<float3>( + GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - FloatReadAttribute radii = params.get_input_attribute<float>( + GVArray_Typed<float> radii = params.get_input_attribute<float>( "Radius", component, ATTR_DOMAIN_POINT, 0.0f); - r_positions.extend(positions.get_span()); - r_radii.extend(radii.get_span()); + for (const int i : IndexRange(positions.size())) { + r_positions.append(positions[i]); + r_radii.append(radii[i]); + } } static void convert_to_grid_index_space(const float voxel_size, @@ -184,6 +186,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_ gather_point_data_from_component( params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii); } + if (geometry_set_in.has<CurveComponent>()) { + gather_point_data_from_component( + params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii); + } const float max_radius = *std::max_element(radii.begin(), radii.end()); const float voxel_size = compute_voxel_size(params, positions, max_radius); diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc new file mode 100644 index 00000000000..6736e963184 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -0,0 +1,163 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static bNodeSocketTemplate geo_node_switch_in[] = { + {SOCK_BOOLEAN, N_("Switch")}, + + {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_BOOLEAN, N_("A")}, + {SOCK_BOOLEAN, N_("B")}, + {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_STRING, N_("A")}, + {SOCK_STRING, N_("B")}, + {SOCK_GEOMETRY, N_("A")}, + {SOCK_GEOMETRY, N_("B")}, + {SOCK_OBJECT, N_("A")}, + {SOCK_OBJECT, N_("B")}, + {SOCK_COLLECTION, N_("A")}, + {SOCK_COLLECTION, N_("B")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_switch_out[] = { + {SOCK_FLOAT, N_("Output")}, + {SOCK_INT, N_("Output")}, + {SOCK_BOOLEAN, N_("Output")}, + {SOCK_VECTOR, N_("Output")}, + {SOCK_RGBA, N_("Output")}, + {SOCK_STRING, N_("Output")}, + {SOCK_GEOMETRY, N_("Output")}, + {SOCK_OBJECT, N_("Output")}, + {SOCK_COLLECTION, N_("Output")}, + {-1, ""}, +}; + +static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE); +} + +static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); + data->input_type = SOCK_FLOAT; + node->storage = data; +} + +namespace blender::nodes { + +template<typename T> +void output_input(GeoNodeExecParams ¶ms, + const bool input, + const StringRef input_suffix, + const StringRef output_identifier) +{ + if (input) { + params.set_output(output_identifier, params.extract_input<T>("B" + input_suffix)); + } + else { + params.set_output(output_identifier, params.extract_input<T>("A" + input_suffix)); + } +} + +static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeSwitch *node_storage = (NodeSwitch *)node->storage; + int index = 0; + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + nodeSetSocketAvailability( + socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type); + index++; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + nodeSetSocketAvailability(socket, + socket->type == (eNodeSocketDatatype)node_storage->input_type); + } +} + +static void geo_node_switch_exec(GeoNodeExecParams params) +{ + const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage; + const bool input = params.extract_input<bool>("Switch"); + switch ((eNodeSocketDatatype)storage.input_type) { + case SOCK_FLOAT: { + output_input<float>(params, input, "", "Output"); + break; + } + case SOCK_INT: { + output_input<int>(params, input, "_001", "Output_001"); + break; + } + case SOCK_BOOLEAN: { + output_input<bool>(params, input, "_002", "Output_002"); + break; + } + case SOCK_VECTOR: { + output_input<float3>(params, input, "_003", "Output_003"); + break; + } + case SOCK_RGBA: { + output_input<Color4f>(params, input, "_004", "Output_004"); + break; + } + case SOCK_STRING: { + output_input<std::string>(params, input, "_005", "Output_005"); + break; + } + case SOCK_GEOMETRY: { + output_input<GeometrySet>(params, input, "_006", "Output_006"); + break; + } + case SOCK_OBJECT: { + output_input<bke::PersistentObjectHandle>(params, input, "_007", "Output_007"); + break; + } + case SOCK_COLLECTION: { + output_input<bke::PersistentCollectionHandle>(params, input, "_008", "Output_008"); + break; + } + default: + BLI_assert_unreachable(); + break; + } +} + +} // namespace blender::nodes + +void register_node_type_geo_switch() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); + node_type_init(&ntype, geo_node_switch_init); + node_type_update(&ntype, blender::nodes::geo_node_switch_update); + node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec; + ntype.draw_buttons = geo_node_switch_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index d54982d16c2..ce52dc90668 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -24,6 +24,7 @@ #include "DNA_volume_types.h" #include "BKE_mesh.h" +#include "BKE_spline.hh" #include "BKE_volume.h" #include "DEG_depsgraph_query.h" @@ -100,7 +101,7 @@ static void transform_instances(InstancesComponent &instances, const float3 rotation, const float3 scale) { - MutableSpan<float4x4> transforms = instances.transforms(); + MutableSpan<float4x4> transforms = instances.instance_transforms(); /* Use only translation if rotation and scale don't apply. */ if (use_translate(rotation, scale)) { @@ -152,6 +153,21 @@ static void transform_volume(Volume *volume, #endif } +static void transform_curve(CurveEval &curve, + const float3 translation, + const float3 rotation, + const float3 scale) +{ + + if (use_translate(rotation, scale)) { + curve.translate(translation); + } + else { + const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); + curve.transform(matrix); + } +} + static void geo_node_transform_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -179,6 +195,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params) transform_volume(volume, translation, rotation, scale, params); } + if (geometry_set.has_curve()) { + CurveEval *curve = geometry_set.get_curve_for_write(); + transform_curve(*curve, translation, rotation, scale); + } + params.set_output("Geometry", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a4fb99a988e..73a702c753a 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -22,6 +22,7 @@ #include "NOD_geometry_exec.hh" #include "NOD_type_callbacks.hh" +#include "NOD_type_conversions.hh" #include "node_geometry_util.hh" @@ -29,22 +30,22 @@ namespace blender::nodes { void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const { - bNodeTree *btree_cow = node_->btree(); + bNodeTree *btree_cow = provider_->dnode->btree(); BLI_assert(btree_cow != nullptr); if (btree_cow == nullptr) { return; } bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow); - const NodeTreeEvaluationContext context(*self_object_, *modifier_); + const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier); BKE_nodetree_error_message_add( - *btree_original, context, *node_->bnode(), type, std::move(message)); + *btree_original, context, *provider_->dnode->bnode(), type, std::move(message)); } const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { - for (const InputSocketRef *socket : node_->inputs()) { + for (const InputSocketRef *socket : provider_->dnode->inputs()) { if (socket->is_available() && socket->name() == name) { return socket->bsocket(); } @@ -53,22 +54,29 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name return nullptr; } -ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const +GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const CustomDataType type, + const void *default_value) const { const bNodeSocket *found_socket = this->find_available_socket(name); BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); + const int64_t domain_size = component.attribute_domain_size(domain); + + if (default_value == nullptr) { + default_value = cpp_type->default_value(); + } + if (found_socket == nullptr) { - return component.attribute_get_constant_for_read(domain, type, default_value); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); /* Try getting the attribute without the default value. */ - ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type); + GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, type); if (attribute) { return attribute; } @@ -80,25 +88,29 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, this->error_message_add(NodeWarningType::Error, TIP_("No attribute with name \"") + name + "\""); } - return component.attribute_get_constant_for_read(domain, type, default_value); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } + const DataTypeConversions &conversions = get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); - return component.attribute_get_constant_for_read_converted( - domain, CD_PROP_FLOAT, type, &value); + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); - return component.attribute_get_constant_for_read_converted( - domain, CD_PROP_FLOAT3, type, &value); + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_RGBA) { const Color4f value = this->get_input<Color4f>(found_socket->identifier); - return component.attribute_get_constant_for_read_converted( - domain, CD_PROP_COLOR, type, &value); + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized(CPPType::get<Color4f>(), *cpp_type, &value, buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } BLI_assert(false); - return component.attribute_get_constant_for_read(domain, type, default_value); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } CustomDataType GeoNodeExecParams::get_input_attribute_data_type( @@ -114,11 +126,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type( if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); - ReadAttributePtr attribute = component.attribute_try_get_for_read(name); - if (!attribute) { - return default_type; + std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name); + if (info) { + return info->data_type; } - return attribute->custom_data_type(); + return default_type; } if (found_socket->type == SOCK_FLOAT) { return CD_PROP_FLOAT; @@ -157,9 +169,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); - ReadAttributePtr attribute = component.attribute_try_get_for_read(name); - if (attribute) { - input_domains.append(attribute->domain()); + std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name); + if (info) { + input_domains.append(info->domain); } } } @@ -171,11 +183,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( return default_domain; } -void GeoNodeExecParams::check_extract_input(StringRef identifier, - const CPPType *requested_type) const +void GeoNodeExecParams::check_input_access(StringRef identifier, + const CPPType *requested_type) const { bNodeSocket *found_socket = nullptr; - for (const InputSocketRef *socket : node_->inputs()) { + for (const InputSocketRef *socket : provider_->dnode->inputs()) { if (socket->identifier() == identifier) { found_socket = socket->bsocket(); break; @@ -185,39 +197,39 @@ void GeoNodeExecParams::check_extract_input(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 InputSocketRef *socket : node_->inputs()) { + for (const InputSocketRef *socket : provider_->dnode->inputs()) { if (socket->is_available()) { std::cout << "'" << socket->identifier() << "', "; } } std::cout << "\n"; - BLI_assert(false); + BLI_assert_unreachable(); } else if (found_socket->flag & SOCK_UNAVAIL) { std::cout << "The socket corresponding to the identifier '" << identifier << "' is disabled.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } - else if (!input_values_.contains(identifier)) { + 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(false); + BLI_assert_unreachable(); } else if (requested_type != nullptr) { const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); if (*requested_type != expected_type) { std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } } } -void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const +void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const { bNodeSocket *found_socket = nullptr; - for (const OutputSocketRef *socket : node_->outputs()) { + for (const OutputSocketRef *socket : provider_->dnode->outputs()) { if (socket->identifier() == identifier) { found_socket = socket->bsocket(); break; @@ -227,29 +239,29 @@ void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &va if (found_socket == nullptr) { std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n"; std::cout << "Possible identifiers are: "; - for (const OutputSocketRef *socket : node_->outputs()) { + for (const OutputSocketRef *socket : provider_->dnode->outputs()) { if (socket->is_available()) { std::cout << "'" << socket->identifier() << "', "; } } std::cout << "\n"; - BLI_assert(false); + BLI_assert_unreachable(); } else if (found_socket->flag & SOCK_UNAVAIL) { std::cout << "The socket corresponding to the identifier '" << identifier << "' is disabled.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } - else if (output_values_.contains(identifier)) { + else if (!provider_->can_set_output(identifier)) { std::cout << "The identifier '" << identifier << "' has been set already.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } else { const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); if (value_type != expected_type) { std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } } } diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index b583523da98..5b5fe183823 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -294,7 +294,6 @@ void node_socket_init_default_value(bNodeSocket *sock) sock->default_value = dval; break; - break; } } } diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc index 1c1b7c7feb5..63f7b8a9ee8 100644 --- a/source/blender/nodes/intern/type_conversions.cc +++ b/source/blender/nodes/intern/type_conversions.cc @@ -24,6 +24,9 @@ namespace blender::nodes { +using fn::GVArrayPtr; +using fn::GVMutableArray; +using fn::GVMutableArrayPtr; using fn::MFDataType; template<typename From, typename To, To (*ConversionF)(const From &)> @@ -227,6 +230,11 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type, const void *from_value, void *to_value) const { + if (from_type == to_type) { + from_type.copy_to_uninitialized(from_value, to_value); + return; + } + const ConversionFunctions *functions = this->get_conversion_functions( MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type)); BLI_assert(functions != nullptr); @@ -234,4 +242,108 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type, functions->convert_single_to_uninitialized(from_value, to_value); } +class GVArray_For_ConvertedGVArray : public GVArray { + private: + GVArrayPtr varray_; + const CPPType &from_type_; + ConversionFunctions old_to_new_conversions_; + + public: + GVArray_For_ConvertedGVArray(GVArrayPtr varray, + const CPPType &to_type, + const DataTypeConversions &conversions) + : GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type()) + { + old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type); + } + + private: + void get_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_initialized(buffer, r_value); + from_type_.destruct(buffer); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value); + from_type_.destruct(buffer); + } +}; + +class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray { + private: + GVMutableArrayPtr varray_; + const CPPType &from_type_; + ConversionFunctions old_to_new_conversions_; + ConversionFunctions new_to_old_conversions_; + + public: + GVMutableArray_For_ConvertedGVMutableArray(GVMutableArrayPtr varray, + const CPPType &to_type, + const DataTypeConversions &conversions) + : GVMutableArray(to_type, varray->size()), + varray_(std::move(varray)), + from_type_(varray_->type()) + { + old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type); + new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_); + } + + private: + void get_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_initialized(buffer, r_value); + from_type_.destruct(buffer); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value); + from_type_.destruct(buffer); + } + + void set_by_move_impl(const int64_t index, void *value) override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + new_to_old_conversions_.convert_single_to_uninitialized(value, buffer); + varray_->set_by_relocate(index, buffer); + } +}; + +fn::GVArrayPtr DataTypeConversions::try_convert(fn::GVArrayPtr varray, + const CPPType &to_type) const +{ + const CPPType &from_type = varray->type(); + if (from_type == to_type) { + return varray; + } + if (!this->is_convertible(from_type, to_type)) { + return {}; + } + return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this); +} + +fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr varray, + const CPPType &to_type) const +{ + const CPPType &from_type = varray->type(); + if (from_type == to_type) { + return varray; + } + if (!this->is_convertible(from_type, to_type)) { + return {}; + } + return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>( + std::move(varray), to_type, *this); +} + } // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 7b52b525541..25e4385de96 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -184,6 +184,12 @@ static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) return true; } +static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER); +} + bNodeTreeType *ntreeType_Shader; void register_node_tree_type_sh(void) @@ -205,6 +211,7 @@ void register_node_tree_type_sh(void) tt->poll = shader_tree_poll; tt->get_from_context = shader_get_from_context; tt->validate_link = shader_validate_link; + tt->valid_socket_type = shader_node_tree_socket_type_valid; tt->rna_ext.srna = &RNA_ShaderNodeTree; diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 48027dc847b..2ae722e3cd8 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -152,6 +152,12 @@ static void update(bNodeTree *ntree) } } +static bool texture_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); +} + bNodeTreeType *ntreeType_Texture; void register_node_tree_type_tex(void) @@ -171,6 +177,7 @@ void register_node_tree_type_tex(void) tt->local_sync = local_sync; tt->local_merge = local_merge; tt->get_from_context = texture_get_from_context; + tt->valid_socket_type = texture_node_tree_socket_type_valid; tt->rna_ext.srna = &RNA_TextureNodeTree; diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index d0965e83e33..e36e3b42617 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -37,17 +37,90 @@ #include "gpu_py_buffer.h" -// #define PYGPU_BUFFER_PROTOCOL +//#define PYGPU_BUFFER_PROTOCOL +#define MAX_DIMENSIONS 64 /* -------------------------------------------------------------------- */ /** \name Utility Functions * \{ */ -static bool pygpu_buffer_dimensions_compare(int ndim, - const Py_ssize_t *shape_a, - const Py_ssize_t *shape_b) +static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len) +{ + Py_ssize_t tot = shape[0]; + for (int i = 1; i < shape_len; i++) { + tot *= shape[i]; + } + + return tot; +} + +static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a, + const Py_ssize_t shape_a_len, + const Py_ssize_t *shape_b, + const Py_ssize_t shape_b_len) +{ + if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) != + pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len)) { + PyErr_Format(PyExc_BufferError, "array size does not match"); + return false; + } + + return true; +} + +static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj, + Py_ssize_t r_shape[MAX_DIMENSIONS], + Py_ssize_t *r_shape_len) { - return (bool)memcmp(shape_a, shape_b, ndim * sizeof(Py_ssize_t)); + Py_ssize_t shape_len = 0; + if (PyLong_Check(shape_obj)) { + shape_len = 1; + if (((r_shape[0] = PyLong_AsLong(shape_obj)) < 1)) { + PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); + return false; + } + } + else if (PySequence_Check(shape_obj)) { + shape_len = PySequence_Size(shape_obj); + if (shape_len > MAX_DIMENSIONS) { + PyErr_SetString(PyExc_AttributeError, + "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS)); + return false; + } + if (shape_len < 1) { + PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension"); + return false; + } + + for (int i = 0; i < shape_len; i++) { + PyObject *ob = PySequence_GetItem(shape_obj, i); + if (!PyLong_Check(ob)) { + PyErr_Format(PyExc_TypeError, + "invalid dimension %i, expected an int, not a %.200s", + i, + Py_TYPE(ob)->tp_name); + Py_DECREF(ob); + return false; + } + + r_shape[i] = PyLong_AsLong(ob); + Py_DECREF(ob); + + if (r_shape[i] < 1) { + PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); + return false; + } + } + } + else { + PyErr_Format(PyExc_TypeError, + "invalid second argument argument expected a sequence " + "or an int, not a %.200s", + Py_TYPE(shape_obj)->tp_name); + } + + *r_shape_len = shape_len; + return true; } static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format) @@ -174,7 +247,7 @@ static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self) return list; } -static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg)) +static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void *UNUSED(arg)) { PyObject *list = PyList_New(self->shape_len); int i; @@ -186,6 +259,30 @@ static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg)) return list; } +static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void *UNUSED(type)) +{ + Py_ssize_t shape[MAX_DIMENSIONS]; + Py_ssize_t shape_len = 0; + + if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) { + return -1; + } + + if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) { + return -1; + } + + size_t size = shape_len * sizeof(*self->shape); + if (shape_len != self->shape_len) { + MEM_freeN(self->shape); + self->shape = MEM_mallocN(size, __func__); + } + + self->shape_len = shape_len; + memcpy(self->shape, shape, size); + return 0; +} + static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg) { Py_VISIT(self->parent); @@ -280,14 +377,13 @@ static int pygpu_buffer_ass_slice(BPyGPUBuffer *self, return err; } -#define MAX_DIMENSIONS 64 static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { PyObject *length_ob, *init = NULL; BPyGPUBuffer *buffer = NULL; Py_ssize_t shape[MAX_DIMENSIONS]; - Py_ssize_t i, shape_len = 0; + Py_ssize_t shape_len = 0; if (kwds && PyDict_Size(kwds)) { PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args"); @@ -300,49 +396,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args return NULL; } - if (PyLong_Check(length_ob)) { - shape_len = 1; - if (((shape[0] = PyLong_AsLong(length_ob)) < 1)) { - PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); - return NULL; - } - } - else if (PySequence_Check(length_ob)) { - shape_len = PySequence_Size(length_ob); - if (shape_len > MAX_DIMENSIONS) { - PyErr_SetString(PyExc_AttributeError, - "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS)); - return NULL; - } - if (shape_len < 1) { - PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension"); - return NULL; - } - - for (i = 0; i < shape_len; i++) { - PyObject *ob = PySequence_GetItem(length_ob, i); - if (!PyLong_Check(ob)) { - PyErr_Format(PyExc_TypeError, - "invalid dimension %i, expected an int, not a %.200s", - i, - Py_TYPE(ob)->tp_name); - Py_DECREF(ob); - return NULL; - } - shape[i] = PyLong_AsLong(ob); - Py_DECREF(ob); - - if (shape[i] < 1) { - PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); - return NULL; - } - } - } - else { - PyErr_Format(PyExc_TypeError, - "invalid second argument argument expected a sequence " - "or an int, not a %.200s", - Py_TYPE(length_ob)->tp_name); + if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) { return NULL; } @@ -354,11 +408,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args return NULL; } - if (shape_len != pybuffer.ndim || - !pygpu_buffer_dimensions_compare(shape_len, shape, pybuffer.shape)) { - PyErr_Format(PyExc_TypeError, "array size does not match"); - } - else { + if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) { buffer = pygpu_buffer_make_from_data( init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf); } @@ -518,7 +568,11 @@ static PyMethodDef pygpu_buffer__tp_methods[] = { }; static PyGetSetDef pygpu_buffer_getseters[] = { - {"dimensions", (getter)pygpu_buffer_dimensions, NULL, NULL, NULL}, + {"dimensions", + (getter)pygpu_buffer_dimensions_get, + (setter)pygpu_buffer_dimensions_set, + NULL, + NULL}, {NULL, NULL, NULL, NULL, NULL}, }; @@ -625,13 +679,7 @@ static size_t pygpu_buffer_calc_size(const int format, const int shape_len, const Py_ssize_t *shape) { - size_t r_size = GPU_texture_dataformat_size(format); - - for (int i = 0; i < shape_len; i++) { - r_size *= shape[i]; - } - - return r_size; + return pygpu_buffer_dimensions_tot_elem(shape, shape_len) * GPU_texture_dataformat_size(format); } size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer) diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c index bc393aaafa4..7f64cb90a97 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.c +++ b/source/blender/python/gpu/gpu_py_framebuffer.c @@ -37,6 +37,8 @@ #include "gpu_py.h" #include "gpu_py_texture.h" +#include "gpu_py.h" +#include "gpu_py_buffer.h" #include "gpu_py_framebuffer.h" /* own include */ /* -------------------------------------------------------------------- */ @@ -46,13 +48,7 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb) { if (UNLIKELY(bpygpu_fb->fb == NULL)) { - PyErr_SetString(PyExc_ReferenceError, -#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD - "GPU framebuffer was freed, no further access is valid" -#else - "GPU framebuffer: internal error" -#endif - ); + PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid"); return -1; } return 0; @@ -68,10 +64,6 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb) static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb) { - if (!fb) { - return; - } - if (GPU_is_init()) { GPU_framebuffer_free(fb); } @@ -80,6 +72,21 @@ static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb) } } +static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self) +{ + if (self->fb) { +#ifndef GPU_NO_USE_PY_REFERENCES + GPU_framebuffer_py_reference_set(self->fb, NULL); + if (!self->shared_reference) +#endif + { + pygpu_framebuffer_free_if_possible(self->fb); + } + + self->fb = NULL; + } +} + /* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */ #define GPU_PY_FRAMEBUFFER_STACK_LEN 16 @@ -336,7 +343,7 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self), GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python"); GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1); - return BPyGPUFrameBuffer_CreatePyObject(fb_python); + return BPyGPUFrameBuffer_CreatePyObject(fb_python, false); } PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc, @@ -450,6 +457,100 @@ static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *U return ret; } +PyDoc_STRVAR( + pygpu_framebuffer_read_color_doc, + ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n" + "\n" + " Read a block of pixels from the frame buffer.\n" + "\n" + " :param x, y: Lower left corner of a rectangular block of pixels.\n" + " :param xsize, ysize: Dimensions of the pixel rectangle.\n" + " :type x, y, xsize, ysize: int\n" + " :param channels: Number of components to read.\n" + " :type channels: int\n" + " :param slot: The framebuffer slot to read data from.\n" + " :type slot: int\n" + " :param format: The format that describes the content of a single channel.\n" + " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n" + " :type type: str\n" + " :arg data: Optional Buffer object to fill with the pixels values.\n" + " :type data: :class:`gpu.types.Buffer`\n" + " :return: The Buffer with the read pixels.\n" + " :rtype: :class:`gpu.types.Buffer`\n"); +static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self, + PyObject *args, + PyObject *kwds) +{ + PYGPU_FRAMEBUFFER_CHECK_OBJ(self); + int x, y, w, h, channels; + uint slot; + struct PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_RGBA8}; + BPyGPUBuffer *py_buffer = NULL; + + static const char *_keywords[] = { + "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL}; + static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kwds, + &_parser, + &x, + &y, + &w, + &h, + &channels, + &slot, + PyC_ParseStringEnum, + &pygpu_dataformat, + &BPyGPU_BufferType, + &py_buffer)) { + return NULL; + } + + if (!IN_RANGE_INCL(channels, 1, 4)) { + PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4"); + return NULL; + } + + if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) { + PyErr_SetString(PyExc_ValueError, "slot overflow"); + return NULL; + } + + if (py_buffer) { + if (pygpu_dataformat.value_found != py_buffer->format) { + PyErr_SetString(PyExc_AttributeError, + "the format of the buffer is different from that specified"); + return NULL; + } + + size_t size_curr = bpygpu_Buffer_size(py_buffer); + size_t size_expected = w * h * channels * + GPU_texture_dataformat_size(pygpu_dataformat.value_found); + if (size_curr < size_expected) { + PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected"); + return NULL; + } + } + else { + py_buffer = BPyGPU_Buffer_CreatePyObject( + pygpu_dataformat.value_found, (Py_ssize_t[3]){h, w, channels}, 3, NULL); + BLI_assert(bpygpu_Buffer_size(py_buffer) == + w * h * channels * GPU_texture_dataformat_size(pygpu_dataformat.value_found)); + } + + GPU_framebuffer_read_color(self->fb, + x, + y, + w, + h, + channels, + (int)slot, + pygpu_dataformat.value_found, + py_buffer->buf.as_void); + + return (PyObject *)py_buffer; +} + #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD PyDoc_STRVAR(pygpu_framebuffer_free_doc, ".. method:: free()\n" @@ -459,15 +560,14 @@ PyDoc_STRVAR(pygpu_framebuffer_free_doc, static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self) { PYGPU_FRAMEBUFFER_CHECK_OBJ(self); - pygpu_framebuffer_free_if_possible(self->fb); - self->fb = NULL; + pygpu_framebuffer_free_safe(self); Py_RETURN_NONE; } #endif static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self) { - pygpu_framebuffer_free_if_possible(self->fb); + pygpu_framebuffer_free_safe(self); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -494,6 +594,10 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = { (PyCFunction)pygpu_framebuffer_viewport_get, METH_NOARGS, pygpu_framebuffer_viewport_get_doc}, + {"read_color", + (PyCFunction)pygpu_framebuffer_read_color, + METH_VARARGS | METH_KEYWORDS, + pygpu_framebuffer_read_color_doc}, #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD {"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc}, #endif @@ -531,13 +635,35 @@ PyTypeObject BPyGPUFrameBuffer_Type = { /** \name Public API * \{ */ -PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb) +PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference) { BPyGPUFrameBuffer *self; +#ifndef GPU_NO_USE_PY_REFERENCES + if (shared_reference) { + void **ref = GPU_framebuffer_py_reference_get(fb); + if (ref) { + /* Retrieve BPyGPUFrameBuffer reference. */ + self = (BPyGPUFrameBuffer *)POINTER_OFFSET(ref, -offsetof(BPyGPUFrameBuffer, fb)); + BLI_assert(self->fb == fb); + Py_INCREF(self); + return (PyObject *)self; + } + } +#else + UNUSED_VARS(shared_reference); +#endif + self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type); self->fb = fb; +#ifndef GPU_NO_USE_PY_REFERENCES + self->shared_reference = shared_reference; + + BLI_assert(GPU_framebuffer_py_reference_get(fb) == NULL); + GPU_framebuffer_py_reference_set(fb, (void **)&self->fb); +#endif + return (PyObject *)self; } diff --git a/source/blender/python/gpu/gpu_py_framebuffer.h b/source/blender/python/gpu/gpu_py_framebuffer.h index 7113e7c35aa..6727328518c 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.h +++ b/source/blender/python/gpu/gpu_py_framebuffer.h @@ -28,6 +28,11 @@ extern PyTypeObject BPyGPUFrameBuffer_Type; typedef struct BPyGPUFrameBuffer { PyObject_HEAD struct GPUFrameBuffer *fb; + +#ifndef GPU_NO_USE_PY_REFERENCES + bool shared_reference; +#endif } BPyGPUFrameBuffer; -PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb) ATTR_NONNULL(1); +PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb, bool shared_reference) + ATTR_NONNULL(1); diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 9e9a0b9066a..28bd24a6877 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -53,6 +53,8 @@ #include "../generic/py_capi_utils.h" #include "gpu_py.h" +#include "gpu_py_texture.h" + #include "gpu_py_offscreen.h" /* own include */ /* Define the free method to avoid breakage. */ @@ -264,6 +266,17 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void * return PyLong_FromLong(GPU_texture_opengl_bindcode(texture)); } +PyDoc_STRVAR(pygpu_offscreen_texture_color_doc, + "The color texture attached.\n" + "\n" + ":type: :class:`gpu.types.GPUTexture`"); +static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type)) +{ + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + GPUTexture *texture = GPU_offscreen_color_texture(self->ofs); + return BPyGPUTexture_CreatePyObject(texture, true); +} + PyDoc_STRVAR( pygpu_offscreen_draw_view3d_doc, ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n" @@ -385,6 +398,11 @@ static PyGetSetDef pygpu_offscreen__tp_getseters[] = { (setter)NULL, pygpu_offscreen_color_texture_doc, NULL}, + {"texture_color", + (getter)pygpu_offscreen_texture_color_get, + (setter)NULL, + pygpu_offscreen_texture_color_doc, + NULL}, {"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL}, {"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index 6b0fade8d1c..173c5afba56 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -25,11 +25,13 @@ #include <Python.h> +#include "GPU_framebuffer.h" #include "GPU_state.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" +#include "gpu_py_framebuffer.h" #include "gpu_py_state.h" /* own include */ /* -------------------------------------------------------------------- */ @@ -334,6 +336,16 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb Py_RETURN_NONE; } +PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc, + ".. function:: framebuffer_active_get(enable)\n" + "\n" + " Return the active framefuffer in context.\n"); +static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self)) +{ + GPUFrameBuffer *fb = GPU_framebuffer_active_get(); + return BPyGPUFrameBuffer_CreatePyObject(fb, true); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -396,6 +408,10 @@ static struct PyMethodDef pygpu_state__tp_methods[] = { (PyCFunction)pygpu_state_program_point_size_set, METH_O, pygpu_state_program_point_size_set_doc}, + {"active_framebuffer_get", + (PyCFunction)pygpu_state_framebuffer_active_get, + METH_NOARGS, + pygpu_state_framebuffer_active_get_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c index 1ae65c1dd11..4df61d35d4c 100644 --- a/source/blender/python/gpu/gpu_py_texture.c +++ b/source/blender/python/gpu/gpu_py_texture.c @@ -247,7 +247,7 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg return NULL; } - return BPyGPUTexture_CreatePyObject(tex); + return BPyGPUTexture_CreatePyObject(tex, false); } PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`"); @@ -412,6 +412,9 @@ static PyObject *pygpu_texture_free(BPyGPUTexture *self) static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self) { if (self->tex) { +#ifndef GPU_NO_USE_PY_REFERENCES + GPU_texture_py_reference_set(self->tex, NULL); +#endif GPU_texture_free(self->tex); } Py_TYPE(self)->tp_free((PyObject *)self); @@ -535,10 +538,7 @@ static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg) BKE_imageuser_default(&iuser); GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL); - /* Increase the texture reference count. */ - GPU_texture_ref(tex); - - return BPyGPUTexture_CreatePyObject(tex); + return BPyGPUTexture_CreatePyObject(tex, true); } static struct PyMethodDef pygpu_texture__m_methods[] = { @@ -595,13 +595,33 @@ PyObject *bpygpu_texture_init(void) /** \name Public API * \{ */ -PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex) +PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference) { BPyGPUTexture *self; + if (shared_reference) { +#ifndef GPU_NO_USE_PY_REFERENCES + void **ref = GPU_texture_py_reference_get(tex); + if (ref) { + /* Retrieve BPyGPUTexture reference. */ + self = (BPyGPUTexture *)POINTER_OFFSET(ref, -offsetof(BPyGPUTexture, tex)); + BLI_assert(self->tex == tex); + Py_INCREF(self); + return (PyObject *)self; + } +#endif + + GPU_texture_ref(tex); + } + self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type); self->tex = tex; +#ifndef GPU_NO_USE_PY_REFERENCES + BLI_assert(GPU_texture_py_reference_get(tex) == NULL); + GPU_texture_py_reference_set(tex, (void **)&self->tex); +#endif + return (PyObject *)self; } diff --git a/source/blender/python/gpu/gpu_py_texture.h b/source/blender/python/gpu/gpu_py_texture.h index 5130273f971..457ddcfc931 100644 --- a/source/blender/python/gpu/gpu_py_texture.h +++ b/source/blender/python/gpu/gpu_py_texture.h @@ -33,4 +33,5 @@ typedef struct BPyGPUTexture { int bpygpu_ParseTexture(PyObject *o, void *p); PyObject *bpygpu_texture_init(void); -PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex) ATTR_NONNULL(1); +PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex, bool weak_reference) + ATTR_NONNULL(1); diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 9ac8d4d9f47..2be2105d327 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -79,6 +79,7 @@ set(SRC bpy_rna_driver.c bpy_rna_gizmo.c bpy_rna_id_collection.c + bpy_rna_operator.c bpy_rna_types_capi.c bpy_rna_ui.c bpy_traceback.c @@ -118,6 +119,7 @@ set(SRC bpy_rna_driver.h bpy_rna_gizmo.h bpy_rna_id_collection.h + bpy_rna_operator.h bpy_rna_types_capi.h bpy_rna_ui.h bpy_traceback.h diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 5f31e0bb74d..4144063cf5c 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -167,6 +167,14 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) } } +static void bpy_context_end(bContext *C) +{ + if (UNLIKELY(C == NULL)) { + return; + } + CTX_wm_operator_poll_msg_clear(C); +} + /** * Use for `CTX_*_set(..)` functions need to set values which are later read back as expected. * In this case we don't want the Python context to override the values as it causes problems @@ -524,6 +532,9 @@ void BPY_python_end(void) /* finalizing, no need to grab the state, except when we are a module */ gilstate = PyGILState_Ensure(); + /* Clear Python values in the context so freeing the context after Python exits doesn't crash. */ + bpy_context_end(BPY_context_get()); + /* Decrement user counts of all callback functions. */ BPY_rna_props_clear_all(); diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 94ad6a8ef78..4a5e2552598 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -244,12 +244,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) } if (WM_operator_poll_context((bContext *)C, ot, context) == false) { - const char *msg = CTX_wm_operator_poll_msg_get(C); + bool msg_free = false; + const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free); PyErr_Format(PyExc_RuntimeError, "Operator bpy.ops.%.200s.poll() %.200s", opname, msg ? msg : "failed, context is incorrect"); - CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */ + CTX_wm_operator_poll_msg_clear(C); + if (msg_free) { + MEM_freeN((void *)msg); + } error_val = -1; } else { diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c new file mode 100644 index 00000000000..6e0db3eca49 --- /dev/null +++ b/source/blender/python/intern/bpy_rna_operator.c @@ -0,0 +1,150 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup pythonintern + * + * This file extends `bpy.types.Operator` with C/Python API methods and attributes. + */ + +#include <Python.h> + +#include "BLI_string.h" + +#include "BKE_context.h" + +#include "../generic/python_utildefines.h" + +#include "BPY_extern.h" +#include "bpy_capi_utils.h" + +/* -------------------------------------------------------------------- */ +/** \name Operator `poll_message_set` Method + * \{ */ + +static char *pyop_poll_message_get_fn(bContext *UNUSED(C), void *user_data) +{ + PyGILState_STATE gilstate = PyGILState_Ensure(); + + PyObject *py_args = user_data; + PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0); + + if (PyUnicode_Check(py_func_or_msg)) { + return BLI_strdup(PyUnicode_AsUTF8(py_func_or_msg)); + } + + PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX); + PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first); + Py_DECREF(py_args_after_first); + + char *msg = NULL; + bool error = false; + + /* NULL for no string. */ + if (py_msg == NULL) { + error = true; + } + else { + if (py_msg == Py_None) { + /* pass */ + } + else if (PyUnicode_Check(py_msg)) { + msg = BLI_strdup(PyUnicode_AsUTF8(py_msg)); + } + else { + PyErr_Format(PyExc_TypeError, + "poll_message_set(function, ...): expected string or None, got %.200s", + Py_TYPE(py_msg)->tp_name); + error = true; + } + Py_DECREF(py_msg); + } + + if (error) { + PyErr_Print(); + PyErr_Clear(); + } + + PyGILState_Release(gilstate); + return msg; +} + +static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data) +{ + /* Handles the GIL. */ + BPY_DECREF(user_data); +} + +PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc, + ".. method:: poll_message_set(message, ...)\n" + "\n" + " Set the message to show in the tool-tip when poll fails.\n" + "\n" + " When message is callable, " + "additional user defined positional arguments are passed to the message function.\n" + "\n" + " :param message: The message or a function that returns the message.\n" + " :type message: string or a callable that returns a string or None.\n"); + +static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args) +{ + const ssize_t args_len = PyTuple_GET_SIZE(args); + if (args_len == 0) { + PyErr_SetString(PyExc_ValueError, + "poll_message_set(message, ...): requires a message argument"); + return NULL; + } + + PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0); + + if (PyUnicode_Check(py_func_or_msg)) { + if (args_len > 1) { + PyErr_SetString(PyExc_ValueError, + "poll_message_set(message): does not support additional arguments"); + return NULL; + } + } + else if (PyCallable_Check(py_func_or_msg)) { + /* pass */ + } + else { + PyErr_Format(PyExc_TypeError, + "poll_message_set(message, ...): " + "expected at least 1 string or callable argument, got %.200s", + Py_TYPE(py_func_or_msg)->tp_name); + return NULL; + } + + bContext *C = BPY_context_get(); + struct bContextPollMsgDyn_Params params = { + .get_fn = pyop_poll_message_get_fn, + .free_fn = pyop_poll_message_free_fn, + .user_data = Py_INCREF_RET(args), + }; + + CTX_wm_operator_poll_msg_set_dynamic(C, ¶ms); + + Py_RETURN_NONE; +} + +PyMethodDef BPY_rna_operator_poll_message_set_method_def = { + "poll_message_set", + (PyCFunction)BPY_rna_operator_poll_message_set, + METH_VARARGS | METH_STATIC, + BPY_rna_operator_poll_message_set_doc, +}; + +/** \} */ diff --git a/source/blender/python/intern/bpy_rna_operator.h b/source/blender/python/intern/bpy_rna_operator.h new file mode 100644 index 00000000000..8040d8a492a --- /dev/null +++ b/source/blender/python/intern/bpy_rna_operator.h @@ -0,0 +1,31 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup pythonintern + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern PyMethodDef BPY_rna_operator_poll_message_set_method_def; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c index 9b15e84663d..2f6e197d1e2 100644 --- a/source/blender/python/intern/bpy_rna_types_capi.c +++ b/source/blender/python/intern/bpy_rna_types_capi.c @@ -41,6 +41,8 @@ #include "bpy_rna_types_capi.h" #include "bpy_rna_ui.h" +#include "bpy_rna_operator.h" + #include "../generic/py_capi_utils.h" #include "RNA_access.h" @@ -87,6 +89,17 @@ static struct PyMethodDef pyrna_uilayout_methods[] = { /** \} */ /* -------------------------------------------------------------------- */ +/** \name Operator + * \{ */ + +static struct PyMethodDef pyrna_operator_methods[] = { + {NULL, NULL, 0, NULL}, /* #BPY_rna_operator_poll_message_set */ + {NULL, NULL, 0, NULL}, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Window Manager Clipboard Property * * Avoid using the RNA API because this value may change between checking its length @@ -228,6 +241,11 @@ void BPY_rna_types_extend_capi(void) /* Space */ pyrna_struct_type_extend_capi(&RNA_Space, pyrna_space_methods, NULL); + /* wmOperator */ + ARRAY_SET_ITEMS(pyrna_operator_methods, BPY_rna_operator_poll_message_set_method_def); + BLI_assert(ARRAY_SIZE(pyrna_operator_methods) == 2); + pyrna_struct_type_extend_capi(&RNA_Operator, pyrna_operator_methods, NULL); + /* WindowManager */ pyrna_struct_type_extend_capi( &RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset); diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c index 33af3bbaf29..242c8a199fb 100644 --- a/source/blender/render/intern/zbuf.c +++ b/source/blender/render/intern/zbuf.c @@ -175,7 +175,7 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2]) /* Functions */ /*-----------------------------------------------------------*/ -/* Scanconvert for strand triangles, calls func for each x, y coordinate +/* Scan-convert for strand triangles, calls function for each x, y coordinate * and gives UV barycentrics and z. */ void zspan_scanconvert(ZSpan *zspan, diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c index 30eaa35a049..fc75771cd62 100644 --- a/source/blender/shader_fx/intern/FX_shader_glow.c +++ b/source/blender/shader_fx/intern/FX_shader_glow.c @@ -71,12 +71,11 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); - if (mode == eShaderFxGlowMode_Luminance) { - uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE); - } - else { + uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE); + if (mode == eShaderFxGlowMode_Color) { uiItemR(layout, ptr, "select_color", 0, NULL, ICON_NONE); } + uiItemR(layout, ptr, "glow_color", 0, NULL, ICON_NONE); uiItemS(layout); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index d54925272de..1d99b605205 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -132,17 +132,21 @@ struct wmWindowManager; extern "C" { #endif +typedef void (*wmGenericUserDataFreeFn)(void *data); + typedef struct wmGenericUserData { void *data; /** When NULL, use #MEM_freeN. */ - void (*free_fn)(void *data); + wmGenericUserDataFreeFn free_fn; bool use_free; } wmGenericUserData; +typedef void (*wmGenericCallbackFn)(struct bContext *C, void *user_data); + typedef struct wmGenericCallback { - void (*exec)(struct bContext *C, void *user_data); + wmGenericCallbackFn exec; void *user_data; - void (*free_user_data)(void *user_data); + wmGenericUserDataFreeFn free_user_data; } wmGenericCallback; /* ************** wmOperatorType ************************ */ diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index cdb7b591907..11783ae3517 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -264,7 +264,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4]) if (wrap == WM_CURSOR_WRAP_X) { mode_axis = GHOST_kAxisX; } - if (wrap == WM_CURSOR_WRAP_Y) { + else if (wrap == WM_CURSOR_WRAP_Y) { mode_axis = GHOST_kGrabAxisY; } } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 0d1f4cc4830..a6588c40f0f 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1048,7 +1048,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons wmWindowManager *wm = CTX_wm_manager(C); int retval = OPERATOR_CANCELLED; - CTX_wm_operator_poll_msg_set(C, NULL); + CTX_wm_operator_poll_msg_clear(C); if (op == NULL || op->type == NULL) { return retval; @@ -1469,7 +1469,7 @@ static int wm_operator_call_internal(bContext *C, { int retval; - CTX_wm_operator_poll_msg_set(C, NULL); + CTX_wm_operator_poll_msg_clear(C); /* Dummy test. */ if (ot) { @@ -4478,16 +4478,21 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.prevtype = event.type; event.prevval = event.val; - /* Ensure the event state is correct, any deviation from this may cause bugs. */ + /* Ensure the event state is correct, any deviation from this may cause bugs. + * + * NOTE: #EVENT_NONE is set when unknown keys are pressed, + * while not common, avoid a false alarm. */ #ifndef NDEBUG if ((event_state->type || event_state->val) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type))) { + !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type) || + (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, "Non-keyboard/mouse button found in 'win->eventstate->type = %d'", event_state->type); } if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype))) { + !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) || + (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, "Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'", event_state->prevtype); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index bbcb0669cce..d0ee7075516 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -2157,21 +2157,9 @@ static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data) C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data); } -static void wm_free_operator_properties_callback(void *user_data) -{ - IDProperty *properties = (IDProperty *)user_data; - IDP_FreeProperty(properties); -} - static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - if (U.uiflag & USER_SAVE_PROMPT && - wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { - wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); - callback->exec = wm_homefile_read_after_dialog_callback; - callback->user_data = IDP_CopyProperty(op->properties); - callback->free_user_data = wm_free_operator_properties_callback; - wm_close_file_dialog(C, callback); + if (wm_operator_close_file_dialog_if_needed(C, op, wm_homefile_read_after_dialog_callback)) { return OPERATOR_INTERFACE; } return wm_homefile_read_exec(C, op); @@ -2331,13 +2319,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op) set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN); } - if (U.uiflag & USER_SAVE_PROMPT && - wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { - wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); - callback->exec = wm_open_mainfile_after_dialog_callback; - callback->user_data = IDP_CopyProperty(op->properties); - callback->free_user_data = wm_free_operator_properties_callback; - wm_close_file_dialog(C, callback); + if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { return OPERATOR_INTERFACE; } return wm_open_mainfile_dispatch(C, op); @@ -2637,12 +2619,25 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data) +{ + WM_operator_name_call_with_properties( + C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data); +} + +static int wm_recover_last_session_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) { /* Keep the current setting instead of using the preferences since a file selector * doesn't give us the option to change the setting. */ wm_open_init_use_scripts(op, false); - return WM_operator_confirm(C, op, event); + + if (wm_operator_close_file_dialog_if_needed( + C, op, wm_recover_last_session_after_dialog_callback)) { + return OPERATOR_INTERFACE; + } + return wm_recover_last_session_exec(C, op); } void WM_OT_recover_last_session(wmOperatorType *ot) @@ -3270,7 +3265,10 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0'; if (file_has_been_saved_before) { - WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL); + if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) & + OPERATOR_CANCELLED) { + execute_callback = false; + } } else { WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL); @@ -3456,4 +3454,31 @@ void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action) } } +static void wm_free_operator_properties_callback(void *user_data) +{ + IDProperty *properties = (IDProperty *)user_data; + IDP_FreeProperty(properties); +} + +/** + * \return True if the dialog was created, the calling operator should return #OPERATOR_INTERFACE + * then. + */ +bool wm_operator_close_file_dialog_if_needed(bContext *C, + wmOperator *op, + wmGenericCallbackFn post_action_fn) +{ + if (U.uiflag & USER_SAVE_PROMPT && + wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { + wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); + callback->exec = post_action_fn; + callback->user_data = IDP_CopyProperty(op->properties); + callback->free_user_data = wm_free_operator_properties_callback; + wm_close_file_dialog(C, callback); + return true; + } + + return false; +} + /** \} */ diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 0bf6fcd94a0..9dd20577a93 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -451,7 +451,7 @@ static void build_pict_list_ex( */ while (IMB_ispic(filepath) && totframes) { - bool hasevent; + bool has_event; size_t size; int file; @@ -532,7 +532,7 @@ static void build_pict_list_ex( BLI_path_sequence_encode( filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr); - while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) { + while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) { GHOST_DispatchEvents(g_WS.ghost_system); if (ps->loading == false) { return; @@ -1408,7 +1408,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) #endif while (ps.picture) { - int hasevent; + bool has_event; #ifndef USE_IMB_CACHE if (ibuf != NULL && ibuf->ftype == IMB_FTYPE_NONE) { IMB_freeImBuf(ibuf); @@ -1513,14 +1513,14 @@ static char *wm_main_playanim_intern(int argc, const char **argv) ps.next_frame = ps.direction; - while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) { + while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) { GHOST_DispatchEvents(g_WS.ghost_system); } if (ps.go == false) { break; } change_frame(&ps); - if (!hasevent) { + if (!has_event) { PIL_sleep_ms(1); } if (ps.wait2) { diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 2e9fd1b1b16..cdd5ea12df8 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -133,7 +133,7 @@ static struct WMInitStruct { * \{ */ static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate); -static int wm_window_timer(const bContext *C); +static bool wm_window_timer(const bContext *C); /* XXX this one should correctly check for apple top header... * done for Cocoa : returns window contents (and not frame) max size*/ @@ -1498,12 +1498,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr * Timer handlers should check for delta to decide if they just update, or follow real time. * Timer handlers can also set duration to match frames passed */ -static int wm_window_timer(const bContext *C) +static bool wm_window_timer(const bContext *C) { Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); double time = PIL_check_seconds_timer(); - int retval = 0; + bool has_event = false; /* Mutable in case the timer gets removed. */ LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) { @@ -1540,31 +1540,34 @@ static int wm_window_timer(const bContext *C) event.customdata = wt; wm_event_add(win, &event); - retval = 1; + has_event = true; } } } - return retval; + return has_event; } void wm_window_process_events(const bContext *C) { BLI_assert(BLI_thread_is_main()); - int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */ + bool has_event = GHOST_ProcessEvents(g_system, false); /* `false` is no wait. */ - if (hasevent) { + if (has_event) { GHOST_DispatchEvents(g_system); } - hasevent |= wm_window_timer(C); + has_event |= wm_window_timer(C); #ifdef WITH_XR_OPENXR /* XR events don't use the regular window queues. So here we don't only trigger * processing/dispatching but also handling. */ - hasevent |= wm_xr_events_handle(CTX_wm_manager(C)); + has_event |= wm_xr_events_handle(CTX_wm_manager(C)); #endif - /* no event, we sleep 5 milliseconds */ - if (hasevent == 0) { + /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle. + * + * Skip sleeping when simulating events so tests don't idle unnecessarily as simulated + * events are typically generated from a timer that runs in the main loop. */ + if ((has_event == false) && !(G.f & G_FLAG_EVENT_SIMULATE)) { PIL_sleep_ms(5); } } diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index d54090a6025..c7fe07cad7f 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -45,6 +45,9 @@ void wm_homefile_read(struct bContext *C, void wm_file_read_report(bContext *C, struct Main *bmain); void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); +bool wm_operator_close_file_dialog_if_needed(bContext *C, + wmOperator *op, + wmGenericCallbackFn exec_fn); bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm); void WM_OT_save_homefile(struct wmOperatorType *ot); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index cb5fc538e69..f1741c3b495 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -130,14 +130,11 @@ set(SRC # MSVC 2010 gives linking errors with the manifest if(WIN32 AND NOT UNIX) - string(SUBSTRING ${BLENDER_VERSION} 0 1 bver1) - string(SUBSTRING ${BLENDER_VERSION} 2 1 bver2) - string(SUBSTRING ${BLENDER_VERSION} 3 1 bver3) add_definitions( -DBLEN_VER_RC_STR="${BLENDER_VERSION}" - -DBLEN_VER_RC_1=${bver1} - -DBLEN_VER_RC_2=${bver2} - -DBLEN_VER_RC_3=${bver3} + -DBLEN_VER_RC_1=${BLENDER_VERSION_MAJOR} + -DBLEN_VER_RC_2=${BLENDER_VERSION_MINOR} + -DBLEN_VER_RC_3=${BLENDER_VERSION_PATCH} -DBLEN_VER_RC_4=0 ) diff --git a/source/creator/creator_intern.h b/source/creator/creator_intern.h index bcc8a15355a..2260da8db11 100644 --- a/source/creator/creator_intern.h +++ b/source/creator/creator_intern.h @@ -72,7 +72,7 @@ enum { /* for the callbacks: */ #ifndef WITH_PYTHON_MODULE -# define BLEND_VERSION_FMT "Blender %d.%02d.%d" +# define BLEND_VERSION_FMT "Blender %d.%d.%d" # define BLEND_VERSION_ARG (BLENDER_VERSION / 100), (BLENDER_VERSION % 100), BLENDER_VERSION_PATCH #endif diff --git a/source/tools b/source/tools -Subproject f99d29ae3e6ad44d45d79309454c45f8088781a +Subproject 2afbb8ec472cac5102eb239f57b006f8c938768 diff --git a/tests/python/operators.py b/tests/python/operators.py index 309a872ac67..4c863edfbdb 100644 --- a/tests/python/operators.py +++ b/tests/python/operators.py @@ -196,6 +196,14 @@ def main(): MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles", [OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]), + # face shade smooth (not a real test) + MeshTest("CubeShadeSmooth", "testCubeShadeSmooth", "expectedCubeShadeSmooth", + [OperatorSpecEditMode("faces_shade_smooth", {}, "VERT", {i for i in range(8)})]), + + # faces shade flat (not a real test) + MeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat", + [OperatorSpecEditMode("faces_shade_flat", {}, "FACE", {i for i in range(6)})]), + # inset faces MeshTest("CubeInset", "testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT", @@ -226,6 +234,10 @@ def main(): MeshTest("EmptyMeshLoopMultiSelect", "testEmptyMeshLoopMultiSelect", "expectedEmptyMeshLoopMultiSelect", [OperatorSpecEditMode("loop_multi_select", {}, "VERT", {})]), + # mark seam + MeshTest("CubeMarkSeam", "testCubeMarkSeam", "expectedCubeMarkSeam", + [OperatorSpecEditMode("mark_seam", {}, "EDGE", {1})]), + # select all MeshTest("CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll", [OperatorSpecEditMode("select_all", {}, "VERT", {1})]), @@ -296,6 +308,26 @@ def main(): MeshTest("EmptyMeshSelectLinked", "testEmptyMeshSelectLinked", "expectedEmptyMeshSelectLinked", [OperatorSpecEditMode("select_linked", {}, "VERT", {})]), + # select nth (checkered deselect) + MeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd", + [OperatorSpecEditMode("select_nth", {}, "VERT", {i for i in range(32)})]), + + # unsubdivide + # normal case + MeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide", + [OperatorSpecEditMode("unsubdivide", {}, "FACE", {i for i in range(6)})]), + + # T87259 - test cases + MeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide", + [OperatorSpecEditMode("unsubdivide", {}, "EDGE", {i for i in range(6)})]), + MeshTest("UVSphereUnsubdivide", "testUVSphereUnsubdivide", "expectedUVSphereUnsubdivide", + [OperatorSpecEditMode("unsubdivide", {'iterations': 9}, "FACE", {i for i in range(512)})]), + + # vert connect path + # Tip: It works only if there is an already existing face or more than 2 vertices. + MeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath", + [OperatorSpecEditMode("vert_connect_path", {}, "VERT", {0, 5})]), + ] operators_test = RunTest(tests) |