Merge branch 'master' into refactor-mesh-position-generic
This commit is contained in:
commit
c525fcb9a3
|
@ -118,14 +118,18 @@ enable_testing()
|
|||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
if("${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "11.0.0")
|
||||
message(FATAL_ERROR "The minimum supported version of GCC is 11.0.0")
|
||||
message(FATAL_ERROR "The minimum supported version of GCC is 11.0.0, found ${CMAKE_C_COMPILER_VERSION}")
|
||||
endif()
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if(CMAKE_COMPILER_IS_GNUCC AND ("${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "8.0"))
|
||||
message(FATAL_ERROR "The minimum supported version of CLANG is 8.0")
|
||||
message(FATAL_ERROR "The minimum supported version of CLANG is 8.0, found ${CMAKE_C_COMPILER_VERSION}")
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||
if(MSVC_VERSION VERSION_LESS "1928")
|
||||
# MSVC_VERSION is an internal version number, it doesn't map to something
|
||||
# the end user would recognize as a version. Because of this, for MSVC we do
|
||||
# not show the found number. When using our make.bat the actual VS version
|
||||
# will be displayed on the console before starting the build, anyway.
|
||||
message(FATAL_ERROR "The minimum supported version of MSVC is 2019 (16.9.16)")
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -39,15 +39,15 @@ with-all,with-opencollada,with-jack,with-pulseaudio,with-embree,with-oidn,with-n
|
|||
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,ver-xr-openxr:,ver-level-zero:\
|
||||
force-all,force-python,force-boost,force-tbb,\
|
||||
force-ocio,force-imath,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\
|
||||
force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,force-usd,\
|
||||
force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,force-materialx,force-usd,\
|
||||
force-xr-openxr,force-level-zero,force-openpgl,\
|
||||
build-all,build-python,build-boost,build-tbb,\
|
||||
build-ocio,build-imath,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\
|
||||
build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,build-usd,\
|
||||
build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,build-materialx,build-usd,\
|
||||
build-xr-openxr,build-level-zero,build-openpgl,\
|
||||
skip-python,skip-boost,skip-tbb,\
|
||||
skip-ocio,skip-imath,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\
|
||||
skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn,skip-usd,\
|
||||
skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn,skip-materialx,skip-usd,\
|
||||
skip-xr-openxr,skip-level-zero,skip-openpgl \
|
||||
-- "$@" \
|
||||
)
|
||||
|
@ -223,6 +223,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
|
|||
--build-ffmpeg
|
||||
Force the build of FFMpeg.
|
||||
|
||||
--build-materialx
|
||||
Force the build of MaterialX.
|
||||
|
||||
--build-usd
|
||||
Force the build of Universal Scene Description.
|
||||
|
||||
|
@ -296,6 +299,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
|
|||
--force-ffmpeg
|
||||
Force the rebuild of FFMpeg.
|
||||
|
||||
--force-materialx
|
||||
Force the rebuild of MaterialX.
|
||||
|
||||
--force-usd
|
||||
Force the rebuild of Universal Scene Description.
|
||||
|
||||
|
@ -362,6 +368,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
|
|||
--skip-ffmpeg
|
||||
Unconditionally skip FFMpeg installation/building.
|
||||
|
||||
--skip-materialx
|
||||
Unconditionally skip MaterialX installation/building.
|
||||
|
||||
--skip-usd
|
||||
Unconditionally skip Universal Scene Description installation/building.
|
||||
|
||||
|
@ -402,6 +411,7 @@ PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT
|
|||
PYTHON_FORCE_BUILD=false
|
||||
PYTHON_FORCE_REBUILD=false
|
||||
PYTHON_SKIP=false
|
||||
_with_built_python=false
|
||||
|
||||
# Additional Python modules.
|
||||
PYTHON_IDNA_VERSION="3.3"
|
||||
|
@ -466,7 +476,9 @@ BOOST_VERSION="1.80.0"
|
|||
BOOST_VERSION_SHORT="1.80"
|
||||
BOOST_VERSION_MIN="1.49"
|
||||
BOOST_VERSION_MEX="2.0"
|
||||
BOOST_FORCE_BUILD=false
|
||||
# XXX Boost currently has an issue with python/tbb, see rB019b930 for details and patch used to fix it.
|
||||
# So for now it has to be built, system packages are not usable. :(
|
||||
BOOST_FORCE_BUILD=true
|
||||
BOOST_FORCE_REBUILD=false
|
||||
BOOST_SKIP=false
|
||||
|
||||
|
@ -560,6 +572,14 @@ ALEMBIC_FORCE_BUILD=false
|
|||
ALEMBIC_FORCE_REBUILD=false
|
||||
ALEMBIC_SKIP=false
|
||||
|
||||
MATERIALX_VERSION="1.38.6"
|
||||
MATERIALX_VERSION_SHORT="1.38"
|
||||
MATERIALX_VERSION_MIN="1.38"
|
||||
MATERIALX_VERSION_MEX="1.40"
|
||||
MATERIALX_FORCE_BUILD=false
|
||||
MATERIALX_FORCE_REBUILD=false
|
||||
MATERIALX_SKIP=false
|
||||
|
||||
USD_VERSION="22.11"
|
||||
USD_VERSION_SHORT="22.11"
|
||||
USD_VERSION_MIN="20.05"
|
||||
|
@ -896,6 +916,9 @@ while true; do
|
|||
--build-alembic)
|
||||
ALEMBIC_FORCE_BUILD=true; shift; continue
|
||||
;;
|
||||
--build-materialx)
|
||||
MATERIALX_FORCE_BUILD=true; shift; continue
|
||||
;;
|
||||
--build-usd)
|
||||
USD_FORCE_BUILD=true; shift; continue
|
||||
;;
|
||||
|
@ -925,6 +948,7 @@ while true; do
|
|||
OIDN_FORCE_REBUILD=true
|
||||
FFMPEG_FORCE_REBUILD=true
|
||||
ALEMBIC_FORCE_REBUILD=true
|
||||
MATERIALX_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
XR_OPENXR_FORCE_REBUILD=true
|
||||
LEVEL_ZERO_FORCE_REBUILD=true
|
||||
|
@ -980,6 +1004,9 @@ while true; do
|
|||
--force-alembic)
|
||||
ALEMBIC_FORCE_REBUILD=true; shift; continue
|
||||
;;
|
||||
--force-materialx)
|
||||
MATERIALX_FORCE_REBUILD=true; shift; continue
|
||||
;;
|
||||
--force-usd)
|
||||
USD_FORCE_REBUILD=true; shift; continue
|
||||
;;
|
||||
|
@ -1043,6 +1070,9 @@ while true; do
|
|||
--skip-usd)
|
||||
USD_SKIP=true; shift; continue
|
||||
;;
|
||||
--skip-materialx)
|
||||
MATERIALX_SKIP=true; shift; continue
|
||||
;;
|
||||
--skip-xr-openxr)
|
||||
XR_OPENXR_SKIP=true; shift; continue
|
||||
;;
|
||||
|
@ -1108,7 +1138,9 @@ PYTHON_SOURCE=( "https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHO
|
|||
|
||||
_boost_version_nodots=`echo "$BOOST_VERSION" | sed -r 's/\./_/g'`
|
||||
BOOST_SOURCE=( "https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_$_boost_version_nodots.tar.bz2" )
|
||||
BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams --with-python --with-program_options --with-serialization --with-atomic"
|
||||
BOOST_BUILD_MODULES="--with-filesystem --with-locale --with-thread --with-regex --with-system --with-date_time --with-wave --with-atomic --with-serialization --with-program_options --with-iostreams --with-python"
|
||||
# Used by debian distros.
|
||||
BOOST_DEB_PACKAGE_MODULES=( "libboost-filesystem" "libboost-locale" "libboost-thread" "libboost-regex" "libboost-system" "libboost-date-time" "libboost-wave" "libboost-atomic" "libboost-serialization" "libboost-program-options" "libboost-iostreams" "libboost-python" "libboost-numpy" )
|
||||
|
||||
TBB_SOURCE=( "https://github.com/oneapi-src/oneTBB/archive/$TBB_VERSION$TBB_VERSION_UPDATE.tar.gz" )
|
||||
TBB_SOURCE_CMAKE=( "https://raw.githubusercontent.com/wjakob/tbb/master/CMakeLists.txt" )
|
||||
|
@ -1165,6 +1197,8 @@ ALEMBIC_SOURCE=( "https://github.com/alembic/alembic/archive/${ALEMBIC_VERSION}.
|
|||
# ALEMBIC_SOURCE_REPO_UID="e6c90d4faa32c4550adeaaf3f556dad4b73a92bb"
|
||||
# ALEMBIC_SOURCE_REPO_BRANCH="master"
|
||||
|
||||
MATERIALX_SOURCE=( "https://github.com/AcademySoftwareFoundation/MaterialX/archive/refs/tags/v${MATERIALX_VERSION}.tar.gz" )
|
||||
|
||||
USD_SOURCE=( "https://github.com/PixarAnimationStudios/USD/archive/v${USD_VERSION}.tar.gz" )
|
||||
|
||||
OPENCOLLADA_USE_REPO=false
|
||||
|
@ -1217,8 +1251,10 @@ Those libraries should be available as packages in all recent distributions (opt
|
|||
* libjpeg, libpng, libtiff, [openjpeg2], [libopenal].
|
||||
* libx11, libxcursor, libxi, libxrandr, libxinerama (and other libx... as needed).
|
||||
* libwayland-client0, libdecor, libwayland-cursor0, libwayland-egl1, libxkbcommon0, libdbus-1-3, libegl1 (Wayland)
|
||||
* libsqlite3, libzstd, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp, flex.
|
||||
* libsdl2, libepoxy, libpugixml, libpotrace, [libgmp], fontconfig, [libharu/libhpdf].\""
|
||||
* libsqlite3, libzstd, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp, flex, pybind11.
|
||||
* libsdl2, libepoxy, libpugixml, libpotrace, [libgmp], fontconfig, [libharu/libhpdf].
|
||||
* [libvulkan/vulkan-loader].
|
||||
* [libfribidi], [libharfbuzz].\""
|
||||
|
||||
DEPS_SPECIFIC_INFO="\"BUILDABLE DEPENDENCIES:
|
||||
|
||||
|
@ -1479,9 +1515,17 @@ _init_python() {
|
|||
_update_deps_python() {
|
||||
if [ "$1" = true ]; then
|
||||
BOOST_FORCE_BUILD=true
|
||||
OCIO_FORCE_BUILD=true
|
||||
OIIO_FORCE_BUILD=true
|
||||
OPENVDB_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
BOOST_FORCE_REBUILD=true
|
||||
OCIO_FORCE_REBUILD=true
|
||||
OIIO_FORCE_REBUILD=true
|
||||
OPENVDB_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -1567,6 +1611,9 @@ compile_Python() {
|
|||
PRINT ""
|
||||
$_python -m pip install $module --no-binary :all:
|
||||
done
|
||||
|
||||
_with_built_python=true
|
||||
_with_built_python_execpath="$INST/python-$PYTHON_VERSION_SHORT/bin/python3"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
@ -1585,12 +1632,14 @@ _update_deps_boost() {
|
|||
OSL_FORCE_BUILD=true
|
||||
OPENVDB_FORCE_BUILD=true
|
||||
ALEMBIC_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
OIIO_FORCE_REBUILD=true
|
||||
OSL_FORCE_REBUILD=true
|
||||
OPENVDB_FORCE_REBUILD=true
|
||||
ALEMBIC_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -1610,7 +1659,7 @@ compile_Boost() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
boost_magic=14
|
||||
boost_magic=15
|
||||
|
||||
_init_boost
|
||||
|
||||
|
@ -1636,11 +1685,13 @@ compile_Boost() {
|
|||
mkdir -p $SRC
|
||||
download BOOST_SOURCE[@] $_src.tar.bz2
|
||||
tar -C $SRC --transform "s,\w*,boost-$BOOST_VERSION,x" -xf $_src.tar.bz2
|
||||
|
||||
patch -d $_src -p1 < $SCRIPT_DIR/patches/boost.diff
|
||||
fi
|
||||
|
||||
cd $_src
|
||||
if [ ! -f $_src/b2 ]; then
|
||||
if [ -d $INST/python-$PYTHON_VERSION_INSTALLED ]; then
|
||||
if [ -d $_with_built_python ]; then
|
||||
./bootstrap.sh --with-python-root="$INST/python-$PYTHON_VERSION_INSTALLED"
|
||||
else
|
||||
./bootstrap.sh
|
||||
|
@ -1835,7 +1886,7 @@ compile_OCIO() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
ocio_magic=3
|
||||
ocio_magic=5
|
||||
_init_ocio
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -1890,9 +1941,13 @@ compile_OCIO() {
|
|||
cmake_d="$cmake_d -D CMAKE_PREFIX_PATH=$_inst"
|
||||
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_APPS=OFF"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=OFF"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=ON"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_GPU_TESTS=OFF"
|
||||
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
if [ $(uname -m) == "aarch64" ]; then
|
||||
cmake_d="$cmake_d -D OCIO_USE_SSE=OFF"
|
||||
fi
|
||||
|
@ -2082,11 +2137,13 @@ _update_deps_openexr() {
|
|||
OIIO_FORCE_BUILD=true
|
||||
OPENVDB_FORCE_BUILD=true
|
||||
ALEMBIC_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
OIIO_FORCE_REBUILD=true
|
||||
OPENVDB_FORCE_REBUILD=true
|
||||
ALEMBIC_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -2215,9 +2272,11 @@ _init_oiio() {
|
|||
_update_deps_oiio() {
|
||||
if [ "$1" = true ]; then
|
||||
OSL_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
OSL_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -2237,7 +2296,7 @@ compile_OIIO() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
oiio_magic=19
|
||||
oiio_magic=20
|
||||
_init_oiio
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -2291,6 +2350,7 @@ 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 BUILD_SHARED_LIBS=ON"
|
||||
|
||||
if [ $(uname -m) != "aarch64" ]; then
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
|
@ -2306,21 +2366,37 @@ compile_OIIO() {
|
|||
cmake_d="$cmake_d -D OpenEXR_ROOT=$INST/openexr"
|
||||
fi
|
||||
|
||||
# ptex is only needed when nicholas bishop is ready
|
||||
cmake_d="$cmake_d -D USE_PTEX=OFF"
|
||||
cmake_d="$cmake_d -D USE_PYTHON=ON"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
# Optional tests and cmd tools
|
||||
cmake_d="$cmake_d -D USE_QT=OFF"
|
||||
cmake_d="$cmake_d -D USE_PYTHON=OFF"
|
||||
cmake_d="$cmake_d -D USE_QT5=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENGL=OFF"
|
||||
cmake_d="$cmake_d -D USE_TBB=OFF"
|
||||
cmake_d="$cmake_d -D USE_BZIP2=OFF"
|
||||
cmake_d="$cmake_d -D USE_FREETYPE=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENCOLORIO=OFF"
|
||||
|
||||
cmake_d="$cmake_d -D USE_WEBP=ON"
|
||||
cmake_d="$cmake_d -D USE_OPENJPEG=ON"
|
||||
|
||||
cmake_d="$cmake_d -D USE_FFMPEG=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENCV=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENVDB=OFF"
|
||||
cmake_d="$cmake_d -D USE_NUKE=OFF"
|
||||
cmake_d="$cmake_d -D USE_DCMTK=OFF"
|
||||
cmake_d="$cmake_d -D USE_LIBHEIF=OFF"
|
||||
cmake_d="$cmake_d -D USE_GIF=OFF"
|
||||
cmake_d="$cmake_d -D USE_LIBRAW=OFF"
|
||||
cmake_d="$cmake_d -D USE_LIBSQUISH=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 TXT2MAN="
|
||||
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
|
||||
|
||||
if [ -d $INST/boost ]; then
|
||||
cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost -D Boost_NO_SYSTEM_PATHS=ON -D Boost_NO_BOOST_CMAKE=ON"
|
||||
|
@ -2878,7 +2954,12 @@ _init_openvdb() {
|
|||
}
|
||||
|
||||
_update_deps_openvdb() {
|
||||
:
|
||||
if [ "$1" = true ]; then
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
clean_OPENVDB() {
|
||||
|
@ -2900,7 +2981,7 @@ compile_OPENVDB() {
|
|||
PRINT ""
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
openvdb_magic=4
|
||||
openvdb_magic=5
|
||||
_init_openvdb
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -2949,12 +3030,18 @@ compile_OPENVDB() {
|
|||
cmake_d="-D CMAKE_BUILD_TYPE=Release"
|
||||
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
cmake_d="$cmake_d -D USE_STATIC_DEPENDENCIES=OFF"
|
||||
cmake_d="$cmake_d -D OPENVDB_CORE_SHARED=ON"
|
||||
cmake_d="$cmake_d -D OPENVDB_CORE_STATIC=OFF"
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_BINARIES=OFF"
|
||||
|
||||
if [ "$WITH_NANOVDB" = true ]; then
|
||||
cmake_d="$cmake_d -D USE_NANOVDB=ON"
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_NANOVDB=ON"
|
||||
cmake_d="$cmake_d -D NANOVDB_BUILD_TOOLS=OFF"
|
||||
else
|
||||
cmake_d="$cmake_d -D USE_NANOVDB=OFF"
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_NANOVDB=OFF"
|
||||
cmake_d="$cmake_d -D NANOVDB_BUILD_TOOLS=OFF"
|
||||
fi
|
||||
|
||||
if [ -d $INST/boost ]; then
|
||||
|
@ -2982,6 +3069,13 @@ compile_OPENVDB() {
|
|||
cmake_d="$cmake_d -D Blosc_ROOT=$INST/blosc"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_PYTHON_MODULE=ON"
|
||||
cmake_d="$cmake_d -D OPENVDB_PYTHON_WRAP_ALL_GRID_TYPES=ON"
|
||||
cmake_d="$cmake_d -D USE_NUMPY=ON"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
cmake $cmake_d ..
|
||||
|
||||
make -j$THREADS install
|
||||
|
@ -3119,6 +3213,103 @@ compile_ALEMBIC() {
|
|||
run_ldconfig "alembic"
|
||||
}
|
||||
|
||||
#### Build materialX ####
|
||||
_init_materialx() {
|
||||
_src=$SRC/MaterialX-$MATERIALX_VERSION
|
||||
_git=false
|
||||
_inst=$INST/materialx-$MATERIALX_VERSION_SHORT
|
||||
_inst_shortcut=$INST/materialx
|
||||
}
|
||||
|
||||
_update_deps_materialx() {
|
||||
:
|
||||
}
|
||||
|
||||
clean_MATERIALX() {
|
||||
_init_materialx
|
||||
if [ -d $_inst ]; then
|
||||
# Force rebuilding the dependencies if needed.
|
||||
_update_deps_materialx false true
|
||||
fi
|
||||
_clean
|
||||
}
|
||||
|
||||
compile_MATERIALX() {
|
||||
if [ "$NO_BUILD" = true ]; then
|
||||
WARNING "--no-build enabled, MaterialX will not be compiled!"
|
||||
return
|
||||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
materialx_magic=1
|
||||
_init_materialx
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
_update_deps_materialx true false
|
||||
|
||||
# Clean install if needed!
|
||||
magic_compile_check materialx-$MATERIALX_VERSION $materialx_magic
|
||||
if [ $? -eq 1 -o "$MATERIALX_FORCE_REBUILD" = true ]; then
|
||||
clean_MATERIALX
|
||||
fi
|
||||
|
||||
if [ ! -d $_inst ]; then
|
||||
INFO "Building MaterialX-$MATERIALX_VERSION"
|
||||
|
||||
# Force rebuilding the dependencies.
|
||||
_update_deps_materialx true true
|
||||
|
||||
prepare_inst
|
||||
|
||||
if [ ! -d $_src ]; then
|
||||
mkdir -p $SRC
|
||||
download MATERIALX_SOURCE[@] "$_src.tar.gz"
|
||||
|
||||
INFO "Unpacking MaterialX-$MATERIALX_VERSION"
|
||||
tar -C $SRC -xf $_src.tar.gz
|
||||
|
||||
patch -d $_src -p1 < $SCRIPT_DIR/patches/materialx.diff
|
||||
fi
|
||||
|
||||
cd $_src
|
||||
|
||||
cmake_d="-D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
|
||||
cmake_d="$cmake_d -DMATERIALX_BUILD_SHARED_LIBS=ON"
|
||||
cmake_d="$cmake_d -DCMAKE_DEBUG_POSTFIX=_d"
|
||||
|
||||
cmake_d="$cmake_d -DMATERIALX_BUILD_RENDER=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DMATERIALX_BUILD_PYTHON=ON"
|
||||
cmake_d="$cmake_d -DMATERIALX_INSTALL_PYTHON=OFF"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
cmake $cmake_d ./
|
||||
make -j$THREADS install
|
||||
make clean
|
||||
|
||||
if [ ! -d $_inst ]; then
|
||||
ERROR "MaterialX-$MATERIALX_VERSION failed to compile, exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
magic_compile_set materialx-$MATERIALX_VERSION $materialx_magic
|
||||
|
||||
cd $CWD
|
||||
INFO "Done compiling MaterialX-$MATERIALX_VERSION!"
|
||||
else
|
||||
INFO "Own MaterialX-$MATERIALX_VERSION is up to date, nothing to do!"
|
||||
INFO "If you want to force rebuild of this lib, use the --force-materialx option."
|
||||
fi
|
||||
|
||||
if [ -d $_inst ]; then
|
||||
_create_inst_shortcut
|
||||
fi
|
||||
run_ldconfig "materialx"
|
||||
}
|
||||
|
||||
#### Build USD ####
|
||||
_init_usd() {
|
||||
_src=$SRC/USD-$USD_VERSION
|
||||
|
@ -3147,7 +3338,7 @@ compile_USD() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
usd_magic=1
|
||||
usd_magic=2
|
||||
_init_usd
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -3181,18 +3372,46 @@ compile_USD() {
|
|||
cmake_d="-D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
# For the reasoning behind these options, please see usd.cmake.
|
||||
if [ -d $INST/boost ]; then
|
||||
cmake_d="$cmake_d $cmake_d -D BOOST_ROOT=$INST/boost"
|
||||
cmake_d="$cmake_d -DBOOST_ROOT=$INST/boost"
|
||||
fi
|
||||
|
||||
if [ -d $INST/tbb ]; then
|
||||
cmake_d="$cmake_d $cmake_d -D TBB_ROOT_DIR=$INST/tbb"
|
||||
cmake_d="$cmake_d -DTBB_ROOT_DIR=$INST/tbb"
|
||||
fi
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_PYTHON_SUPPORT=OFF"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_IMAGING=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_PYTHON_SUPPORT=ON"
|
||||
cmake_d="$cmake_d -DPXR_USE_PYTHON_3=ON"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D PYTHON_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_IMAGING=ON"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_OPENIMAGEIO_PLUGIN=ON"
|
||||
if [ -d $INST/openexr ]; then
|
||||
cmake_d="$cmake_d -DOPENEXR_LOCATION=$INST/openexr"
|
||||
fi
|
||||
if [ -d $INST/oiio ]; then
|
||||
cmake_d="$cmake_d -DOpenImageIO_ROOT=$INST/oiio"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_OPENVDB_SUPPORT=ON"
|
||||
if [ -d $INST/openvdb ]; then
|
||||
cmake_d="$cmake_d -DOPENVDB_LOCATION=$INST/openvdb"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_GL_SUPPORT=ON"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_TESTS=OFF"
|
||||
cmake_d="$cmake_d -DBUILD_SHARED_LIBS=ON"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_MONOLITHIC=ON"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_EXAMPLES=OFF"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_TUTORIALS=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_USD_TOOLS=OFF"
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_HDF5_SUPPORT=OFF"
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_MATERIALX_SUPPORT=OFF"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_USDVIEW=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_MONOLITHIC=ON"
|
||||
cmake_d="$cmake_d -DBUILD_SHARED_LIBS=ON"
|
||||
cmake_d="$cmake_d -DCMAKE_DEBUG_POSTFIX=_d"
|
||||
|
||||
cmake $cmake_d ./
|
||||
|
@ -4205,11 +4424,12 @@ install_DEB() {
|
|||
git libfreetype6-dev libfontconfig-dev libx11-dev flex bison libxxf86vm-dev \
|
||||
libxcursor-dev libxi-dev wget libsqlite3-dev libxrandr-dev libxinerama-dev \
|
||||
libwayland-dev libdecor-0-dev wayland-protocols libegl-dev libxkbcommon-dev libdbus-1-dev linux-libc-dev \
|
||||
libvulkan-dev libshaderc-dev \
|
||||
libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev \
|
||||
libopenal-dev libepoxy-dev yasm \
|
||||
libopenal-dev libepoxy-dev yasm pybind11-dev \
|
||||
libsdl2-dev libfftw3-dev patch bzip2 libxml2-dev libtinyxml-dev libjemalloc-dev \
|
||||
libgmp-dev libpugixml-dev libpotrace-dev libhpdf-dev libzstd-dev libpystring-dev \
|
||||
libglfw3-dev"
|
||||
libglfw3-dev libfribidi-dev libharfbuzz-dev"
|
||||
|
||||
VORBIS_USE=true
|
||||
OGG_USE=true
|
||||
|
@ -4393,7 +4613,7 @@ install_DEB() {
|
|||
|
||||
boost_version=$(echo `get_package_version_DEB libboost-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
|
||||
|
||||
install_packages_DEB libboost-{filesystem,iostreams,locale,regex,system,thread,wave,program-options}$boost_version-dev
|
||||
install_packages_DEB ${BOOST_DEB_PACKAGE_MODULES[@]/%/$boost_version-dev}
|
||||
clean_Boost
|
||||
else
|
||||
compile_Boost
|
||||
|
@ -4585,6 +4805,16 @@ install_DEB() {
|
|||
compile_ALEMBIC
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
|
@ -4928,10 +5158,12 @@ install_RPM() {
|
|||
libtiff-devel libjpeg-devel libpng-devel sqlite-devel fftw-devel SDL2-devel \
|
||||
libX11-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel \
|
||||
wayland-devel libdecor-devel wayland-protocols-devel mesa-libEGL-devel libxkbcommon-devel dbus-devel kernel-headers \
|
||||
vulkan-loader-devel libshaderc-devel \
|
||||
wget ncurses-devel readline-devel $OPENJPEG_DEV openal-soft-devel \
|
||||
libepoxy-devel yasm patch \
|
||||
libepoxy-devel yasm patch pybind11-devel \
|
||||
libxml2-devel yaml-cpp-devel tinyxml-devel jemalloc-devel \
|
||||
gmp-devel pugixml-devel potrace-devel libharu-devel libzstd-devel pystring-devel"
|
||||
gmp-devel pugixml-devel potrace-devel libharu-devel libzstd-devel pystring-devel \
|
||||
fribidi-devel harfbuzz-devel"
|
||||
|
||||
OPENJPEG_USE=true
|
||||
VORBIS_USE=true
|
||||
|
@ -5312,6 +5544,16 @@ install_RPM() {
|
|||
compile_ALEMBIC
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
|
@ -5582,9 +5824,10 @@ install_ARCH() {
|
|||
|
||||
_packages="$BASE_DEVEL git cmake fontconfig flex \
|
||||
libxi libxcursor libxrandr libxinerama libepoxy libdecor libpng libtiff wget openal \
|
||||
$OPENJPEG_DEV yasm sdl2 fftw \
|
||||
vulkan-icd-loader vulkan-headers shaderc \
|
||||
$OPENJPEG_DEV yasm sdl2 fftw pybind11 \
|
||||
libxml2 yaml-cpp tinyxml python-requests jemalloc gmp potrace pugixml libharu \
|
||||
zstd pystring"
|
||||
zstd pystring fribidi harfbuzz"
|
||||
|
||||
OPENJPEG_USE=true
|
||||
VORBIS_USE=true
|
||||
|
@ -5916,6 +6159,16 @@ install_ARCH() {
|
|||
compile_ALEMBIC
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
|
@ -6207,6 +6460,27 @@ install_OTHER() {
|
|||
fi
|
||||
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
elif [ "$USD_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced USD building, as requested..."
|
||||
compile_USD
|
||||
else
|
||||
compile_USD
|
||||
fi
|
||||
|
||||
|
||||
if [ "$WITH_OPENCOLLADA" = true ]; then
|
||||
PRINT ""
|
||||
if [ "$OPENCOLLADA_SKIP" = true ]; then
|
||||
|
@ -6284,7 +6558,8 @@ print_info() {
|
|||
|
||||
_buildargs="-U *SNDFILE* -U PYTHON* -U *BOOST* -U *Boost* -U *TBB*"
|
||||
_buildargs="$_buildargs -U *OPENCOLORIO* -U *OPENEXR* -U *OPENIMAGEIO* -U *LLVM* -U *CLANG* -U *CYCLES*"
|
||||
_buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *BLOSC* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC* -U *USD*"
|
||||
_buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *BLOSC* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC*"
|
||||
_buildargs="$_buildargs -U *MATERIALX* -U *USD*"
|
||||
_buildargs="$_buildargs -U *EMBREE* -U *OPENIMAGEDENOISE* -U *OPENXR* -U *OPENPGL*"
|
||||
|
||||
_1="-D WITH_CODEC_SNDFILE=ON"
|
||||
|
@ -6471,6 +6746,17 @@ print_info() {
|
|||
fi
|
||||
fi
|
||||
|
||||
if [ "$MATERIALX_SKIP" = false ]; then
|
||||
_1="-D WITH_MATERIALX=ON"
|
||||
PRINT " $_1"
|
||||
_buildargs="$_buildargs $_1"
|
||||
if [ -d $INST/materialx ]; then
|
||||
_1="-D MaterialX_DIR=$INST/materialx/lib/cmake/MaterialX"
|
||||
PRINT " $_1"
|
||||
_buildargs="$_buildargs $_1"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$USD_SKIP" = false ]; then
|
||||
_1="-D WITH_USD=ON"
|
||||
PRINT " $_1"
|
||||
|
|
|
@ -49,7 +49,7 @@ update-code:
|
|||
#
|
||||
buildbot:
|
||||
gcc:
|
||||
version: '9.0.0'
|
||||
version: '11.0.0'
|
||||
cuda10:
|
||||
version: '10.1.243'
|
||||
cuda11:
|
||||
|
|
|
@ -1543,6 +1543,17 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
|||
default=False,
|
||||
)
|
||||
|
||||
kernel_optimization_level: EnumProperty(
|
||||
name="Kernel Optimization",
|
||||
description="Kernels can be optimized based on scene content. Optimized kernels are requested at the start of a render. If optimized kernels are not available, rendering will proceed using generic kernels until the optimized set is available in the cache. This can result in additional CPU usage for a brief time (tens of seconds).",
|
||||
default='FULL',
|
||||
items=(
|
||||
('OFF', "Off", "Disable kernel optimization. Slowest rendering, no extra background CPU usage"),
|
||||
('INTERSECT', "Intersection only", "Optimize only intersection kernels. Faster rendering, negligible extra background CPU usage"),
|
||||
('FULL', "Full", "Optimize all kernels. Fastest rendering, may result in extra background CPU usage"),
|
||||
),
|
||||
)
|
||||
|
||||
def find_existing_device_entry(self, device):
|
||||
for device_entry in self.devices:
|
||||
if device_entry.id == device[2] and device_entry.type == device[1]:
|
||||
|
@ -1711,10 +1722,12 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
|||
if compute_device_type == 'METAL':
|
||||
import platform
|
||||
# MetalRT only works on Apple Silicon at present, pending argument encoding fixes on AMD
|
||||
# Kernel specialization is only viable on Apple Silicon at present due to relative compilation speed
|
||||
if platform.machine() == 'arm64':
|
||||
row = layout.row()
|
||||
row.use_property_split = True
|
||||
row.prop(self, "use_metalrt")
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.prop(self, "kernel_optimization_level")
|
||||
col.prop(self, "use_metalrt")
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_impl(self.layout, context)
|
||||
|
|
|
@ -30,7 +30,10 @@ int blender_device_threads(BL::Scene &b_scene)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scene, bool background)
|
||||
DeviceInfo blender_device_info(BL::Preferences &b_preferences,
|
||||
BL::Scene &b_scene,
|
||||
bool background,
|
||||
bool preview)
|
||||
{
|
||||
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
|
||||
|
||||
|
@ -113,6 +116,18 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
|
|||
device.use_metalrt = true;
|
||||
}
|
||||
|
||||
if (preview) {
|
||||
/* Disable specialization for preview renders. */
|
||||
device.kernel_optimization_level = KERNEL_OPTIMIZATION_LEVEL_OFF;
|
||||
}
|
||||
else {
|
||||
device.kernel_optimization_level = (KernelOptimizationLevel)get_enum(
|
||||
cpreferences,
|
||||
"kernel_optimization_level",
|
||||
KERNEL_OPTIMIZATION_NUM_LEVELS,
|
||||
KERNEL_OPTIMIZATION_LEVEL_FULL);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ int blender_device_threads(BL::Scene &b_scene);
|
|||
/* Convert Blender settings to device specification. */
|
||||
DeviceInfo blender_device_info(BL::Preferences &b_preferences,
|
||||
BL::Scene &b_scene,
|
||||
bool background);
|
||||
bool background,
|
||||
bool preview);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -754,7 +754,7 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key
|
|||
RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyscene), &sceneptr);
|
||||
BL::Scene b_scene(sceneptr);
|
||||
|
||||
DeviceInfo device = blender_device_info(b_preferences, b_scene, true);
|
||||
DeviceInfo device = blender_device_info(b_preferences, b_scene, true, true);
|
||||
|
||||
/* Get denoising parameters from view layer. */
|
||||
PointerRNA viewlayerptr;
|
||||
|
|
|
@ -866,7 +866,8 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
|
|||
|
||||
/* Device */
|
||||
params.threads = blender_device_threads(b_scene);
|
||||
params.device = blender_device_info(b_preferences, b_scene, params.background);
|
||||
params.device = blender_device_info(
|
||||
b_preferences, b_scene, params.background, b_engine.is_preview());
|
||||
|
||||
/* samples */
|
||||
int samples = get_int(cscene, "samples");
|
||||
|
|
|
@ -57,22 +57,32 @@ enum DeviceTypeMask {
|
|||
|
||||
#define DEVICE_MASK(type) (DeviceTypeMask)(1 << type)
|
||||
|
||||
enum KernelOptimizationLevel {
|
||||
KERNEL_OPTIMIZATION_LEVEL_OFF = 0,
|
||||
KERNEL_OPTIMIZATION_LEVEL_INTERSECT = 1,
|
||||
KERNEL_OPTIMIZATION_LEVEL_FULL = 2,
|
||||
|
||||
KERNEL_OPTIMIZATION_NUM_LEVELS
|
||||
};
|
||||
|
||||
class DeviceInfo {
|
||||
public:
|
||||
DeviceType type;
|
||||
string description;
|
||||
string id; /* used for user preferences, should stay fixed with changing hardware config */
|
||||
int num;
|
||||
bool display_device; /* GPU is used as a display device. */
|
||||
bool has_nanovdb; /* Support NanoVDB volumes. */
|
||||
bool has_light_tree; /* Support light tree. */
|
||||
bool has_osl; /* Support Open Shading Language. */
|
||||
bool has_guiding; /* Support path guiding. */
|
||||
bool has_profiling; /* Supports runtime collection of profiling info. */
|
||||
bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */
|
||||
bool has_gpu_queue; /* Device supports GPU queue. */
|
||||
bool use_metalrt; /* Use MetalRT to accelerate ray queries (Metal only). */
|
||||
DenoiserTypeMask denoisers; /* Supported denoiser types. */
|
||||
bool display_device; /* GPU is used as a display device. */
|
||||
bool has_nanovdb; /* Support NanoVDB volumes. */
|
||||
bool has_light_tree; /* Support light tree. */
|
||||
bool has_osl; /* Support Open Shading Language. */
|
||||
bool has_guiding; /* Support path guiding. */
|
||||
bool has_profiling; /* Supports runtime collection of profiling info. */
|
||||
bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */
|
||||
bool has_gpu_queue; /* Device supports GPU queue. */
|
||||
bool use_metalrt; /* Use MetalRT to accelerate ray queries (Metal only). */
|
||||
KernelOptimizationLevel kernel_optimization_level; /* Optimization level applied to path tracing
|
||||
kernels (Metal only). */
|
||||
DenoiserTypeMask denoisers; /* Supported denoiser types. */
|
||||
int cpu_threads;
|
||||
vector<DeviceInfo> multi_devices;
|
||||
string error_msg;
|
||||
|
|
|
@ -22,7 +22,7 @@ class MetalDevice;
|
|||
thread_mutex MetalDevice::existing_devices_mutex;
|
||||
std::map<int, MetalDevice *> MetalDevice::active_device_ids;
|
||||
|
||||
/* Thread-safe device access for async work. Calling code must pass an appropriatelty scoped lock
|
||||
/* Thread-safe device access for async work. Calling code must pass an appropriately scoped lock
|
||||
* to existing_devices_mutex to safeguard against destruction of the returned instance. */
|
||||
MetalDevice *MetalDevice::get_device_by_ID(int ID, thread_scoped_lock &existing_devices_mutex_lock)
|
||||
{
|
||||
|
@ -110,10 +110,6 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
|||
case METAL_GPU_APPLE: {
|
||||
max_threads_per_threadgroup = 512;
|
||||
use_metalrt = info.use_metalrt;
|
||||
|
||||
/* Specialize the intersection kernels on Apple GPUs by default as these can be built very
|
||||
* quickly. */
|
||||
kernel_specialization_level = PSO_SPECIALIZED_INTERSECT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +122,22 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
|||
capture_enabled = true;
|
||||
}
|
||||
|
||||
if (device_vendor == METAL_GPU_APPLE) {
|
||||
/* Set kernel_specialization_level based on user prefs. */
|
||||
switch (info.kernel_optimization_level) {
|
||||
case KERNEL_OPTIMIZATION_LEVEL_OFF:
|
||||
kernel_specialization_level = PSO_GENERIC;
|
||||
break;
|
||||
default:
|
||||
case KERNEL_OPTIMIZATION_LEVEL_INTERSECT:
|
||||
kernel_specialization_level = PSO_SPECIALIZED_INTERSECT;
|
||||
break;
|
||||
case KERNEL_OPTIMIZATION_LEVEL_FULL:
|
||||
kernel_specialization_level = PSO_SPECIALIZED_SHADE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto envstr = getenv("CYCLES_METAL_SPECIALIZATION_LEVEL")) {
|
||||
kernel_specialization_level = (MetalPipelineType)atoi(envstr);
|
||||
}
|
||||
|
@ -365,7 +377,7 @@ bool MetalDevice::load_kernels(const uint _kernel_features)
|
|||
|
||||
/* Only request generic kernels if they aren't cached in memory. */
|
||||
if (make_source_and_check_if_compile_needed(PSO_GENERIC)) {
|
||||
/* If needed, load them asynchronously in order to responsively message progess to the user. */
|
||||
/* If needed, load them asynchronously in order to responsively message progress to the user. */
|
||||
int this_device_id = this->device_id;
|
||||
auto compile_kernels_fn = ^() {
|
||||
compile_and_load(this_device_id, PSO_GENERIC);
|
||||
|
@ -389,8 +401,7 @@ bool MetalDevice::make_source_and_check_if_compile_needed(MetalPipelineType pso_
|
|||
void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
|
||||
{
|
||||
/* Thread-safe front-end compilation. Typically the MSL->AIR compilation can take a few seconds,
|
||||
* so we avoid blocking device teardown if the user cancels a render immediately.
|
||||
*/
|
||||
* so we avoid blocking device tear-down if the user cancels a render immediately. */
|
||||
|
||||
id<MTLDevice> mtlDevice;
|
||||
string source;
|
||||
|
@ -444,7 +455,7 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
|
|||
source);
|
||||
}
|
||||
|
||||
const double starttime = time_dt();
|
||||
double starttime = time_dt();
|
||||
|
||||
NSError *error = NULL;
|
||||
id<MTLLibrary> mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str())
|
||||
|
@ -457,6 +468,13 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
|
|||
|
||||
[options release];
|
||||
|
||||
bool blocking_pso_build = (getenv("CYCLES_METAL_PROFILING") ||
|
||||
MetalDeviceKernels::is_benchmark_warmup());
|
||||
if (blocking_pso_build) {
|
||||
MetalDeviceKernels::wait_for_all();
|
||||
starttime = 0.0;
|
||||
}
|
||||
|
||||
/* Save the compiled MTLLibrary and trigger the AIR->PSO builds (if the MetalDevice still
|
||||
* exists). */
|
||||
{
|
||||
|
@ -464,6 +482,8 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
|
|||
if (MetalDevice *instance = get_device_by_ID(device_id, lock)) {
|
||||
if (mtlLibrary) {
|
||||
instance->mtlLibrary[pso_type] = mtlLibrary;
|
||||
|
||||
starttime = time_dt();
|
||||
MetalDeviceKernels::load(instance, pso_type);
|
||||
}
|
||||
else {
|
||||
|
@ -472,6 +492,14 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (starttime && blocking_pso_build) {
|
||||
MetalDeviceKernels::wait_for_all();
|
||||
|
||||
metal_printf("Back-end compilation finished in %.1f seconds (%s)\n",
|
||||
time_dt() - starttime,
|
||||
kernel_type_as_string(pso_type));
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDevice::load_texture_info()
|
||||
|
@ -832,10 +860,8 @@ void MetalDevice::optimize_for_scene(Scene *scene)
|
|||
}
|
||||
|
||||
/* Block during benchmark warm-up to ensure kernels are cached prior to the observed run. */
|
||||
for (int i = 0; i < *_NSGetArgc(); i++) {
|
||||
if (!strcmp((*_NSGetArgv())[i], "--warm-up")) {
|
||||
specialize_in_background = false;
|
||||
}
|
||||
if (MetalDeviceKernels::is_benchmark_warmup()) {
|
||||
specialize_in_background = false;
|
||||
}
|
||||
|
||||
if (specialize_in_background) {
|
||||
|
|
|
@ -101,6 +101,8 @@ int get_loaded_kernel_count(MetalDevice const *device, MetalPipelineType pso_typ
|
|||
bool should_load_kernels(MetalDevice const *device, MetalPipelineType pso_type);
|
||||
bool load(MetalDevice *device, MetalPipelineType pso_type);
|
||||
const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel);
|
||||
void wait_for_all();
|
||||
bool is_benchmark_warmup();
|
||||
|
||||
} /* namespace MetalDeviceKernels */
|
||||
|
||||
|
|
|
@ -116,19 +116,29 @@ struct ShaderCache {
|
|||
};
|
||||
|
||||
bool ShaderCache::running = true;
|
||||
std::mutex g_shaderCacheMutex;
|
||||
std::map<id<MTLDevice>, unique_ptr<ShaderCache>> g_shaderCache;
|
||||
|
||||
const int MAX_POSSIBLE_GPUS_ON_SYSTEM = 8;
|
||||
using DeviceShaderCache = std::pair<id<MTLDevice>, unique_ptr<ShaderCache>>;
|
||||
int g_shaderCacheCount = 0;
|
||||
DeviceShaderCache g_shaderCache[MAX_POSSIBLE_GPUS_ON_SYSTEM];
|
||||
|
||||
ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice)
|
||||
{
|
||||
thread_scoped_lock lock(g_shaderCacheMutex);
|
||||
auto it = g_shaderCache.find(mtlDevice);
|
||||
if (it != g_shaderCache.end()) {
|
||||
return it->second.get();
|
||||
for (int i = 0; i < g_shaderCacheCount; i++) {
|
||||
if (g_shaderCache[i].first == mtlDevice) {
|
||||
return g_shaderCache[i].second.get();
|
||||
}
|
||||
}
|
||||
|
||||
g_shaderCache[mtlDevice] = make_unique<ShaderCache>(mtlDevice);
|
||||
return g_shaderCache[mtlDevice].get();
|
||||
static thread_mutex g_shaderCacheCountMutex;
|
||||
g_shaderCacheCountMutex.lock();
|
||||
int index = g_shaderCacheCount++;
|
||||
g_shaderCacheCountMutex.unlock();
|
||||
|
||||
assert(index < MAX_POSSIBLE_GPUS_ON_SYSTEM);
|
||||
g_shaderCache[index].first = mtlDevice;
|
||||
g_shaderCache[index].second = make_unique<ShaderCache>(mtlDevice);
|
||||
return g_shaderCache[index].second.get();
|
||||
}
|
||||
|
||||
ShaderCache::~ShaderCache()
|
||||
|
@ -145,7 +155,7 @@ ShaderCache::~ShaderCache()
|
|||
num_incomplete = int(incomplete_requests);
|
||||
}
|
||||
|
||||
if (num_incomplete) {
|
||||
if (num_incomplete && !MetalDeviceKernels::is_benchmark_warmup()) {
|
||||
metal_printf("ShaderCache still busy (incomplete_requests = %d). Terminating...\n",
|
||||
num_incomplete);
|
||||
std::terminate();
|
||||
|
@ -313,17 +323,15 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
|||
pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup;
|
||||
|
||||
if (occupancy_tuning[device_kernel].threads_per_threadgroup) {
|
||||
pipeline->threads_per_threadgroup =
|
||||
occupancy_tuning[device_kernel].threads_per_threadgroup;
|
||||
pipeline->num_threads_per_block =
|
||||
occupancy_tuning[device_kernel].num_threads_per_block;
|
||||
pipeline->threads_per_threadgroup = occupancy_tuning[device_kernel].threads_per_threadgroup;
|
||||
pipeline->num_threads_per_block = occupancy_tuning[device_kernel].num_threads_per_block;
|
||||
}
|
||||
|
||||
/* metalrt options */
|
||||
pipeline->use_metalrt = device->use_metalrt;
|
||||
pipeline->metalrt_features = device->use_metalrt ?
|
||||
(device->kernel_features & METALRT_FEATURE_MASK) :
|
||||
0;
|
||||
(device->kernel_features & METALRT_FEATURE_MASK) :
|
||||
0;
|
||||
|
||||
{
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
|
@ -334,12 +342,6 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
|||
|
||||
MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const MetalDevice *device)
|
||||
{
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
auto &collection = pipelines[kernel];
|
||||
if (collection.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* metalrt options */
|
||||
bool use_metalrt = device->use_metalrt;
|
||||
bool device_metalrt_hair = use_metalrt && device->kernel_features & KERNEL_FEATURE_HAIR;
|
||||
|
@ -351,34 +353,43 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M
|
|||
device->kernel_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||
|
||||
MetalKernelPipeline *best_pipeline = nullptr;
|
||||
for (auto &pipeline : collection) {
|
||||
if (!pipeline->loaded) {
|
||||
/* still loading - ignore */
|
||||
continue;
|
||||
}
|
||||
while (!best_pipeline) {
|
||||
{
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
for (auto &pipeline : pipelines[kernel]) {
|
||||
if (!pipeline->loaded) {
|
||||
/* still loading - ignore */
|
||||
continue;
|
||||
}
|
||||
|
||||
bool pipeline_metalrt_hair = pipeline->metalrt_features & KERNEL_FEATURE_HAIR;
|
||||
bool pipeline_metalrt_hair_thick = pipeline->metalrt_features & KERNEL_FEATURE_HAIR_THICK;
|
||||
bool pipeline_metalrt_pointcloud = pipeline->metalrt_features & KERNEL_FEATURE_POINTCLOUD;
|
||||
bool pipeline_metalrt_motion = use_metalrt &&
|
||||
pipeline->metalrt_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||
bool pipeline_metalrt_hair = pipeline->metalrt_features & KERNEL_FEATURE_HAIR;
|
||||
bool pipeline_metalrt_hair_thick = pipeline->metalrt_features & KERNEL_FEATURE_HAIR_THICK;
|
||||
bool pipeline_metalrt_pointcloud = pipeline->metalrt_features & KERNEL_FEATURE_POINTCLOUD;
|
||||
bool pipeline_metalrt_motion = use_metalrt &&
|
||||
pipeline->metalrt_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||
|
||||
if (pipeline->use_metalrt != use_metalrt || pipeline_metalrt_hair != device_metalrt_hair ||
|
||||
pipeline_metalrt_hair_thick != device_metalrt_hair_thick ||
|
||||
pipeline_metalrt_pointcloud != device_metalrt_pointcloud ||
|
||||
pipeline_metalrt_motion != device_metalrt_motion) {
|
||||
/* wrong combination of metalrt options */
|
||||
continue;
|
||||
}
|
||||
if (pipeline->use_metalrt != use_metalrt || pipeline_metalrt_hair != device_metalrt_hair ||
|
||||
pipeline_metalrt_hair_thick != device_metalrt_hair_thick ||
|
||||
pipeline_metalrt_pointcloud != device_metalrt_pointcloud ||
|
||||
pipeline_metalrt_motion != device_metalrt_motion) {
|
||||
/* wrong combination of metalrt options */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pipeline->pso_type != PSO_GENERIC) {
|
||||
if (pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_INTERSECT] ||
|
||||
pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_SHADE]) {
|
||||
best_pipeline = pipeline.get();
|
||||
if (pipeline->pso_type != PSO_GENERIC) {
|
||||
if (pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_INTERSECT] ||
|
||||
pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_SHADE]) {
|
||||
best_pipeline = pipeline.get();
|
||||
}
|
||||
}
|
||||
else if (!best_pipeline) {
|
||||
best_pipeline = pipeline.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!best_pipeline) {
|
||||
best_pipeline = pipeline.get();
|
||||
|
||||
if (!best_pipeline) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -742,8 +753,7 @@ void MetalKernelPipeline::compile()
|
|||
if (!num_threads_per_block) {
|
||||
num_threads_per_block = round_down(pipeline.maxTotalThreadsPerThreadgroup,
|
||||
pipeline.threadExecutionWidth);
|
||||
num_threads_per_block = std::max(num_threads_per_block,
|
||||
(int)pipeline.threadExecutionWidth);
|
||||
num_threads_per_block = std::max(num_threads_per_block, (int)pipeline.threadExecutionWidth);
|
||||
}
|
||||
|
||||
if (@available(macOS 11.0, *)) {
|
||||
|
@ -805,28 +815,26 @@ void MetalKernelPipeline::compile()
|
|||
|
||||
bool MetalDeviceKernels::load(MetalDevice *device, MetalPipelineType pso_type)
|
||||
{
|
||||
const double starttime = time_dt();
|
||||
auto shader_cache = get_shader_cache(device->mtlDevice);
|
||||
for (int i = 0; i < DEVICE_KERNEL_NUM; i++) {
|
||||
shader_cache->load_kernel((DeviceKernel)i, device, pso_type);
|
||||
}
|
||||
|
||||
if (getenv("CYCLES_METAL_PROFILING")) {
|
||||
shader_cache->wait_for_all();
|
||||
metal_printf("Back-end compilation finished in %.1f seconds (%s)\n",
|
||||
time_dt() - starttime,
|
||||
kernel_type_as_string(pso_type));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalDeviceKernels::wait_for_all()
|
||||
{
|
||||
for (int i = 0; i < g_shaderCacheCount; i++) {
|
||||
g_shaderCache[i].second->wait_for_all();
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalDeviceKernels::any_specialization_happening_now()
|
||||
{
|
||||
/* Return true if any ShaderCaches have ongoing specialization requests (typically there will be
|
||||
* only 1). */
|
||||
thread_scoped_lock lock(g_shaderCacheMutex);
|
||||
for (auto &it : g_shaderCache) {
|
||||
if (it.second->incomplete_specialization_requests > 0) {
|
||||
for (int i = 0; i < g_shaderCacheCount; i++) {
|
||||
if (g_shaderCache[i].second->incomplete_specialization_requests > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -857,6 +865,19 @@ const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevi
|
|||
return get_shader_cache(device->mtlDevice)->get_best_pipeline(kernel, device);
|
||||
}
|
||||
|
||||
bool MetalDeviceKernels::is_benchmark_warmup()
|
||||
{
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
for (int i = 0; i < args.count; i++) {
|
||||
if (const char *arg = [[args objectAtIndex:i] cStringUsingEncoding:NSASCIIStringEncoding]) {
|
||||
if (!strcmp(arg, "--warm-up")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_METAL*/
|
||||
|
|
|
@ -202,6 +202,9 @@ MetalDeviceQueue::~MetalDeviceQueue()
|
|||
assert(mtlCommandBuffer_ == nil);
|
||||
assert(command_buffers_submitted_ == command_buffers_completed_);
|
||||
|
||||
close_compute_encoder();
|
||||
close_blit_encoder();
|
||||
|
||||
if (@available(macos 10.14, *)) {
|
||||
[shared_event_listener_ release];
|
||||
[shared_event_ release];
|
||||
|
@ -637,9 +640,7 @@ bool MetalDeviceQueue::synchronize()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mtlComputeEncoder_) {
|
||||
close_compute_encoder();
|
||||
}
|
||||
close_compute_encoder();
|
||||
close_blit_encoder();
|
||||
|
||||
if (mtlCommandBuffer_) {
|
||||
|
@ -855,9 +856,7 @@ id<MTLComputeCommandEncoder> MetalDeviceQueue::get_compute_encoder(DeviceKernel
|
|||
if (@available(macos 10.14, *)) {
|
||||
if (timing_shared_event_) {
|
||||
/* Close the current encoder to ensure we're able to capture per-encoder timing data. */
|
||||
if (mtlComputeEncoder_) {
|
||||
close_compute_encoder();
|
||||
}
|
||||
close_compute_encoder();
|
||||
}
|
||||
|
||||
if (mtlComputeEncoder_) {
|
||||
|
@ -897,9 +896,7 @@ id<MTLBlitCommandEncoder> MetalDeviceQueue::get_blit_encoder()
|
|||
return mtlBlitEncoder_;
|
||||
}
|
||||
|
||||
if (mtlComputeEncoder_) {
|
||||
close_compute_encoder();
|
||||
}
|
||||
close_compute_encoder();
|
||||
|
||||
if (!mtlCommandBuffer_) {
|
||||
mtlCommandBuffer_ = [mtlCommandQueue_ commandBuffer];
|
||||
|
@ -913,12 +910,14 @@ id<MTLBlitCommandEncoder> MetalDeviceQueue::get_blit_encoder()
|
|||
|
||||
void MetalDeviceQueue::close_compute_encoder()
|
||||
{
|
||||
[mtlComputeEncoder_ endEncoding];
|
||||
mtlComputeEncoder_ = nil;
|
||||
if (mtlComputeEncoder_) {
|
||||
[mtlComputeEncoder_ endEncoding];
|
||||
mtlComputeEncoder_ = nil;
|
||||
|
||||
if (@available(macos 10.14, *)) {
|
||||
if (timing_shared_event_) {
|
||||
[mtlCommandBuffer_ encodeSignalEvent:timing_shared_event_ value:timing_shared_event_id_++];
|
||||
if (@available(macos 10.14, *)) {
|
||||
if (timing_shared_event_) {
|
||||
[mtlCommandBuffer_ encodeSignalEvent:timing_shared_event_ value:timing_shared_event_id_++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -364,7 +364,9 @@ ccl_device_inline void film_write_emission_or_background_pass(
|
|||
}
|
||||
# endif /* __DENOISING_FEATURES__ */
|
||||
|
||||
if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) {
|
||||
const bool is_shadowcatcher = (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) != 0;
|
||||
if (!is_shadowcatcher && lightgroup != LIGHTGROUP_NONE &&
|
||||
kernel_data.film.pass_lightgroup != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup,
|
||||
contribution);
|
||||
}
|
||||
|
@ -373,13 +375,12 @@ ccl_device_inline void film_write_emission_or_background_pass(
|
|||
/* Directly visible, write to emission or background pass. */
|
||||
pass_offset = pass;
|
||||
}
|
||||
else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
else if (is_shadowcatcher) {
|
||||
/* Don't write any light passes for shadow catcher, for easier
|
||||
* compositing back together of the combined pass. */
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const Spectrum diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
|
|
|
@ -180,8 +180,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
|
|||
|
||||
/* Write to render buffer. */
|
||||
guiding_record_background(kg, state, light_eval, mis_weight);
|
||||
film_write_surface_emission(
|
||||
kg, state, light_eval, mis_weight, render_buffer, kernel_data.background.lightgroup);
|
||||
film_write_surface_emission(kg, state, light_eval, mis_weight, render_buffer, ls.group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -263,8 +263,9 @@ static float3 output_estimate_emission(ShaderOutput *output, bool &is_constant)
|
|||
|
||||
return estimate;
|
||||
}
|
||||
else if (node->type == LightFalloffNode::get_node_type()) {
|
||||
/* Light Falloff node. */
|
||||
else if (node->type == LightFalloffNode::get_node_type() ||
|
||||
node->type == IESLightNode::get_node_type()) {
|
||||
/* Get strength from Light Falloff and IES texture node. */
|
||||
ShaderInput *strength_in = node->input("Strength");
|
||||
is_constant = false;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
|
@ -14,10 +15,18 @@
|
|||
|
||||
namespace {
|
||||
|
||||
struct Local;
|
||||
struct Global;
|
||||
|
||||
/**
|
||||
* This is stored per thread. Align to cache line size to avoid false sharing.
|
||||
*/
|
||||
struct alignas(64) Local {
|
||||
struct alignas(128) Local {
|
||||
/**
|
||||
* Retain shared ownership of #Global to make sure that it is not destructed.
|
||||
*/
|
||||
std::shared_ptr<Global> global;
|
||||
|
||||
/** Helps to find bugs during program shutdown. */
|
||||
bool destructed = false;
|
||||
/**
|
||||
|
@ -49,7 +58,8 @@ struct alignas(64) Local {
|
|||
};
|
||||
|
||||
/**
|
||||
* This is a singleton that stores global data.
|
||||
* This is a singleton that stores global data. It's owned by a `std::shared_ptr` which is owned by
|
||||
* the static variable in #get_global_ptr and all #Local objects.
|
||||
*/
|
||||
struct Global {
|
||||
/**
|
||||
|
@ -71,7 +81,7 @@ struct Global {
|
|||
* To solve this, the memory counts are added to these global counters when the thread
|
||||
* exists. The global counters are also used when the entire process starts to exit, because the
|
||||
* #Local data of the main thread is already destructed when the leak detection happens (during
|
||||
* destruction of static variables which happens after destruction of threadlocals).
|
||||
* destruction of static variables which happens after destruction of thread-locals).
|
||||
*/
|
||||
std::atomic<int64_t> mem_in_use_outside_locals = 0;
|
||||
/**
|
||||
|
@ -98,10 +108,15 @@ static std::atomic<bool> use_local_counters = true;
|
|||
*/
|
||||
static constexpr int64_t peak_update_threshold = 1024 * 1024;
|
||||
|
||||
static std::shared_ptr<Global> &get_global_ptr()
|
||||
{
|
||||
static std::shared_ptr<Global> global = std::make_shared<Global>();
|
||||
return global;
|
||||
}
|
||||
|
||||
static Global &get_global()
|
||||
{
|
||||
static Global global;
|
||||
return global;
|
||||
return *get_global_ptr();
|
||||
}
|
||||
|
||||
static Local &get_local_data()
|
||||
|
@ -113,32 +128,32 @@ static Local &get_local_data()
|
|||
|
||||
Local::Local()
|
||||
{
|
||||
Global &global = get_global();
|
||||
std::lock_guard lock{global.locals_mutex};
|
||||
this->global = get_global_ptr();
|
||||
|
||||
if (global.locals.empty()) {
|
||||
std::lock_guard lock{this->global->locals_mutex};
|
||||
if (this->global->locals.empty()) {
|
||||
/* This is the first thread creating #Local, it is therefore the main thread because it's
|
||||
* created through #memory_usage_init. */
|
||||
this->is_main = true;
|
||||
}
|
||||
/* Register self in the global list. */
|
||||
global.locals.push_back(this);
|
||||
this->global->locals.push_back(this);
|
||||
}
|
||||
|
||||
Local::~Local()
|
||||
{
|
||||
Global &global = get_global();
|
||||
std::lock_guard lock{global.locals_mutex};
|
||||
std::lock_guard lock{this->global->locals_mutex};
|
||||
|
||||
/* Unregister self from the global list. */
|
||||
global.locals.erase(std::find(global.locals.begin(), global.locals.end(), this));
|
||||
this->global->locals.erase(
|
||||
std::find(this->global->locals.begin(), this->global->locals.end(), this));
|
||||
/* Don't forget the memory counts stored locally. */
|
||||
global.blocks_num_outside_locals.fetch_add(this->blocks_num, std::memory_order_relaxed);
|
||||
global.mem_in_use_outside_locals.fetch_add(this->mem_in_use, std::memory_order_relaxed);
|
||||
this->global->blocks_num_outside_locals.fetch_add(this->blocks_num, std::memory_order_relaxed);
|
||||
this->global->mem_in_use_outside_locals.fetch_add(this->mem_in_use, std::memory_order_relaxed);
|
||||
|
||||
if (this->is_main) {
|
||||
/* The main thread started shutting down. Use global counters from now on to avoid accessing
|
||||
* threadlocals after they have been destructed. */
|
||||
* thread-locals after they have been destructed. */
|
||||
use_local_counters.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
/* Helps to detect when thread locals are accidentally accessed after destruction. */
|
||||
|
@ -164,7 +179,7 @@ static void update_global_peak()
|
|||
|
||||
void memory_usage_init()
|
||||
{
|
||||
/* Makes sure that the static and threadlocal variables on the main thread are initialized. */
|
||||
/* Makes sure that the static and thread-local variables on the main thread are initialized. */
|
||||
get_local_data();
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 96143b1a8b037ea3c81f065f557025db9fe1ace3
|
||||
Subproject commit bdcfdd47ec3451822b21d1cff2ea2db751093c9a
|
|
@ -81,8 +81,8 @@ class ASSET_OT_open_containing_blend_file(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
asset_file_handle = getattr(context, 'asset_file_handle', None)
|
||||
asset_library_ref = getattr(context, 'asset_library_ref', None)
|
||||
asset_file_handle = getattr(context, "asset_file_handle", None)
|
||||
asset_library_ref = getattr(context, "asset_library_ref", None)
|
||||
|
||||
if not asset_library_ref:
|
||||
cls.poll_message_set("No asset library selected")
|
||||
|
|
|
@ -620,7 +620,7 @@ class CLIP_OT_setup_tracking_scene(Operator):
|
|||
if not view_layers.get("Foreground"):
|
||||
if len(view_layers) == 1:
|
||||
fg = view_layers[0]
|
||||
fg.name = 'Foreground'
|
||||
fg.name = "Foreground"
|
||||
else:
|
||||
fg = view_layers.new("Foreground")
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ class SCENE_OT_freestyle_add_edge_marks_to_keying_set(Operator):
|
|||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
for i, edge in enumerate(mesh.edges):
|
||||
if not edge.hide and edge.select:
|
||||
path = 'edges[%d].use_freestyle_mark' % i
|
||||
path = "edges[%d].use_freestyle_mark" % i
|
||||
ks.paths.add(mesh, path, index=0)
|
||||
bpy.ops.object.mode_set(mode=ob_mode, toggle=False)
|
||||
return {'FINISHED'}
|
||||
|
@ -173,7 +173,7 @@ class SCENE_OT_freestyle_add_face_marks_to_keying_set(Operator):
|
|||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
for i, polygon in enumerate(mesh.polygons):
|
||||
if not polygon.hide and polygon.select:
|
||||
path = 'polygons[%d].use_freestyle_mark' % i
|
||||
path = "polygons[%d].use_freestyle_mark" % i
|
||||
ks.paths.add(mesh, path, index=0)
|
||||
bpy.ops.object.mode_set(mode=ob_mode, toggle=False)
|
||||
return {'FINISHED'}
|
||||
|
|
|
@ -223,7 +223,7 @@ class SequencerFadesAdd(Operator):
|
|||
if not self.is_long_enough(sequence, duration):
|
||||
continue
|
||||
|
||||
animated_property = 'volume' if hasattr(sequence, 'volume') else 'blend_alpha'
|
||||
animated_property = "volume" if hasattr(sequence, "volume") else "blend_alpha"
|
||||
fade_fcurve = self.fade_find_or_create_fcurve(context, sequence, animated_property)
|
||||
fades = self.calculate_fades(sequence, fade_fcurve, animated_property, duration)
|
||||
self.fade_animation_clear(fade_fcurve, fades)
|
||||
|
|
|
@ -2507,7 +2507,7 @@ class WM_OT_batch_rename(Operator):
|
|||
('COLLECTION', "Collections", ""),
|
||||
('MATERIAL', "Materials", ""),
|
||||
None,
|
||||
# Enum identifiers are compared with 'object.type'.
|
||||
# Enum identifiers are compared with `object.type`.
|
||||
# Follow order in "Add" menu.
|
||||
('MESH', "Meshes", ""),
|
||||
('CURVE', "Curves", ""),
|
||||
|
|
|
@ -66,7 +66,7 @@ class MotionPathButtonsPanel:
|
|||
col.operator(op_category + ".paths_calculate", text="Calculate...", icon=icon)
|
||||
|
||||
# Update All & Clear All.
|
||||
# Note that 'col' is from inside the preceeding `if` or `else` block.
|
||||
# Note that `col` is from inside the preceding `if` or `else` block.
|
||||
row = col.row(align=True)
|
||||
row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD')
|
||||
row.operator(op_category + ".paths_clear", text="", icon='X').only_selected = False
|
||||
|
|
|
@ -87,7 +87,7 @@ class ConstraintButtonsPanel:
|
|||
@staticmethod
|
||||
def target_template(layout, con, subtargets=True):
|
||||
col = layout.column()
|
||||
col.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object
|
||||
col.prop(con, "target") # XXX: limiting settings for only `curves` or some type of object.
|
||||
|
||||
if con.target and subtargets:
|
||||
if con.target.type == 'ARMATURE':
|
||||
|
|
|
@ -87,7 +87,7 @@ class BONE_PT_transform(BoneButtonsPanel, Panel):
|
|||
row.use_property_decorate = False
|
||||
row.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
||||
row = layout.row(align=True)
|
||||
row.prop(pchan, "rotation_mode", text='Mode')
|
||||
row.prop(pchan, "rotation_mode", text="Mode")
|
||||
row.label(text="", icon='BLANK1')
|
||||
|
||||
col = layout.column()
|
||||
|
@ -403,9 +403,9 @@ class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
|
|||
col.prop(pchan, "ik_rotation_weight", text="IK Rotation Weight", slider=True)
|
||||
col.active = active
|
||||
# not supported yet
|
||||
#row = layout.row()
|
||||
#row.prop(pchan, "use_ik_linear_control", text="Joint Size")
|
||||
#row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
|
||||
# row = layout.row()
|
||||
# row.prop(pchan, "use_ik_linear_control", text="Joint Size")
|
||||
# row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
|
||||
|
||||
|
||||
class BONE_PT_deform(BoneButtonsPanel, Panel):
|
||||
|
|
|
@ -12,7 +12,7 @@ class DataButtonsPanel:
|
|||
@classmethod
|
||||
def poll(cls, context):
|
||||
engine = context.scene.render.engine
|
||||
return hasattr(context, 'curves') and context.curves and (engine in cls.COMPAT_ENGINES)
|
||||
return hasattr(context, "curves") and context.curves and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class DATA_PT_context_curves(DataButtonsPanel, Panel):
|
||||
|
@ -73,8 +73,8 @@ class CURVES_MT_add_attribute(Menu):
|
|||
layout = self.layout
|
||||
curves = context.curves
|
||||
|
||||
self.add_standard_attribute(layout, curves, 'radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, curves, 'color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, curves, "radius", 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, curves, "color", 'FLOAT_COLOR', 'POINT')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -102,8 +102,8 @@ class CURVES_UL_attributes(UIList):
|
|||
return flags, indices
|
||||
|
||||
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
domain = attribute.bl_rna.properties['domain'].enum_items[attribute.domain]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
domain = attribute.bl_rna.properties["domain"].enum_items[attribute.domain]
|
||||
|
||||
split = layout.split(factor=0.5)
|
||||
split.emboss = 'NONE'
|
||||
|
|
|
@ -551,7 +551,7 @@ class MESH_UL_attributes(UIList):
|
|||
return flags, indices
|
||||
|
||||
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
|
||||
domain_name = self.display_domain_names.get(attribute.domain, "")
|
||||
|
||||
|
@ -658,7 +658,7 @@ class ColorAttributesListBase():
|
|||
|
||||
class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
|
||||
def draw_item(self, _context, layout, data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
|
||||
domain_name = self.display_domain_names.get(attribute.domain, "")
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ class DataButtonsPanel:
|
|||
@classmethod
|
||||
def poll(cls, context):
|
||||
engine = context.scene.render.engine
|
||||
return hasattr(context, 'pointcloud') and context.pointcloud and (engine in cls.COMPAT_ENGINES)
|
||||
return hasattr(context, "pointcloud") and context.pointcloud and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class DATA_PT_context_pointcloud(DataButtonsPanel, Panel):
|
||||
|
@ -53,10 +53,10 @@ class POINTCLOUD_MT_add_attribute(Menu):
|
|||
layout = self.layout
|
||||
pointcloud = context.pointcloud
|
||||
|
||||
self.add_standard_attribute(layout, pointcloud, 'radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'id', 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'velocity', 'FLOAT_VECTOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "radius", 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "color", 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "id", 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "velocity", 'FLOAT_VECTOR', 'POINT')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -84,7 +84,7 @@ class POINTCLOUD_UL_attributes(UIList):
|
|||
return flags, indices
|
||||
|
||||
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
|
||||
split = layout.split(factor=0.75)
|
||||
split.emboss = 'NONE'
|
||||
|
|
|
@ -68,7 +68,7 @@ class DATA_PT_volume_file(DataButtonsPanel, Panel):
|
|||
class VOLUME_UL_grids(UIList):
|
||||
def draw_item(self, _context, layout, _data, grid, _icon, _active_data, _active_propname, _index):
|
||||
name = grid.name
|
||||
data_type = grid.bl_rna.properties['data_type'].enum_items[grid.data_type]
|
||||
data_type = grid.bl_rna.properties["data_type"].enum_items[grid.data_type]
|
||||
|
||||
layout.emboss = 'NONE'
|
||||
layout.label(text=name)
|
||||
|
|
|
@ -324,7 +324,7 @@ class RENDER_PT_output_color_management(RenderOutputButtonsPanel, Panel):
|
|||
col = flow.column()
|
||||
|
||||
if image_settings.has_linear_colorspace:
|
||||
if hasattr(owner, 'linear_colorspace_settings'):
|
||||
if hasattr(owner, "linear_colorspace_settings"):
|
||||
col.prop(owner.linear_colorspace_settings, "name", text="Color Space")
|
||||
else:
|
||||
col.prop(owner.display_settings, "display_device")
|
||||
|
|
|
@ -31,10 +31,10 @@ class CONSOLE_MT_view(Menu):
|
|||
layout = self.layout
|
||||
|
||||
props = layout.operator("wm.context_cycle_int", text="Zoom In")
|
||||
props.data_path = 'space_data.font_size'
|
||||
props.data_path = "space_data.font_size"
|
||||
props.reverse = False
|
||||
props = layout.operator("wm.context_cycle_int", text="Zoom Out")
|
||||
props.data_path = 'space_data.font_size'
|
||||
props.data_path = "space_data.font_size"
|
||||
props.reverse = True
|
||||
|
||||
layout.separator()
|
||||
|
@ -62,7 +62,7 @@ class CONSOLE_MT_language(Menu):
|
|||
layout = self.layout
|
||||
layout.column()
|
||||
|
||||
# Collect modules with 'console_*.execute'
|
||||
# Collect modules with `console_*.execute`.
|
||||
languages = []
|
||||
for modname, mod in sys.modules.items():
|
||||
if modname.startswith("console_") and hasattr(mod, "execute"):
|
||||
|
|
|
@ -550,7 +550,7 @@ class DOPESHEET_PT_custom_props_action(PropertyPanel, Panel):
|
|||
bl_space_type = 'DOPESHEET_EDITOR'
|
||||
bl_category = "Action"
|
||||
bl_region_type = 'UI'
|
||||
bl_context = 'data'
|
||||
bl_context = "data"
|
||||
_context_path = "active_action"
|
||||
_property_type = bpy.types.Action
|
||||
|
||||
|
|
|
@ -332,6 +332,7 @@ class GRAPH_MT_slider(Menu):
|
|||
layout.operator("graph.breakdown", text="Breakdown")
|
||||
layout.operator("graph.blend_to_neighbor", text="Blend to Neighbor")
|
||||
layout.operator("graph.blend_to_default", text="Blend to Default Value")
|
||||
layout.operator("graph.ease", text="Ease")
|
||||
|
||||
|
||||
class GRAPH_MT_view_pie(Menu):
|
||||
|
|
|
@ -742,7 +742,7 @@ class IMAGE_HT_header(Header):
|
|||
|
||||
# Snap.
|
||||
snap_uv_element = tool_settings.snap_uv_element
|
||||
act_snap_uv_element = tool_settings.bl_rna.properties['snap_uv_element'].enum_items[snap_uv_element]
|
||||
act_snap_uv_element = tool_settings.bl_rna.properties["snap_uv_element"].enum_items[snap_uv_element]
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(tool_settings, "use_snap_uv", text="")
|
||||
|
|
|
@ -41,7 +41,7 @@ class NODE_HT_header(Header):
|
|||
|
||||
layout.template_header()
|
||||
|
||||
# Now expanded via the 'ui_type'
|
||||
# Now expanded via the `ui_type`.
|
||||
# layout.prop(snode, "tree_type", text="")
|
||||
|
||||
if snode.tree_type == 'ShaderNodeTree':
|
||||
|
@ -661,7 +661,7 @@ class NODE_PT_active_node_properties(Panel):
|
|||
)
|
||||
|
||||
def show_socket_input(self, socket):
|
||||
return hasattr(socket, 'draw') and socket.enabled and not socket.is_linked
|
||||
return hasattr(socket, "draw") and socket.enabled and not socket.is_linked
|
||||
|
||||
|
||||
class NODE_PT_texture_mapping(Panel):
|
||||
|
@ -961,7 +961,7 @@ def node_panel(cls):
|
|||
node_cls.bl_space_type = 'NODE_EDITOR'
|
||||
node_cls.bl_region_type = 'UI'
|
||||
node_cls.bl_category = "Options"
|
||||
if hasattr(node_cls, 'bl_parent_id'):
|
||||
if hasattr(node_cls, "bl_parent_id"):
|
||||
node_cls.bl_parent_id = 'NODE_' + node_cls.bl_parent_id
|
||||
|
||||
return node_cls
|
||||
|
|
|
@ -51,8 +51,8 @@ ToolDef = namedtuple(
|
|||
"idname",
|
||||
# The name to display in the interface.
|
||||
"label",
|
||||
# Description (for tool-tip), when not set, use the description of 'operator',
|
||||
# may be a string or a 'function(context, item, key-map) -> string'.
|
||||
# Description (for tool-tip), when not set, use the description of `operator`,
|
||||
# may be a string or a `function(context, item, key-map) -> string`.
|
||||
"description",
|
||||
# The name of the icon to use (found in `release/datafiles/icons`) or None for no icon.
|
||||
"icon",
|
||||
|
@ -88,7 +88,7 @@ ToolDef = namedtuple(
|
|||
# Note that this isn't used for Blender's built in tools which use the built-in key-map.
|
||||
# Keep this functionality since it's likely useful for add-on key-maps.
|
||||
#
|
||||
# Warning: currently 'from_dict' this is a list of one item,
|
||||
# Warning: currently `from_dict` this is a list of one item,
|
||||
# so internally we can swap the key-map function for the key-map itself.
|
||||
# This isn't very nice and may change, tool definitions shouldn't care about this.
|
||||
"keymap",
|
||||
|
@ -256,7 +256,7 @@ class ToolSelectPanelHelper:
|
|||
|
||||
# tool flattening
|
||||
#
|
||||
# usually 'tools' is already expanded into ToolDef
|
||||
# usually 'tools' is already expanded into `ToolDef`
|
||||
# but when registering a tool, this can still be a function
|
||||
# (_tools_flatten is usually called with cls.tools_from_context(context)
|
||||
# [that already yields from the function])
|
||||
|
@ -792,7 +792,7 @@ class ToolSelectPanelHelper:
|
|||
item, tool, icon_value = cls._tool_get_active(context, space_type, mode, with_icon=True)
|
||||
if item is None:
|
||||
return None
|
||||
# Note: we could show 'item.text' here but it makes the layout jitter when switching tools.
|
||||
# NOTE: we could show `item.text` here but it makes the layout jitter when switching tools.
|
||||
# Add some spacing since the icon is currently assuming regular small icon size.
|
||||
if show_tool_icon_always:
|
||||
layout.label(text=" " + iface_(item.label, "Operator"), icon_value=icon_value)
|
||||
|
|
|
@ -2706,7 +2706,7 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
)
|
||||
|
||||
# Private tools dictionary, store data to implement `tools_all` & `tools_from_context`.
|
||||
# The keys match image spaces modes: 'context.space_data.mode'.
|
||||
# The keys match image spaces modes: `context.space_data.mode`.
|
||||
# The values represent the tools, see `ToolSelectPanelHelper` for details.
|
||||
_tools = {
|
||||
None: [
|
||||
|
@ -2892,7 +2892,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
)
|
||||
|
||||
# Private tools dictionary, store data to implement `tools_all` & `tools_from_context`.
|
||||
# The keys match object-modes from: 'context.mode'.
|
||||
# The keys match object-modes from: `context.mode`.
|
||||
# The values represent the tools, see `ToolSelectPanelHelper` for details.
|
||||
_tools = {
|
||||
None: [
|
||||
|
@ -3230,7 +3230,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
)
|
||||
|
||||
# Private tools dictionary, store data to implement `tools_all` & `tools_from_context`.
|
||||
# The keys match sequence editors view type: 'context.space_data.view_type'.
|
||||
# The keys match sequence editors view type: `context.space_data.view_type`.
|
||||
# The values represent the tools, see `ToolSelectPanelHelper` for details.
|
||||
_tools = {
|
||||
None: [
|
||||
|
|
|
@ -1286,7 +1286,7 @@ class ThemeGenericClassGenerator:
|
|||
def generate_panel_classes_from_theme_areas():
|
||||
from bpy.types import Theme
|
||||
|
||||
for theme_area in Theme.bl_rna.properties['theme_area'].enum_items_static:
|
||||
for theme_area in Theme.bl_rna.properties["theme_area"].enum_items_static:
|
||||
if theme_area.identifier in {'USER_INTERFACE', 'STYLE', 'BONE_COLOR_SETS'}:
|
||||
continue
|
||||
|
||||
|
@ -2324,7 +2324,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
|
|||
({"property": "use_sculpt_tools_tilt"}, "T82877"),
|
||||
({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")),
|
||||
({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
|
||||
({"property": "use_realtime_compositor"}, "T99210"),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class VIEW3D_HT_tool_header(Header):
|
|||
layout.popover("VIEW3D_PT_tools_brush_falloff")
|
||||
layout.popover("VIEW3D_PT_tools_brush_display")
|
||||
|
||||
# Note: general mode options should be added to 'draw_mode_settings'.
|
||||
# NOTE: general mode options should be added to `draw_mode_settings`.
|
||||
if tool_mode == 'SCULPT':
|
||||
if is_valid_context:
|
||||
draw_3d_brush_settings(layout, tool_mode)
|
||||
|
@ -889,7 +889,7 @@ class VIEW3D_HT_header(Header):
|
|||
row = layout.row()
|
||||
row.active = (object_mode == 'EDIT') or (shading.type in {'WIREFRAME', 'SOLID'})
|
||||
|
||||
# While exposing 'shading.show_xray(_wireframe)' is correct.
|
||||
# While exposing `shading.show_xray(_wireframe)` is correct.
|
||||
# this hides the key shortcut from users: T70433.
|
||||
if has_pose_mode:
|
||||
draw_depressed = overlay.show_xray_bone
|
||||
|
@ -2290,9 +2290,9 @@ class VIEW3D_MT_add(Menu):
|
|||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# note, don't use 'EXEC_SCREEN' or operators won't get the 'v3d' context.
|
||||
# NOTE: don't use 'EXEC_SCREEN' or operators won't get the `v3d` context.
|
||||
|
||||
# Note: was EXEC_AREA, but this context does not have the 'rv3d', which prevents
|
||||
# NOTE: was `EXEC_AREA`, but this context does not have the `rv3d`, which prevents
|
||||
# "align_view" to work on first call (see T32719).
|
||||
layout.operator_context = 'EXEC_REGION_WIN'
|
||||
|
||||
|
@ -3291,23 +3291,23 @@ class VIEW3D_MT_mask(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Smooth Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Smooth Mask")
|
||||
props.filter_type = 'SMOOTH'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Sharpen Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Sharpen Mask")
|
||||
props.filter_type = 'SHARPEN'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Grow Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Grow Mask")
|
||||
props.filter_type = 'GROW'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Shrink Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Shrink Mask")
|
||||
props.filter_type = 'SHRINK'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Increase Contrast')
|
||||
props = layout.operator("sculpt.mask_filter", text="Increase Contrast")
|
||||
props.filter_type = 'CONTRAST_INCREASE'
|
||||
props.auto_iteration_count = False
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Decrease Contrast')
|
||||
props = layout.operator("sculpt.mask_filter", text="Decrease Contrast")
|
||||
props.filter_type = 'CONTRAST_DECREASE'
|
||||
props.auto_iteration_count = False
|
||||
|
||||
|
@ -3352,13 +3352,13 @@ class VIEW3D_MT_face_sets(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
op = layout.operator("sculpt.face_sets_create", text='Face Set from Masked')
|
||||
op = layout.operator("sculpt.face_sets_create", text="Face Set from Masked")
|
||||
op.mode = 'MASKED'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_create", text='Face Set from Visible')
|
||||
op = layout.operator("sculpt.face_sets_create", text="Face Set from Visible")
|
||||
op.mode = 'VISIBLE'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_create", text='Face Set from Edit Mode Selection')
|
||||
op = layout.operator("sculpt.face_sets_create", text="Face Set from Edit Mode Selection")
|
||||
op.mode = 'SELECTION'
|
||||
|
||||
layout.separator()
|
||||
|
@ -3367,10 +3367,10 @@ class VIEW3D_MT_face_sets(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("sculpt.face_set_edit", text='Grow Face Set')
|
||||
op = layout.operator("sculpt.face_set_edit", text="Grow Face Set")
|
||||
op.mode = 'GROW'
|
||||
|
||||
op = layout.operator("sculpt.face_set_edit", text='Shrink Face Set')
|
||||
op = layout.operator("sculpt.face_set_edit", text="Shrink Face Set")
|
||||
op.mode = 'SHRINK'
|
||||
|
||||
layout.separator()
|
||||
|
@ -3389,18 +3389,18 @@ class VIEW3D_MT_face_sets(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("mesh.face_set_extract", text='Extract Face Set')
|
||||
op = layout.operator("mesh.face_set_extract", text="Extract Face Set")
|
||||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("sculpt.face_set_change_visibility", text='Invert Visible Face Sets')
|
||||
op = layout.operator("sculpt.face_set_change_visibility", text="Invert Visible Face Sets")
|
||||
op.mode = 'INVERT'
|
||||
|
||||
op = layout.operator("sculpt.reveal_all", text='Show All Face Sets')
|
||||
op = layout.operator("sculpt.reveal_all", text="Show All Face Sets")
|
||||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("sculpt.face_sets_randomize_colors", text='Randomize Colors')
|
||||
op = layout.operator("sculpt.face_sets_randomize_colors", text="Randomize Colors")
|
||||
|
||||
|
||||
class VIEW3D_MT_sculpt_set_pivot(Menu):
|
||||
|
@ -3431,31 +3431,31 @@ class VIEW3D_MT_face_sets_init(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Loose Parts')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Loose Parts")
|
||||
op.mode = 'LOOSE_PARTS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Face Set Boundaries')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Face Set Boundaries")
|
||||
op.mode = 'FACE_SET_BOUNDARIES'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Materials')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Materials")
|
||||
op.mode = 'MATERIALS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Normals')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Normals")
|
||||
op.mode = 'NORMALS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By UV Seams')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By UV Seams")
|
||||
op.mode = 'UV_SEAMS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Edge Creases')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Edge Creases")
|
||||
op.mode = 'CREASES'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Edge Bevel Weight')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Edge Bevel Weight")
|
||||
op.mode = 'BEVEL_WEIGHT'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Sharp Edges')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Sharp Edges")
|
||||
op.mode = 'SHARP_EDGES'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Face Maps')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Face Maps")
|
||||
op.mode = 'FACE_MAPS'
|
||||
|
||||
|
||||
|
@ -3465,13 +3465,13 @@ class VIEW3D_MT_random_mask(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
op = layout.operator("sculpt.mask_init", text='Per Vertex')
|
||||
op = layout.operator("sculpt.mask_init", text="Per Vertex")
|
||||
op.mode = 'RANDOM_PER_VERTEX'
|
||||
|
||||
op = layout.operator("sculpt.mask_init", text='Per Face Set')
|
||||
op = layout.operator("sculpt.mask_init", text="Per Face Set")
|
||||
op.mode = 'RANDOM_PER_FACE_SET'
|
||||
|
||||
op = layout.operator("sculpt.mask_init", text='Per Loose Part')
|
||||
op = layout.operator("sculpt.mask_init", text="Per Loose Part")
|
||||
op.mode = 'RANDOM_PER_LOOSE_PART'
|
||||
|
||||
|
||||
|
@ -3660,10 +3660,6 @@ class VIEW3D_MT_pose_propagate(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("pose.propagate").mode = 'WHILE_HELD'
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("pose.propagate", text="To Next Keyframe").mode = 'NEXT_KEY'
|
||||
layout.operator("pose.propagate", text="To Last Keyframe (Make Cyclic)").mode = 'LAST_KEY'
|
||||
|
||||
|
@ -5500,23 +5496,23 @@ class VIEW3D_MT_sculpt_mask_edit_pie(Menu):
|
|||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
op = pie.operator("paint.mask_flood_fill", text='Invert Mask')
|
||||
op = pie.operator("paint.mask_flood_fill", text="Invert Mask")
|
||||
op.mode = 'INVERT'
|
||||
op = pie.operator("paint.mask_flood_fill", text='Clear Mask')
|
||||
op = pie.operator("paint.mask_flood_fill", text="Clear Mask")
|
||||
op.mode = 'VALUE'
|
||||
op.value = 0.0
|
||||
op = pie.operator("sculpt.mask_filter", text='Smooth Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Smooth Mask")
|
||||
op.filter_type = 'SMOOTH'
|
||||
op = pie.operator("sculpt.mask_filter", text='Sharpen Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Sharpen Mask")
|
||||
op.filter_type = 'SHARPEN'
|
||||
op = pie.operator("sculpt.mask_filter", text='Grow Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Grow Mask")
|
||||
op.filter_type = 'GROW'
|
||||
op = pie.operator("sculpt.mask_filter", text='Shrink Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Shrink Mask")
|
||||
op.filter_type = 'SHRINK'
|
||||
op = pie.operator("sculpt.mask_filter", text='Increase Contrast')
|
||||
op = pie.operator("sculpt.mask_filter", text="Increase Contrast")
|
||||
op.filter_type = 'CONTRAST_INCREASE'
|
||||
op.auto_iteration_count = False
|
||||
op = pie.operator("sculpt.mask_filter", text='Decrease Contrast')
|
||||
op = pie.operator("sculpt.mask_filter", text="Decrease Contrast")
|
||||
op.filter_type = 'CONTRAST_DECREASE'
|
||||
op.auto_iteration_count = False
|
||||
|
||||
|
@ -5565,16 +5561,16 @@ class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu):
|
|||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
op = pie.operator("sculpt.face_sets_create", text='Face Set from Masked')
|
||||
op = pie.operator("sculpt.face_sets_create", text="Face Set from Masked")
|
||||
op.mode = 'MASKED'
|
||||
|
||||
op = pie.operator("sculpt.face_sets_create", text='Face Set from Visible')
|
||||
op = pie.operator("sculpt.face_sets_create", text="Face Set from Visible")
|
||||
op.mode = 'VISIBLE'
|
||||
|
||||
op = pie.operator("sculpt.face_set_change_visibility", text='Invert Visible')
|
||||
op = pie.operator("sculpt.face_set_change_visibility", text="Invert Visible")
|
||||
op.mode = 'INVERT'
|
||||
|
||||
op = pie.operator("sculpt.reveal_all", text='Show All')
|
||||
op = pie.operator("sculpt.reveal_all", text="Show All")
|
||||
|
||||
|
||||
class VIEW3D_MT_wpaint_vgroup_lock_pie(Menu):
|
||||
|
@ -6216,8 +6212,7 @@ class VIEW3D_PT_shading_compositor(Panel):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.space_data.shading.type in {'MATERIAL', 'RENDERED'} and
|
||||
context.preferences.experimental.use_realtime_compositor)
|
||||
return context.space_data.shading.type in {'MATERIAL', 'RENDERED'}
|
||||
|
||||
def draw(self, context):
|
||||
shading = context.space_data.shading
|
||||
|
@ -6974,9 +6969,9 @@ class VIEW3D_PT_snapping(Panel):
|
|||
col.prop(tool_settings, "use_snap_project")
|
||||
|
||||
if 'FACE_NEAREST' in snap_elements:
|
||||
col.prop(tool_settings, 'use_snap_to_same_target')
|
||||
col.prop(tool_settings, "use_snap_to_same_target")
|
||||
if object_mode == 'EDIT':
|
||||
col.prop(tool_settings, 'snap_face_nearest_steps')
|
||||
col.prop(tool_settings, "snap_face_nearest_steps")
|
||||
|
||||
if 'VOLUME' in snap_elements:
|
||||
col.prop(tool_settings, "use_snap_peel_object")
|
||||
|
|
|
@ -1084,7 +1084,7 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel):
|
|||
wpaint = tool_settings.weight_paint
|
||||
mesh = context.object.data
|
||||
|
||||
layout.prop(mesh, 'use_mirror_vertex_groups')
|
||||
layout.prop(mesh, "use_mirror_vertex_groups")
|
||||
|
||||
draw_vpaint_symmetry(layout, wpaint, context.object)
|
||||
|
||||
|
|
|
@ -597,7 +597,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
|
|||
path = keyingsets_utils.path_add_property(base_path, "delta_rotation_quaternion")
|
||||
elif data.rotation_mode == 'AXIS_ANGLE':
|
||||
# XXX: for now, this is not available yet
|
||||
#path = path_add_property(base_path, "delta_rotation_axis_angle")
|
||||
# path = path_add_property(base_path, "delta_rotation_axis_angle")
|
||||
return
|
||||
else:
|
||||
path = keyingsets_utils.path_add_property(base_path, "delta_rotation_euler")
|
||||
|
@ -637,7 +637,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
|
|||
###############################
|
||||
|
||||
|
||||
# Note that this controls order of options in 'insert keyframe' menu.
|
||||
# Note that this controls order of options in `insert keyframe` menu.
|
||||
# Better try to keep some logical order here beyond mere alphabetical one, also because of menu entries shortcut.
|
||||
# See also T51867.
|
||||
classes = (
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* An #AnonymousAttributeID is used to identify attributes that are not explicitly named.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct AnonymousAttributeID AnonymousAttributeID;
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name);
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name);
|
||||
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id);
|
||||
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id);
|
||||
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,155 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Wrapper for #AnonymousAttributeID with RAII semantics.
|
||||
* This class should typically not be used directly. Instead use #StrongAnonymousAttributeID or
|
||||
* #WeakAnonymousAttributeID.
|
||||
*/
|
||||
template<bool IsStrongReference> class OwnedAnonymousAttributeID {
|
||||
private:
|
||||
const AnonymousAttributeID *data_ = nullptr;
|
||||
|
||||
template<bool OtherIsStrongReference> friend class OwnedAnonymousAttributeID;
|
||||
|
||||
public:
|
||||
OwnedAnonymousAttributeID() = default;
|
||||
|
||||
/** Create a new anonymous attribute id. */
|
||||
explicit OwnedAnonymousAttributeID(StringRefNull debug_name)
|
||||
{
|
||||
if constexpr (IsStrongReference) {
|
||||
data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str());
|
||||
}
|
||||
else {
|
||||
data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This transfers ownership, so no incref is necessary.
|
||||
* The caller has to make sure that it owned the anonymous id.
|
||||
*/
|
||||
explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id)
|
||||
: data_(anonymous_id)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
this->incref();
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
this->incref();
|
||||
other.decref();
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
|
||||
~OwnedAnonymousAttributeID()
|
||||
{
|
||||
this->decref();
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~OwnedAnonymousAttributeID();
|
||||
new (this) OwnedAnonymousAttributeID(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~OwnedAnonymousAttributeID();
|
||||
new (this) OwnedAnonymousAttributeID(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
StringRefNull debug_name() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return BKE_anonymous_attribute_id_debug_name(data_);
|
||||
}
|
||||
|
||||
bool has_strong_references() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return BKE_anonymous_attribute_id_has_strong_references(data_);
|
||||
}
|
||||
|
||||
/** Extract the ownership of the currently wrapped anonymous id. */
|
||||
const AnonymousAttributeID *extract()
|
||||
{
|
||||
const AnonymousAttributeID *extracted_data = data_;
|
||||
/* Don't decref because the caller becomes the new owner. */
|
||||
data_ = nullptr;
|
||||
return extracted_data;
|
||||
}
|
||||
|
||||
/** Get the wrapped anonymous id, without taking ownership. */
|
||||
const AnonymousAttributeID *get() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
void incref()
|
||||
{
|
||||
if (data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if constexpr (IsStrongReference) {
|
||||
BKE_anonymous_attribute_id_increment_strong(data_);
|
||||
}
|
||||
else {
|
||||
BKE_anonymous_attribute_id_increment_weak(data_);
|
||||
}
|
||||
}
|
||||
|
||||
void decref()
|
||||
{
|
||||
if (data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if constexpr (IsStrongReference) {
|
||||
BKE_anonymous_attribute_id_decrement_strong(data_);
|
||||
}
|
||||
else {
|
||||
BKE_anonymous_attribute_id_decrement_weak(data_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using StrongAnonymousAttributeID = OwnedAnonymousAttributeID<true>;
|
||||
using WeakAnonymousAttributeID = OwnedAnonymousAttributeID<false>;
|
||||
|
||||
} // namespace blender::bke
|
|
@ -0,0 +1,103 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_user_counter.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* An #AnonymousAttributeID contains information about a specific anonymous attribute.
|
||||
* Like normal attributes, anonymous attributes are also identified by their name, so one should
|
||||
* not have to compare #AnonymousAttributeID pointers.
|
||||
*
|
||||
* Anonymous attributes don't need additional information besides their name, with a few
|
||||
* exceptions:
|
||||
* - The name of anonymous attributes is generated automatically, so it is generally not human
|
||||
* readable (just random characters). #AnonymousAttributeID can provide more context as where a
|
||||
* specific anonymous attribute was created which can simplify debugging.
|
||||
* - [Not yet supported.] When anonymous attributes are contained in on-disk caches, we have to map
|
||||
* those back to anonymous attributes at run-time. The issue is that (for various reasons) we
|
||||
* might change how anonymous attribute names are generated in the future, which would lead to a
|
||||
* mis-match between stored and new attribute names. To work around it, we should cache
|
||||
* additional information for anonymous attributes on disk (like which node created it). This
|
||||
* information can then be used to map stored attributes to their run-time counterpart.
|
||||
*
|
||||
* Once created, #AnonymousAttributeID is immutable. Also it is intrinsically reference counted so
|
||||
* that it can have shared ownership. `std::shared_ptr` can't be used for that purpose here,
|
||||
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
|
||||
* should be used to avoid manual reference counting in C++ code.
|
||||
*/
|
||||
class AnonymousAttributeID {
|
||||
private:
|
||||
mutable std::atomic<int> users_ = 1;
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
|
||||
public:
|
||||
virtual ~AnonymousAttributeID() = default;
|
||||
|
||||
StringRefNull name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
void user_add() const
|
||||
{
|
||||
users_.fetch_add(1);
|
||||
}
|
||||
|
||||
void user_remove() const
|
||||
{
|
||||
const int new_users = users_.fetch_sub(1) - 1;
|
||||
if (new_users == 0) {
|
||||
MEM_delete(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
|
||||
using AutoAnonymousAttributeID = UserCounter<const AnonymousAttributeID>;
|
||||
|
||||
/**
|
||||
* A set of anonymous attribute names that is passed around in geometry nodes.
|
||||
*/
|
||||
class AnonymousAttributeSet {
|
||||
public:
|
||||
/**
|
||||
* This uses `std::shared_ptr` because attributes sets are passed around by value during geometry
|
||||
* nodes evaluation, and this makes it very small if there is no name. Also it makes copying very
|
||||
* cheap.
|
||||
*/
|
||||
std::shared_ptr<Set<std::string>> names;
|
||||
};
|
||||
|
||||
/**
|
||||
* Can be passed to algorithms which propagate attributes. It can tell the algorithm which
|
||||
* anonymous attributes should be propagated and can be skipped.
|
||||
*/
|
||||
class AnonymousAttributePropagationInfo {
|
||||
public:
|
||||
/**
|
||||
* This uses `std::shared_ptr` because it's usually initialized from an #AnonymousAttributeSet
|
||||
* and then the set doesn't have to be copied.
|
||||
*/
|
||||
std::shared_ptr<Set<std::string>> names;
|
||||
|
||||
/**
|
||||
* Propagate all anonymous attributes even if the set above is empty.
|
||||
*/
|
||||
bool propagate_all = true;
|
||||
|
||||
/**
|
||||
* Return true when the anonymous attribute should be propagated and false otherwise.
|
||||
*/
|
||||
bool propagate(const AnonymousAttributeID &anonymous_id) const;
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
|
@ -11,7 +11,7 @@
|
|||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
struct Mesh;
|
||||
|
@ -24,7 +24,7 @@ class GField;
|
|||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Identifies an attribute that is either named or anonymous.
|
||||
* Identifies an attribute with optional anonymous attribute information.
|
||||
* It does not own the identifier, so it is just a reference.
|
||||
*/
|
||||
class AttributeIDRef {
|
||||
|
@ -38,15 +38,14 @@ class AttributeIDRef {
|
|||
AttributeIDRef(StringRefNull name);
|
||||
AttributeIDRef(const char *name);
|
||||
AttributeIDRef(const std::string &name);
|
||||
AttributeIDRef(const AnonymousAttributeID &anonymous_id);
|
||||
AttributeIDRef(const AnonymousAttributeID *anonymous_id);
|
||||
|
||||
operator bool() const;
|
||||
uint64_t hash() const;
|
||||
bool is_named() const;
|
||||
bool is_anonymous() const;
|
||||
StringRef name() const;
|
||||
const AnonymousAttributeID &anonymous_id() const;
|
||||
bool should_be_kept() const;
|
||||
|
||||
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b);
|
||||
friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
|
||||
|
@ -749,6 +748,7 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
|
|||
const bke::AttributeAccessor src_attributes,
|
||||
bke::MutableAttributeAccessor dst_attributes,
|
||||
eAttrDomainMask domain_mask,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip = {});
|
||||
|
||||
/**
|
||||
|
@ -762,6 +762,7 @@ void copy_attribute_domain(AttributeAccessor src_attributes,
|
|||
MutableAttributeAccessor dst_attributes,
|
||||
IndexMask selection,
|
||||
eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip = {});
|
||||
|
||||
bool allow_procedural_attribute_access(StringRef attribute_name);
|
||||
|
@ -852,29 +853,31 @@ inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name)
|
|||
}
|
||||
|
||||
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
|
||||
: anonymous_id_(anonymous_id)
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID &anonymous_id)
|
||||
: AttributeIDRef(anonymous_id.name())
|
||||
{
|
||||
anonymous_id_ = &anonymous_id;
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
|
||||
: AttributeIDRef(anonymous_id ? anonymous_id->name() : "")
|
||||
{
|
||||
anonymous_id_ = anonymous_id;
|
||||
}
|
||||
|
||||
inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
|
||||
{
|
||||
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
|
||||
return a.name_ == b.name_;
|
||||
}
|
||||
|
||||
inline AttributeIDRef::operator bool() const
|
||||
{
|
||||
return this->is_named() || this->is_anonymous();
|
||||
return !name_.is_empty();
|
||||
}
|
||||
|
||||
inline uint64_t AttributeIDRef::hash() const
|
||||
{
|
||||
return get_default_hash_2(name_, anonymous_id_);
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_named() const
|
||||
{
|
||||
return !name_.is_empty();
|
||||
return get_default_hash(name_);
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_anonymous() const
|
||||
|
@ -884,7 +887,6 @@ inline bool AttributeIDRef::is_anonymous() const
|
|||
|
||||
inline StringRef AttributeIDRef::name() const
|
||||
{
|
||||
BLI_assert(this->is_named());
|
||||
return name_;
|
||||
}
|
||||
|
||||
|
@ -894,14 +896,4 @@ inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const
|
|||
return *anonymous_id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True if the attribute should not be removed automatically as an optimization during
|
||||
* processing or copying. Anonymous attributes can be removed when they no longer have any
|
||||
* references.
|
||||
*/
|
||||
inline bool AttributeIDRef::should_be_kept() const
|
||||
{
|
||||
return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
struct CurvesGeometry;
|
||||
struct Mesh;
|
||||
|
||||
/** \file
|
||||
|
@ -11,6 +10,9 @@ struct Mesh;
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
struct CurvesGeometry;
|
||||
class AnonymousAttributePropagationInfo;
|
||||
|
||||
/**
|
||||
* Extrude all splines in the profile curve along the path of every spline in the curve input.
|
||||
* Transfer curve attributes to the mesh.
|
||||
|
@ -23,11 +25,13 @@ namespace blender::bke {
|
|||
*/
|
||||
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
const CurvesGeometry &profile,
|
||||
bool fill_caps);
|
||||
bool fill_caps,
|
||||
const AnonymousAttributePropagationInfo &propagation_info);
|
||||
/**
|
||||
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
|
||||
* Transfer curve attributes to the mesh.
|
||||
*/
|
||||
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve);
|
||||
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
|
||||
const AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -404,8 +404,10 @@ class CurvesGeometry : public ::CurvesGeometry {
|
|||
|
||||
void calculate_bezier_auto_handles();
|
||||
|
||||
void remove_points(IndexMask points_to_delete);
|
||||
void remove_curves(IndexMask curves_to_delete);
|
||||
void remove_points(IndexMask points_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info = {});
|
||||
void remove_curves(IndexMask curves_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info = {});
|
||||
|
||||
/**
|
||||
* Change the direction of selected curves (switch the start and end) without changing their
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct AnonymousAttributeID;
|
||||
struct BMesh;
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
|
@ -227,7 +226,7 @@ void *CustomData_add_layer_anonymous(struct CustomData *data,
|
|||
eCDAllocType alloctype,
|
||||
void *layer,
|
||||
int totelem,
|
||||
const struct AnonymousAttributeID *anonymous_id);
|
||||
const AnonymousAttributeIDHandle *anonymous_id);
|
||||
|
||||
/**
|
||||
* Frees the active or first data layer with the give type.
|
||||
|
@ -275,8 +274,6 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data,
|
|||
int type,
|
||||
const char *name,
|
||||
int totelem);
|
||||
void *CustomData_duplicate_referenced_layer_anonymous(
|
||||
CustomData *data, int type, const struct AnonymousAttributeID *anonymous_id, int totelem);
|
||||
bool CustomData_is_referenced_layer(struct CustomData *data, int type);
|
||||
|
||||
/**
|
||||
|
|
|
@ -261,18 +261,14 @@ class NormalFieldInput : public GeometryFieldInput {
|
|||
|
||||
class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
||||
private:
|
||||
/**
|
||||
* A strong reference is required to make sure that the referenced attribute is not removed
|
||||
* automatically.
|
||||
*/
|
||||
StrongAnonymousAttributeID anonymous_id_;
|
||||
AutoAnonymousAttributeID anonymous_id_;
|
||||
std::string producer_name_;
|
||||
|
||||
public:
|
||||
AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id,
|
||||
AnonymousAttributeFieldInput(AutoAnonymousAttributeID anonymous_id,
|
||||
const CPPType &type,
|
||||
std::string producer_name)
|
||||
: GeometryFieldInput(type, anonymous_id.debug_name()),
|
||||
: GeometryFieldInput(type, anonymous_id->name()),
|
||||
anonymous_id_(std::move(anonymous_id)),
|
||||
producer_name_(producer_name)
|
||||
{
|
||||
|
@ -280,7 +276,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name)
|
||||
static fn::Field<T> Create(AutoAnonymousAttributeID anonymous_id, std::string producer_name)
|
||||
{
|
||||
const CPPType &type = CPPType::get<T>();
|
||||
auto field_input = std::make_shared<AnonymousAttributeFieldInput>(
|
||||
|
@ -288,6 +284,11 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
|||
return fn::Field<T>{field_input};
|
||||
}
|
||||
|
||||
const AutoAnonymousAttributeID &anonymous_id() const
|
||||
{
|
||||
return anonymous_id_;
|
||||
}
|
||||
|
||||
GVArray get_varray_for_context(const GeometryFieldContext &context,
|
||||
IndexMask mask) const override;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "BLI_user_counter.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_geometry_set.h"
|
||||
|
||||
|
@ -213,6 +213,7 @@ struct GeometrySet {
|
|||
blender::Span<GeometryComponentType> component_types,
|
||||
GeometryComponentType dst_component_type,
|
||||
bool include_instances,
|
||||
const blender::bke::AnonymousAttributePropagationInfo &propagation_info,
|
||||
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const;
|
||||
|
||||
blender::Vector<GeometryComponentType> gather_component_types(bool include_instances,
|
||||
|
|
|
@ -155,7 +155,8 @@ class Instances {
|
|||
* Remove the indices that are not contained in the mask input, and remove unused instance
|
||||
* references afterwards.
|
||||
*/
|
||||
void remove(const blender::IndexMask mask);
|
||||
void remove(const blender::IndexMask mask,
|
||||
const blender::bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
/**
|
||||
* Get an id for every instance. These can be used for e.g. motion blur.
|
||||
*/
|
||||
|
|
|
@ -410,8 +410,6 @@ bool BKE_lib_override_library_status_check_reference(struct Main *bmain, struct
|
|||
* \note This is by far the biggest operation (the more time-consuming) of the three so far,
|
||||
* since it has to go over all properties in depth (all overridable ones at least).
|
||||
* Generating differential values and applying overrides are much cheaper.
|
||||
*
|
||||
* \return true if any library operation was created.
|
||||
*/
|
||||
void BKE_lib_override_library_operations_create(struct Main *bmain,
|
||||
struct ID *local,
|
||||
|
@ -425,6 +423,29 @@ void BKE_lib_override_library_main_operations_create(struct Main *bmain,
|
|||
bool force_auto,
|
||||
int *r_report_flags);
|
||||
|
||||
/**
|
||||
* Restore forbidden modified override properties to the values of their matching properties in the
|
||||
* linked reference ID.
|
||||
*
|
||||
* \param r_report_flags #eRNAOverrideMatchResult flags giving info about the result of this call.
|
||||
*
|
||||
* \note Typically used as part of BKE_lib_override_library_main_operations_create process, since
|
||||
* modifying RNA properties from non-main threads is not safe.
|
||||
*/
|
||||
void BKE_lib_override_library_operations_restore(struct Main *bmain,
|
||||
struct ID *local,
|
||||
int *r_report_flags);
|
||||
/**
|
||||
* Restore forbidden modified override properties to the values of their matching properties in the
|
||||
* linked reference ID, for all liboverride IDs tagged as needing such process in given `bmain`.
|
||||
*
|
||||
* \param r_report_flags #eRNAOverrideMatchResult flags giving info about the result of this call.
|
||||
*
|
||||
* \note Typically used as part of BKE_lib_override_library_main_operations_create process, since
|
||||
* modifying RNA properties from non-main threads is not safe.
|
||||
*/
|
||||
void BKE_lib_override_library_main_operations_restore(struct Main *bmain, int *r_report_flags);
|
||||
|
||||
/**
|
||||
* Reset all overrides in given \a id_root, while preserving ID relations.
|
||||
*
|
||||
|
|
|
@ -39,40 +39,42 @@ void BKE_movieclip_clear_proxy_cache(struct MovieClip *clip);
|
|||
*/
|
||||
void BKE_movieclip_convert_multilayer_ibuf(struct ImBuf *ibuf);
|
||||
|
||||
struct ImBuf *BKE_movieclip_get_ibuf(struct MovieClip *clip, struct MovieClipUser *user);
|
||||
struct ImBuf *BKE_movieclip_get_ibuf(struct MovieClip *clip, const struct MovieClipUser *user);
|
||||
struct ImBuf *BKE_movieclip_get_postprocessed_ibuf(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int postprocess_flag);
|
||||
struct ImBuf *BKE_movieclip_get_stable_ibuf(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
float loc[2],
|
||||
float *scale,
|
||||
float *angle,
|
||||
int postprocess_flag);
|
||||
struct ImBuf *BKE_movieclip_get_ibuf_flag(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int flag,
|
||||
int cache_flag);
|
||||
void BKE_movieclip_get_size(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int *width,
|
||||
int *height);
|
||||
void BKE_movieclip_get_size_fl(struct MovieClip *clip, struct MovieClipUser *user, float size[2]);
|
||||
void BKE_movieclip_get_size_fl(struct MovieClip *clip,
|
||||
const struct MovieClipUser *user,
|
||||
float size[2]);
|
||||
int BKE_movieclip_get_duration(struct MovieClip *clip);
|
||||
float BKE_movieclip_get_fps(struct MovieClip *clip);
|
||||
void BKE_movieclip_get_aspect(struct MovieClip *clip, float *aspx, float *aspy);
|
||||
bool BKE_movieclip_has_frame(struct MovieClip *clip, struct MovieClipUser *user);
|
||||
bool BKE_movieclip_has_frame(struct MovieClip *clip, const struct MovieClipUser *user);
|
||||
void BKE_movieclip_user_set_frame(struct MovieClipUser *user, int framenr);
|
||||
|
||||
void BKE_movieclip_update_scopes(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
struct MovieClipScopes *scopes);
|
||||
|
||||
/**
|
||||
* Get segments of cached frames. useful for debugging cache policies.
|
||||
*/
|
||||
void BKE_movieclip_get_cache_segments(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int *r_totseg,
|
||||
int **r_points);
|
||||
|
||||
|
@ -105,7 +107,7 @@ float BKE_movieclip_remap_scene_to_clip_frame(const struct MovieClip *clip, floa
|
|||
float BKE_movieclip_remap_clip_to_scene_frame(const struct MovieClip *clip, float framenr);
|
||||
|
||||
void BKE_movieclip_filename_for_frame(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
char *name);
|
||||
|
||||
/**
|
||||
|
@ -113,11 +115,11 @@ void BKE_movieclip_filename_for_frame(struct MovieClip *clip,
|
|||
* Used by a prefetch job which takes care of creating a local copy of the clip.
|
||||
*/
|
||||
struct ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(struct MovieClip *clip,
|
||||
struct MovieClipUser *user);
|
||||
const struct MovieClipUser *user);
|
||||
|
||||
bool BKE_movieclip_has_cached_frame(struct MovieClip *clip, struct MovieClipUser *user);
|
||||
bool BKE_movieclip_has_cached_frame(struct MovieClip *clip, const struct MovieClipUser *user);
|
||||
bool BKE_movieclip_put_frame_if_possible(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
struct ImBuf *ibuf);
|
||||
|
||||
struct GPUTexture *BKE_movieclip_get_gpu_texture(struct MovieClip *clip,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "BLI_cache_mutex.hh"
|
||||
#include "BLI_multi_value_map.hh"
|
||||
#include "BLI_resource_scope.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
@ -24,6 +25,10 @@ namespace blender::nodes {
|
|||
struct FieldInferencingInterface;
|
||||
class NodeDeclaration;
|
||||
struct GeometryNodesLazyFunctionGraphInfo;
|
||||
namespace anonymous_attribute_lifetime {
|
||||
struct RelationsInNode;
|
||||
}
|
||||
namespace aal = anonymous_attribute_lifetime;
|
||||
} // namespace blender::nodes
|
||||
|
||||
namespace blender {
|
||||
|
@ -106,6 +111,8 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
|
|||
|
||||
/** Information about how inputs and outputs of the node group interact with fields. */
|
||||
std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface;
|
||||
/** Information about usage of anonymous attributes within the group. */
|
||||
std::unique_ptr<nodes::aal::RelationsInNode> anonymous_attribute_relations;
|
||||
|
||||
/**
|
||||
* For geometry nodes, a lazy function graph with some additional info is cached. This is used to
|
||||
|
@ -162,13 +169,6 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
|
|||
/** #eNodeTreeChangedFlag. */
|
||||
uint32_t changed_flag = 0;
|
||||
|
||||
/**
|
||||
* The location of the sockets, in the view-space of the node editor.
|
||||
* \note Only calculated when drawing.
|
||||
*/
|
||||
float locx = 0;
|
||||
float locy = 0;
|
||||
|
||||
/**
|
||||
* Runtime-only cache of the number of input links, for multi-input sockets,
|
||||
* including dragged node links that aren't actually in the tree.
|
||||
|
@ -330,7 +330,11 @@ inline bool topology_cache_is_available(const bNodeSocket &socket)
|
|||
namespace node_field_inferencing {
|
||||
bool update_field_inferencing(const bNodeTree &tree);
|
||||
}
|
||||
|
||||
namespace anonymous_attribute_inferencing {
|
||||
Array<const nodes::aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
|
||||
ResourceScope &scope);
|
||||
bool update_anonymous_attribute_relations(bNodeTree &tree);
|
||||
} // namespace anonymous_attribute_inferencing
|
||||
} // namespace blender::bke
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -64,7 +64,7 @@ set(SRC
|
|||
intern/anim_path.c
|
||||
intern/anim_sys.c
|
||||
intern/anim_visualization.c
|
||||
intern/anonymous_attribute.cc
|
||||
intern/anonymous_attribute_id.cc
|
||||
intern/appdir.c
|
||||
intern/armature.c
|
||||
intern/armature_deform.c
|
||||
|
@ -229,6 +229,7 @@ set(SRC
|
|||
intern/nla.c
|
||||
intern/node.cc
|
||||
intern/node_runtime.cc
|
||||
intern/node_tree_anonymous_attributes.cc
|
||||
intern/node_tree_field_inferencing.cc
|
||||
intern/node_tree_update.cc
|
||||
intern/object.cc
|
||||
|
@ -315,8 +316,7 @@ set(SRC
|
|||
BKE_anim_path.h
|
||||
BKE_anim_visualization.h
|
||||
BKE_animsys.h
|
||||
BKE_anonymous_attribute.h
|
||||
BKE_anonymous_attribute.hh
|
||||
BKE_anonymous_attribute_id.hh
|
||||
BKE_appdir.h
|
||||
BKE_armature.h
|
||||
BKE_armature.hh
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
|
||||
using namespace blender::bke;
|
||||
|
||||
/**
|
||||
* A struct that identifies an attribute. It's lifetime is managed by an atomic reference count.
|
||||
*
|
||||
* Additionally, this struct can be strongly or weakly owned. The difference is that strong
|
||||
* ownership means that attributes with this id will be kept around. Weak ownership just makes sure
|
||||
* that the struct itself stays alive, but corresponding attributes might still be removed
|
||||
* automatically.
|
||||
*/
|
||||
struct AnonymousAttributeID {
|
||||
/**
|
||||
* Total number of references to this attribute id. Once this reaches zero, the struct can be
|
||||
* freed. This includes strong and weak references.
|
||||
*/
|
||||
mutable std::atomic<int> refcount_tot = 0;
|
||||
|
||||
/**
|
||||
* Number of strong references to this attribute id. When this is zero, the corresponding
|
||||
* attributes can be removed from geometries automatically.
|
||||
*/
|
||||
mutable std::atomic<int> refcount_strong = 0;
|
||||
|
||||
/**
|
||||
* Only used to identify this struct in a debugging session.
|
||||
*/
|
||||
std::string debug_name;
|
||||
|
||||
/**
|
||||
* Unique name of the this attribute id during the current session.
|
||||
*/
|
||||
std::string internal_name;
|
||||
};
|
||||
|
||||
/** Every time this function is called, it outputs a different name. */
|
||||
static std::string get_new_internal_name()
|
||||
{
|
||||
static std::atomic<int> index = 0;
|
||||
const int next_index = index.fetch_add(1);
|
||||
return ".a_" + std::to_string(next_index);
|
||||
}
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name)
|
||||
{
|
||||
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
|
||||
anonymous_id->debug_name = debug_name;
|
||||
anonymous_id->internal_name = get_new_internal_name();
|
||||
anonymous_id->refcount_tot.store(1);
|
||||
return anonymous_id;
|
||||
}
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name)
|
||||
{
|
||||
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
|
||||
anonymous_id->debug_name = debug_name;
|
||||
anonymous_id->internal_name = get_new_internal_name();
|
||||
anonymous_id->refcount_tot.store(1);
|
||||
anonymous_id->refcount_strong.store(1);
|
||||
return anonymous_id;
|
||||
}
|
||||
|
||||
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->refcount_strong.load() >= 1;
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_tot.fetch_add(1);
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_tot.fetch_add(1);
|
||||
anonymous_id->refcount_strong.fetch_add(1);
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1;
|
||||
if (new_refcount == 0) {
|
||||
BLI_assert(anonymous_id->refcount_strong == 0);
|
||||
delete anonymous_id;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_strong.fetch_sub(1);
|
||||
BKE_anonymous_attribute_id_decrement_weak(anonymous_id);
|
||||
}
|
||||
|
||||
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->debug_name.c_str();
|
||||
}
|
||||
|
||||
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->internal_name.c_str();
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
bool AnonymousAttributePropagationInfo::propagate(const AnonymousAttributeID &anonymous_id) const
|
||||
{
|
||||
if (this->propagate_all) {
|
||||
return true;
|
||||
}
|
||||
if (!this->names) {
|
||||
return false;
|
||||
}
|
||||
return this->names->contains_as(anonymous_id.name());
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
|
@ -40,13 +40,9 @@ namespace blender::bke {
|
|||
|
||||
std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (attribute_id) {
|
||||
stream << attribute_id.name();
|
||||
}
|
||||
else if (attribute_id.is_anonymous()) {
|
||||
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
|
||||
stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">";
|
||||
}
|
||||
else {
|
||||
stream << "<none>";
|
||||
}
|
||||
|
@ -153,7 +149,7 @@ eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains)
|
|||
static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer)
|
||||
{
|
||||
if (layer.anonymous_id != nullptr) {
|
||||
return layer.anonymous_id;
|
||||
return *layer.anonymous_id;
|
||||
}
|
||||
return layer.name;
|
||||
}
|
||||
|
@ -207,7 +203,7 @@ static void *add_generic_custom_data_layer(CustomData &custom_data,
|
|||
const int domain_num,
|
||||
const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
char attribute_name_c[MAX_NAME];
|
||||
attribute_id.name().copy(attribute_name_c);
|
||||
return CustomData_add_layer_named(
|
||||
|
@ -261,9 +257,6 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
|
|||
if (!attribute_id) {
|
||||
return false;
|
||||
}
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return layer.anonymous_id == &attribute_id.anonymous_id();
|
||||
}
|
||||
return layer.name == attribute_id.name();
|
||||
}
|
||||
|
||||
|
@ -451,14 +444,8 @@ GAttributeWriter CustomDataAttributeProvider::try_get_for_write(
|
|||
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
continue;
|
||||
}
|
||||
if (attribute_id.is_named()) {
|
||||
CustomData_duplicate_referenced_layer_named(
|
||||
custom_data, layer.type, layer.name, element_num);
|
||||
}
|
||||
else {
|
||||
CustomData_duplicate_referenced_layer_anonymous(
|
||||
custom_data, layer.type, &attribute_id.anonymous_id(), element_num);
|
||||
}
|
||||
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, element_num);
|
||||
|
||||
const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
|
||||
if (type == nullptr) {
|
||||
continue;
|
||||
|
@ -854,7 +841,7 @@ void MutableAttributeAccessor::remove_anonymous()
|
|||
}
|
||||
|
||||
while (!anonymous_ids.is_empty()) {
|
||||
this->remove(anonymous_ids.pop_last());
|
||||
this->remove(*anonymous_ids.pop_last());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -883,12 +870,7 @@ GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef
|
|||
#ifdef DEBUG
|
||||
if (attribute) {
|
||||
auto checker = std::make_shared<FinishCallChecker>();
|
||||
if (attribute_id.is_named()) {
|
||||
checker->name = attribute_id.name();
|
||||
}
|
||||
else {
|
||||
checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id());
|
||||
}
|
||||
checker->name = attribute_id.name();
|
||||
checker->real_finish_fn = attribute.tag_modified_fn;
|
||||
attribute.tag_modified_fn = [checker]() {
|
||||
if (checker->real_finish_fn) {
|
||||
|
@ -968,6 +950,7 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
|
|||
const bke::AttributeAccessor src_attributes,
|
||||
bke::MutableAttributeAccessor dst_attributes,
|
||||
const eAttrDomainMask domain_mask,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip)
|
||||
{
|
||||
Vector<AttributeTransferData> attributes;
|
||||
|
@ -976,10 +959,10 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
|
|||
if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_named() && skip.contains(id.name())) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
if (!id.should_be_kept()) {
|
||||
if (skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -999,6 +982,7 @@ void copy_attribute_domain(const AttributeAccessor src_attributes,
|
|||
MutableAttributeAccessor dst_attributes,
|
||||
const IndexMask selection,
|
||||
const eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip)
|
||||
{
|
||||
src_attributes.for_all(
|
||||
|
@ -1006,10 +990,10 @@ void copy_attribute_domain(const AttributeAccessor src_attributes,
|
|||
if (meta_data.domain != domain) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_named() && skip.contains(id.name())) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
if (!id.should_be_kept()) {
|
||||
if (skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -335,7 +335,7 @@ namespace attribute_accessor_functions {
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return false;
|
||||
}
|
||||
const StringRef name = attribute_id.name();
|
||||
|
@ -345,7 +345,7 @@ inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_i
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
@ -395,7 +395,7 @@ template<const ComponentAttributeProviders &providers>
|
|||
inline AttributeValidator lookup_validator(const void * /*owner*/,
|
||||
const blender::bke::AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return {};
|
||||
}
|
||||
const BuiltinAttributeProvider *provider =
|
||||
|
@ -442,7 +442,7 @@ inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner,
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
@ -461,7 +461,7 @@ inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attr
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool remove(void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
@ -486,7 +486,7 @@ inline bool add(void *owner,
|
|||
if (contains<providers>(owner, attribute_id)) {
|
||||
return false;
|
||||
}
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
|
|
@ -1115,8 +1115,15 @@ static void cloth_selfcollision(void *__restrict userdata,
|
|||
float epsilon = clmd->coll_parms->selfepsilon;
|
||||
float pa[3], pb[3], vect[3];
|
||||
|
||||
tri_a = &clmd->clothObject->tri[data->overlap[index].indexA];
|
||||
tri_b = &clmd->clothObject->tri[data->overlap[index].indexB];
|
||||
/* Collision math is currently not symmetric, so ensure a stable order for each pair. */
|
||||
int indexA = data->overlap[index].indexA, indexB = data->overlap[index].indexB;
|
||||
|
||||
if (indexA > indexB) {
|
||||
SWAP(int, indexA, indexB);
|
||||
}
|
||||
|
||||
tri_a = &clmd->clothObject->tri[indexA];
|
||||
tri_b = &clmd->clothObject->tri[indexB];
|
||||
|
||||
BLI_assert(cloth_bvh_selfcollision_is_active(clmd, clmd->clothObject, tri_a, tri_b));
|
||||
|
||||
|
@ -1521,8 +1528,9 @@ static bool cloth_bvh_obj_overlap_cb(void *userdata,
|
|||
|
||||
static bool cloth_bvh_self_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
|
||||
{
|
||||
/* No need for equal combinations (eg. (0,1) & (1,0)). */
|
||||
if (index_a < index_b) {
|
||||
/* This shouldn't happen, but just in case. Note that equal combinations
|
||||
* (eg. (0,1) & (1,0)) would be filtered out by BLI_bvhtree_overlap_self. */
|
||||
if (index_a != index_b) {
|
||||
ClothModifierData *clmd = (ClothModifierData *)userdata;
|
||||
struct Cloth *clothObject = clmd->clothObject;
|
||||
const MVertTri *tri_a, *tri_b;
|
||||
|
@ -1599,8 +1607,8 @@ int cloth_bvh_collision(
|
|||
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) {
|
||||
bvhtree_update_from_cloth(clmd, false, true);
|
||||
|
||||
overlap_self = BLI_bvhtree_overlap(
|
||||
cloth->bvhselftree, cloth->bvhselftree, &coll_count_self, cloth_bvh_self_overlap_cb, clmd);
|
||||
overlap_self = BLI_bvhtree_overlap_self(
|
||||
cloth->bvhselftree, &coll_count_self, cloth_bvh_self_overlap_cb, clmd);
|
||||
}
|
||||
|
||||
do {
|
||||
|
|
|
@ -28,6 +28,8 @@ BLI_CPP_TYPE_MAKE(Material *, CPPTypeFlags::BasicType)
|
|||
|
||||
BLI_CPP_TYPE_MAKE(MStringProperty, CPPTypeFlags::None);
|
||||
|
||||
BLI_CPP_TYPE_MAKE(blender::bke::AnonymousAttributeSet, CPPTypeFlags::None);
|
||||
|
||||
void BKE_cpp_types_init()
|
||||
{
|
||||
blender::register_cpp_types();
|
||||
|
@ -45,4 +47,6 @@ void BKE_cpp_types_init()
|
|||
BLI_CPP_TYPE_REGISTER(Material *);
|
||||
|
||||
BLI_CPP_TYPE_REGISTER(MStringProperty);
|
||||
|
||||
BLI_CPP_TYPE_REGISTER(blender::bke::AnonymousAttributeSet);
|
||||
}
|
||||
|
|
|
@ -328,18 +328,19 @@ static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_a
|
|||
static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes,
|
||||
const AttributeAccessor &mesh_attributes,
|
||||
const AttributeIDRef &id,
|
||||
const AttributeMetaData &meta_data)
|
||||
const AttributeMetaData &meta_data,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
|
||||
/* The position attribute has special non-generic evaluation. */
|
||||
if (id.is_named() && id.name() == "position") {
|
||||
if (id.name() == "position") {
|
||||
return false;
|
||||
}
|
||||
/* Don't propagate built-in curves attributes that are not built-in on meshes. */
|
||||
if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) {
|
||||
return false;
|
||||
}
|
||||
if (!id.should_be_kept()) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return false;
|
||||
}
|
||||
if (meta_data.data_type == CD_PROP_STRING) {
|
||||
|
@ -626,7 +627,8 @@ static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offset
|
|||
|
||||
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
const CurvesGeometry &profile,
|
||||
const bool fill_caps)
|
||||
const bool fill_caps,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const CurvesInfo curves_info = get_curves_info(main, profile);
|
||||
|
||||
|
@ -713,7 +715,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
|||
MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write();
|
||||
|
||||
main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id, meta_data)) {
|
||||
if (!should_add_attribute_to_mesh(
|
||||
main_attributes, mesh_attributes, id, meta_data, propagation_info)) {
|
||||
return true;
|
||||
}
|
||||
main_attributes_set.add_new(id);
|
||||
|
@ -750,7 +753,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
|||
if (main_attributes.contains(id)) {
|
||||
return true;
|
||||
}
|
||||
if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id, meta_data)) {
|
||||
if (!should_add_attribute_to_mesh(
|
||||
profile_attributes, mesh_attributes, id, meta_data, propagation_info)) {
|
||||
return true;
|
||||
}
|
||||
const eAttrDomain src_domain = meta_data.domain;
|
||||
|
@ -794,10 +798,11 @@ static CurvesGeometry get_curve_single_vert()
|
|||
return curves;
|
||||
}
|
||||
|
||||
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve)
|
||||
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
static const CurvesGeometry vert_curve = get_curve_single_vert();
|
||||
return curve_to_mesh_sweep(curve, vert_curve, false);
|
||||
return curve_to_mesh_sweep(curve, vert_curve, false, propagation_info);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -1047,8 +1047,10 @@ static Array<int> build_point_to_curve_map(const CurvesGeometry &curves)
|
|||
return point_to_curve_map;
|
||||
}
|
||||
|
||||
static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
|
||||
const IndexMask points_to_delete)
|
||||
static CurvesGeometry copy_with_removed_points(
|
||||
const CurvesGeometry &curves,
|
||||
const IndexMask points_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
/* Use a map from points to curves to facilitate using an #IndexMask input. */
|
||||
const Array<int> point_to_curve_map = build_point_to_curve_map(curves);
|
||||
|
@ -1093,9 +1095,15 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
|
|||
|
||||
CurvesGeometry new_curves{new_point_count, new_curve_count};
|
||||
Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
|
||||
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
|
||||
curves.attributes(),
|
||||
new_curves.attributes_for_write(),
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info);
|
||||
Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
|
||||
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
|
||||
curves.attributes(),
|
||||
new_curves.attributes_for_write(),
|
||||
ATTR_DOMAIN_MASK_CURVE,
|
||||
propagation_info);
|
||||
|
||||
threading::parallel_invoke(
|
||||
256 < new_point_count * new_curve_count,
|
||||
|
@ -1144,7 +1152,8 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
|
|||
return new_curves;
|
||||
}
|
||||
|
||||
void CurvesGeometry::remove_points(const IndexMask points_to_delete)
|
||||
void CurvesGeometry::remove_points(const IndexMask points_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
if (points_to_delete.is_empty()) {
|
||||
return;
|
||||
|
@ -1152,11 +1161,13 @@ void CurvesGeometry::remove_points(const IndexMask points_to_delete)
|
|||
if (points_to_delete.size() == this->points_num()) {
|
||||
*this = {};
|
||||
}
|
||||
*this = copy_with_removed_points(*this, points_to_delete);
|
||||
*this = copy_with_removed_points(*this, points_to_delete, propagation_info);
|
||||
}
|
||||
|
||||
static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
|
||||
const IndexMask curves_to_delete)
|
||||
static CurvesGeometry copy_with_removed_curves(
|
||||
const CurvesGeometry &curves,
|
||||
const IndexMask curves_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const Span<int> old_offsets = curves.offsets();
|
||||
const Vector<IndexRange> old_curve_ranges = curves_to_delete.extract_ranges_invert(
|
||||
|
@ -1178,9 +1189,15 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
|
|||
|
||||
CurvesGeometry new_curves{new_tot_points, new_tot_curves};
|
||||
Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
|
||||
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
|
||||
curves.attributes(),
|
||||
new_curves.attributes_for_write(),
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info);
|
||||
Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
|
||||
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
|
||||
curves.attributes(),
|
||||
new_curves.attributes_for_write(),
|
||||
ATTR_DOMAIN_MASK_CURVE,
|
||||
propagation_info);
|
||||
|
||||
threading::parallel_invoke(
|
||||
256 < new_tot_points * new_tot_curves,
|
||||
|
@ -1251,7 +1268,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
|
|||
return new_curves;
|
||||
}
|
||||
|
||||
void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
|
||||
void CurvesGeometry::remove_curves(const IndexMask curves_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
if (curves_to_delete.is_empty()) {
|
||||
return;
|
||||
|
@ -1260,7 +1278,7 @@ void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
|
|||
*this = {};
|
||||
return;
|
||||
}
|
||||
*this = copy_with_removed_curves(*this, curves_to_delete);
|
||||
*this = copy_with_removed_curves(*this, curves_to_delete, propagation_info);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -1315,7 +1333,7 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
|
|||
if (meta_data.data_type == CD_PROP_STRING) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_named() && bezier_handle_names.contains(id.name())) {
|
||||
if (bezier_handle_names.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_anonymous_attribute.h"
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_customdata_file.h"
|
||||
#include "BKE_deform.h"
|
||||
|
@ -2356,7 +2356,7 @@ bool CustomData_merge(const CustomData *source,
|
|||
layer->anonymous_id = nullptr;
|
||||
}
|
||||
else {
|
||||
BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id);
|
||||
layer->anonymous_id->user_add();
|
||||
}
|
||||
}
|
||||
if (alloctype == CD_ASSIGN) {
|
||||
|
@ -2461,7 +2461,7 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to
|
|||
const LayerTypeInfo *typeInfo;
|
||||
|
||||
if (layer->anonymous_id != nullptr) {
|
||||
BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id);
|
||||
layer->anonymous_id->user_remove();
|
||||
layer->anonymous_id = nullptr;
|
||||
}
|
||||
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
|
||||
|
@ -2958,9 +2958,9 @@ void *CustomData_add_layer_anonymous(CustomData *data,
|
|||
const eCDAllocType alloctype,
|
||||
void *layerdata,
|
||||
const int totelem,
|
||||
const AnonymousAttributeID *anonymous_id)
|
||||
const AnonymousAttributeIDHandle *anonymous_id)
|
||||
{
|
||||
const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id);
|
||||
const char *name = anonymous_id->name().c_str();
|
||||
CustomDataLayer *layer = customData_add_layer__internal(
|
||||
data, type, alloctype, layerdata, totelem, name);
|
||||
CustomData_update_typemap(data);
|
||||
|
@ -2969,7 +2969,7 @@ void *CustomData_add_layer_anonymous(CustomData *data,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
BKE_anonymous_attribute_id_increment_weak(anonymous_id);
|
||||
anonymous_id->user_add();
|
||||
layer->anonymous_id = anonymous_id;
|
||||
return layer->data;
|
||||
}
|
||||
|
@ -3148,20 +3148,6 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data,
|
|||
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
|
||||
}
|
||||
|
||||
void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data,
|
||||
const int /*type*/,
|
||||
const AnonymousAttributeID *anonymous_id,
|
||||
const int totelem)
|
||||
{
|
||||
for (int i = 0; i < data->totlayer; i++) {
|
||||
if (data->layers[i].anonymous_id == anonymous_id) {
|
||||
return customData_duplicate_referenced_layer_index(data, i, totelem);
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem)
|
||||
{
|
||||
for (int i = 0; i < data->totlayer; i++) {
|
||||
|
|
|
@ -1045,7 +1045,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
|||
GAttributeReader try_get_for_read(const void *owner,
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return {};
|
||||
}
|
||||
const Mesh *mesh = static_cast<const Mesh *>(owner);
|
||||
|
@ -1069,7 +1069,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
|||
|
||||
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return {};
|
||||
}
|
||||
Mesh *mesh = static_cast<Mesh *>(owner);
|
||||
|
@ -1090,7 +1090,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
|||
|
||||
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return false;
|
||||
}
|
||||
Mesh *mesh = static_cast<Mesh *>(owner);
|
||||
|
|
|
@ -332,7 +332,7 @@ GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryField
|
|||
const IndexMask /*mask*/) const
|
||||
{
|
||||
const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
|
||||
return context.attributes()->lookup(anonymous_id_.get(), context.domain(), data_type);
|
||||
return context.attributes()->lookup(*anonymous_id_, context.domain(), data_type);
|
||||
}
|
||||
|
||||
std::string AnonymousAttributeFieldInput::socket_inspection_name() const
|
||||
|
@ -363,8 +363,7 @@ std::optional<eAttrDomain> AnonymousAttributeFieldInput::preferred_domain(
|
|||
if (!attributes.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(
|
||||
anonymous_id_.get());
|
||||
const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(*anonymous_id_);
|
||||
if (!meta_data.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -434,8 +433,10 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
|
|||
GMutableSpan{type, buffer, domain_size});
|
||||
evaluator.evaluate();
|
||||
|
||||
if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) {
|
||||
if (attribute.domain == domain && attribute.varray.type() == type) {
|
||||
const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(attribute_id);
|
||||
|
||||
if (meta_data && meta_data->domain == domain && meta_data->data_type == data_type) {
|
||||
if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) {
|
||||
attribute.varray.set_all(buffer);
|
||||
attribute.finish();
|
||||
type.destruct_n(buffer, domain_size);
|
||||
|
@ -443,6 +444,7 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
attributes.remove(attribute_id);
|
||||
if (attributes.add(attribute_id, domain, data_type, bke::AttributeInitMoveArray{buffer})) {
|
||||
return true;
|
||||
|
|
|
@ -581,6 +581,7 @@ void GeometrySet::gather_attributes_for_propagation(
|
|||
const Span<GeometryComponentType> component_types,
|
||||
const GeometryComponentType dst_component_type,
|
||||
bool include_instances,
|
||||
const blender::bke::AnonymousAttributePropagationInfo &propagation_info,
|
||||
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const
|
||||
{
|
||||
using namespace blender;
|
||||
|
@ -605,7 +606,8 @@ void GeometrySet::gather_attributes_for_propagation(
|
|||
/* Propagating string attributes is not supported yet. */
|
||||
return;
|
||||
}
|
||||
if (!attribute_id.should_be_kept()) {
|
||||
if (attribute_id.is_anonymous() &&
|
||||
!propagation_info.propagate(attribute_id.anonymous_id())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,8 @@ blender::Span<InstanceReference> Instances::references() const
|
|||
return references_;
|
||||
}
|
||||
|
||||
void Instances::remove(const IndexMask mask)
|
||||
void Instances::remove(const IndexMask mask,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
using namespace blender;
|
||||
if (mask.is_range() && mask.as_range().start() == 0) {
|
||||
|
@ -132,7 +133,7 @@ void Instances::remove(const IndexMask mask)
|
|||
|
||||
src_attributes.foreach_attribute(
|
||||
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) {
|
||||
if (!id.should_be_kept()) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -3297,7 +3297,10 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
|
|||
return true;
|
||||
}
|
||||
|
||||
void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_report_flags)
|
||||
static void lib_override_library_operations_create(Main *bmain,
|
||||
ID *local,
|
||||
const eRNAOverrideMatch override_match_flags,
|
||||
eRNAOverrideMatchResult *r_report_flags)
|
||||
{
|
||||
BLI_assert(!ID_IS_LINKED(local));
|
||||
BLI_assert(local->override_library != nullptr);
|
||||
|
@ -3330,19 +3333,24 @@ void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_r
|
|||
RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference);
|
||||
|
||||
eRNAOverrideMatchResult local_report_flags = RNA_OVERRIDE_MATCH_RESULT_INIT;
|
||||
RNA_struct_override_matches(
|
||||
bmain,
|
||||
&rnaptr_local,
|
||||
&rnaptr_reference,
|
||||
nullptr,
|
||||
0,
|
||||
local->override_library,
|
||||
(eRNAOverrideMatch)(RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE),
|
||||
&local_report_flags);
|
||||
RNA_struct_override_matches(bmain,
|
||||
&rnaptr_local,
|
||||
&rnaptr_reference,
|
||||
nullptr,
|
||||
0,
|
||||
local->override_library,
|
||||
override_match_flags,
|
||||
&local_report_flags);
|
||||
|
||||
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) {
|
||||
CLOG_INFO(&LOG, 2, "We did restore some properties of %s from its reference", local->name);
|
||||
}
|
||||
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED) {
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"We did tag some properties of %s for restoration from its reference",
|
||||
local->name);
|
||||
}
|
||||
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
|
||||
CLOG_INFO(&LOG, 2, "We did generate library override rules for %s", local->name);
|
||||
}
|
||||
|
@ -3351,10 +3359,50 @@ void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_r
|
|||
}
|
||||
|
||||
if (r_report_flags != nullptr) {
|
||||
*r_report_flags |= local_report_flags;
|
||||
*r_report_flags = static_cast<eRNAOverrideMatchResult>(*r_report_flags | local_report_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_report_flags)
|
||||
{
|
||||
lib_override_library_operations_create(
|
||||
bmain,
|
||||
local,
|
||||
static_cast<eRNAOverrideMatch>(RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE),
|
||||
reinterpret_cast<eRNAOverrideMatchResult *>(r_report_flags));
|
||||
}
|
||||
|
||||
void BKE_lib_override_library_operations_restore(Main *bmain, ID *local, int *r_report_flags)
|
||||
{
|
||||
if (!ID_IS_OVERRIDE_LIBRARY_REAL(local) || (local->override_library->runtime->tag &
|
||||
IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RESTORE) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA rnaptr_src, rnaptr_dst;
|
||||
RNA_id_pointer_create(local, &rnaptr_dst);
|
||||
RNA_id_pointer_create(local->override_library->reference, &rnaptr_src);
|
||||
RNA_struct_override_apply(
|
||||
bmain,
|
||||
&rnaptr_dst,
|
||||
&rnaptr_src,
|
||||
nullptr,
|
||||
local->override_library,
|
||||
static_cast<eRNAOverrideApplyFlag>(RNA_OVERRIDE_APPLY_FLAG_SKIP_RESYNC_CHECK |
|
||||
RNA_OVERRIDE_APPLY_FLAG_RESTORE_ONLY));
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (
|
||||
IDOverrideLibraryProperty *, op, &local->override_library->properties) {
|
||||
if (op->tag & IDOVERRIDE_LIBRARY_PROPERTY_TAG_NEEDS_RETORE) {
|
||||
BKE_lib_override_library_property_delete(local->override_library, op);
|
||||
}
|
||||
}
|
||||
local->override_library->runtime->tag &= ~IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RESTORE;
|
||||
|
||||
if (r_report_flags != nullptr) {
|
||||
*r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_RESTORED;
|
||||
}
|
||||
}
|
||||
|
||||
struct LibOverrideOpCreateData {
|
||||
Main *bmain;
|
||||
|
@ -3368,8 +3416,12 @@ static void lib_override_library_operations_create_cb(TaskPool *__restrict pool,
|
|||
ID *id = static_cast<ID *>(taskdata);
|
||||
|
||||
eRNAOverrideMatchResult report_flags = RNA_OVERRIDE_MATCH_RESULT_INIT;
|
||||
BKE_lib_override_library_operations_create(
|
||||
create_data->bmain, id, reinterpret_cast<int *>(&report_flags));
|
||||
lib_override_library_operations_create(
|
||||
create_data->bmain,
|
||||
id,
|
||||
static_cast<eRNAOverrideMatch>(RNA_OVERRIDE_COMPARE_CREATE |
|
||||
RNA_OVERRIDE_COMPARE_TAG_FOR_RESTORE),
|
||||
&report_flags);
|
||||
atomic_fetch_and_or_uint32(reinterpret_cast<uint32_t *>(&create_data->report_flags),
|
||||
report_flags);
|
||||
}
|
||||
|
@ -3443,6 +3495,13 @@ void BKE_lib_override_library_main_operations_create(Main *bmain,
|
|||
|
||||
BLI_task_pool_free(task_pool);
|
||||
|
||||
if (create_pool_data.report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED) {
|
||||
BKE_lib_override_library_main_operations_restore(
|
||||
bmain, reinterpret_cast<int *>(&create_pool_data.report_flags));
|
||||
create_pool_data.report_flags = static_cast<eRNAOverrideMatchResult>(
|
||||
(create_pool_data.report_flags & ~RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED));
|
||||
}
|
||||
|
||||
if (r_report_flags != nullptr) {
|
||||
*r_report_flags |= create_pool_data.report_flags;
|
||||
}
|
||||
|
@ -3456,6 +3515,28 @@ void BKE_lib_override_library_main_operations_create(Main *bmain,
|
|||
#endif
|
||||
}
|
||||
|
||||
void BKE_lib_override_library_main_operations_restore(Main *bmain, int *r_report_flags)
|
||||
{
|
||||
ID *id;
|
||||
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
if (!(!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
|
||||
(id->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RESTORE) !=
|
||||
0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only restore overrides if we do have the real reference data available, and not some empty
|
||||
* 'placeholder' for missing data (broken links). */
|
||||
if (id->override_library->reference->tag & LIB_TAG_MISSING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BKE_lib_override_library_operations_restore(bmain, id, r_report_flags);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
}
|
||||
|
||||
static bool lib_override_library_id_reset_do(Main *bmain,
|
||||
ID *id_root,
|
||||
const bool do_reset_system_override)
|
||||
|
|
|
@ -822,7 +822,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o
|
|||
return BKE_mesh_copy_for_eval(mesh, false);
|
||||
}
|
||||
if (const Curves *curves = get_evaluated_curves_from_object(evaluated_object)) {
|
||||
return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry));
|
||||
const blender::bke::AnonymousAttributePropagationInfo propagation_info;
|
||||
return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry),
|
||||
propagation_info);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -884,7 +884,7 @@ static ImBuf *get_imbuf_cache(MovieClip *clip, const MovieClipUser *user, int fl
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static bool has_imbuf_cache(MovieClip *clip, MovieClipUser *user, int flag)
|
||||
static bool has_imbuf_cache(MovieClip *clip, const MovieClipUser *user, int flag)
|
||||
{
|
||||
if (clip->cache) {
|
||||
MovieClipImBufCacheKey key;
|
||||
|
@ -1344,7 +1344,6 @@ static ImBuf *movieclip_get_postprocessed_ibuf(
|
|||
}
|
||||
|
||||
if (ibuf) {
|
||||
clip->lastframe = framenr;
|
||||
real_ibuf_size(clip, user, ibuf, &clip->lastsize[0], &clip->lastsize[1]);
|
||||
|
||||
/* Post-process frame and put to cache if needed. */
|
||||
|
@ -1373,25 +1372,31 @@ static ImBuf *movieclip_get_postprocessed_ibuf(
|
|||
return ibuf;
|
||||
}
|
||||
|
||||
ImBuf *BKE_movieclip_get_ibuf(MovieClip *clip, MovieClipUser *user)
|
||||
ImBuf *BKE_movieclip_get_ibuf(MovieClip *clip, const MovieClipUser *user)
|
||||
{
|
||||
return BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, 0);
|
||||
}
|
||||
|
||||
ImBuf *BKE_movieclip_get_ibuf_flag(MovieClip *clip, MovieClipUser *user, int flag, int cache_flag)
|
||||
ImBuf *BKE_movieclip_get_ibuf_flag(MovieClip *clip,
|
||||
const MovieClipUser *user,
|
||||
const int flag,
|
||||
const int cache_flag)
|
||||
{
|
||||
return movieclip_get_postprocessed_ibuf(clip, user, flag, 0, cache_flag);
|
||||
}
|
||||
|
||||
ImBuf *BKE_movieclip_get_postprocessed_ibuf(MovieClip *clip,
|
||||
MovieClipUser *user,
|
||||
int postprocess_flag)
|
||||
const MovieClipUser *user,
|
||||
const int postprocess_flag)
|
||||
{
|
||||
return movieclip_get_postprocessed_ibuf(clip, user, clip->flag, postprocess_flag, 0);
|
||||
}
|
||||
|
||||
static ImBuf *get_stable_cached_frame(
|
||||
MovieClip *clip, MovieClipUser *user, ImBuf *reference_ibuf, int framenr, int postprocess_flag)
|
||||
static ImBuf *get_stable_cached_frame(MovieClip *clip,
|
||||
const MovieClipUser *user,
|
||||
ImBuf *reference_ibuf,
|
||||
const int framenr,
|
||||
const int postprocess_flag)
|
||||
{
|
||||
MovieClipCache *cache = clip->cache;
|
||||
MovieTracking *tracking = &clip->tracking;
|
||||
|
@ -1449,8 +1454,11 @@ static ImBuf *get_stable_cached_frame(
|
|||
return stableibuf;
|
||||
}
|
||||
|
||||
static ImBuf *put_stabilized_frame_to_cache(
|
||||
MovieClip *clip, MovieClipUser *user, ImBuf *ibuf, int framenr, int postprocess_flag)
|
||||
static ImBuf *put_stabilized_frame_to_cache(MovieClip *clip,
|
||||
const MovieClipUser *user,
|
||||
ImBuf *ibuf,
|
||||
const int framenr,
|
||||
const int postprocess_flag)
|
||||
{
|
||||
MovieClipCache *cache = clip->cache;
|
||||
MovieTracking *tracking = &clip->tracking;
|
||||
|
@ -1492,11 +1500,11 @@ static ImBuf *put_stabilized_frame_to_cache(
|
|||
}
|
||||
|
||||
ImBuf *BKE_movieclip_get_stable_ibuf(MovieClip *clip,
|
||||
MovieClipUser *user,
|
||||
const MovieClipUser *user,
|
||||
float loc[2],
|
||||
float *scale,
|
||||
float *angle,
|
||||
int postprocess_flag)
|
||||
const int postprocess_flag)
|
||||
{
|
||||
ImBuf *ibuf, *stableibuf = NULL;
|
||||
int framenr = user->framenr;
|
||||
|
@ -1552,7 +1560,7 @@ ImBuf *BKE_movieclip_get_stable_ibuf(MovieClip *clip,
|
|||
return ibuf;
|
||||
}
|
||||
|
||||
bool BKE_movieclip_has_frame(MovieClip *clip, MovieClipUser *user)
|
||||
bool BKE_movieclip_has_frame(MovieClip *clip, const MovieClipUser *user)
|
||||
{
|
||||
ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, user);
|
||||
|
||||
|
@ -1564,19 +1572,9 @@ bool BKE_movieclip_has_frame(MovieClip *clip, MovieClipUser *user)
|
|||
return false;
|
||||
}
|
||||
|
||||
void BKE_movieclip_get_size(MovieClip *clip, MovieClipUser *user, int *width, int *height)
|
||||
void BKE_movieclip_get_size(MovieClip *clip, const MovieClipUser *user, int *width, int *height)
|
||||
{
|
||||
#if 0
|
||||
/* originally was needed to support image sequences with different image dimensions,
|
||||
* which might be useful for such things as reconstruction of unordered image sequence,
|
||||
* or painting/rotoscoping of non-equal-sized images, but this ended up in unneeded
|
||||
* cache lookups and even unwanted non-proxied files loading when doing mask parenting,
|
||||
* so let's disable this for now and assume image sequence consists of images with
|
||||
* equal sizes (sergey)
|
||||
* TODO(sergey): Support reading sequences of different resolution.
|
||||
*/
|
||||
if (user->framenr == clip->lastframe) {
|
||||
#endif
|
||||
/* TODO(sergey): Support reading sequences of different resolution. */
|
||||
if (clip->lastsize[0] != 0 && clip->lastsize[1] != 0) {
|
||||
*width = clip->lastsize[0];
|
||||
*height = clip->lastsize[1];
|
||||
|
@ -1597,7 +1595,7 @@ void BKE_movieclip_get_size(MovieClip *clip, MovieClipUser *user, int *width, in
|
|||
}
|
||||
}
|
||||
}
|
||||
void BKE_movieclip_get_size_fl(MovieClip *clip, MovieClipUser *user, float size[2])
|
||||
void BKE_movieclip_get_size_fl(MovieClip *clip, const MovieClipUser *user, float size[2])
|
||||
{
|
||||
int width, height;
|
||||
BKE_movieclip_get_size(clip, user, &width, &height);
|
||||
|
@ -1641,7 +1639,7 @@ void BKE_movieclip_get_aspect(MovieClip *clip, float *aspx, float *aspy)
|
|||
}
|
||||
|
||||
void BKE_movieclip_get_cache_segments(MovieClip *clip,
|
||||
MovieClipUser *user,
|
||||
const MovieClipUser *user,
|
||||
int *r_totseg,
|
||||
int **r_points)
|
||||
{
|
||||
|
@ -1728,7 +1726,9 @@ void BKE_movieclip_reload(Main *bmain, MovieClip *clip)
|
|||
BKE_ntree_update_tag_id_changed(bmain, &clip->id);
|
||||
}
|
||||
|
||||
void BKE_movieclip_update_scopes(MovieClip *clip, MovieClipUser *user, MovieClipScopes *scopes)
|
||||
void BKE_movieclip_update_scopes(MovieClip *clip,
|
||||
const MovieClipUser *user,
|
||||
MovieClipScopes *scopes)
|
||||
{
|
||||
if (scopes->ok) {
|
||||
return;
|
||||
|
@ -1945,17 +1945,17 @@ bool BKE_movieclip_proxy_enabled(MovieClip *clip)
|
|||
return clip->flag & MCLIP_USE_PROXY;
|
||||
}
|
||||
|
||||
float BKE_movieclip_remap_scene_to_clip_frame(const MovieClip *clip, float framenr)
|
||||
float BKE_movieclip_remap_scene_to_clip_frame(const MovieClip *clip, const float framenr)
|
||||
{
|
||||
return framenr - (float)clip->start_frame + 1.0f;
|
||||
}
|
||||
|
||||
float BKE_movieclip_remap_clip_to_scene_frame(const MovieClip *clip, float framenr)
|
||||
float BKE_movieclip_remap_clip_to_scene_frame(const MovieClip *clip, const float framenr)
|
||||
{
|
||||
return framenr + (float)clip->start_frame - 1.0f;
|
||||
}
|
||||
|
||||
void BKE_movieclip_filename_for_frame(MovieClip *clip, MovieClipUser *user, char *name)
|
||||
void BKE_movieclip_filename_for_frame(MovieClip *clip, const MovieClipUser *user, char *name)
|
||||
{
|
||||
if (clip->source == MCLIP_SRC_SEQUENCE) {
|
||||
int use_proxy;
|
||||
|
@ -1977,7 +1977,7 @@ void BKE_movieclip_filename_for_frame(MovieClip *clip, MovieClipUser *user, char
|
|||
}
|
||||
}
|
||||
|
||||
ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(MovieClip *clip, MovieClipUser *user)
|
||||
ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(MovieClip *clip, const MovieClipUser *user)
|
||||
{
|
||||
ImBuf *ibuf = NULL;
|
||||
|
||||
|
@ -1988,7 +1988,7 @@ ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(MovieClip *clip, MovieClipUser
|
|||
return ibuf;
|
||||
}
|
||||
|
||||
bool BKE_movieclip_has_cached_frame(MovieClip *clip, MovieClipUser *user)
|
||||
bool BKE_movieclip_has_cached_frame(MovieClip *clip, const MovieClipUser *user)
|
||||
{
|
||||
bool has_frame = false;
|
||||
|
||||
|
@ -1999,7 +1999,7 @@ bool BKE_movieclip_has_cached_frame(MovieClip *clip, MovieClipUser *user)
|
|||
return has_frame;
|
||||
}
|
||||
|
||||
bool BKE_movieclip_put_frame_if_possible(MovieClip *clip, MovieClipUser *user, ImBuf *ibuf)
|
||||
bool BKE_movieclip_put_frame_if_possible(MovieClip *clip, const MovieClipUser *user, ImBuf *ibuf)
|
||||
{
|
||||
bool result;
|
||||
|
||||
|
|
|
@ -207,6 +207,11 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons
|
|||
dst_runtime.field_inferencing_interface = std::make_unique<FieldInferencingInterface>(
|
||||
*ntree_src->runtime->field_inferencing_interface);
|
||||
}
|
||||
if (ntree_src->runtime->anonymous_attribute_relations) {
|
||||
dst_runtime.anonymous_attribute_relations =
|
||||
std::make_unique<blender::nodes::anonymous_attribute_lifetime::RelationsInNode>(
|
||||
*ntree_src->runtime->anonymous_attribute_relations);
|
||||
}
|
||||
|
||||
if (flag & LIB_ID_COPY_NO_PREVIEW) {
|
||||
ntree_dst->preview = nullptr;
|
||||
|
@ -3556,16 +3561,16 @@ void nodeSetActive(bNodeTree *ntree, bNode *node)
|
|||
void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_available)
|
||||
{
|
||||
const bool was_available = (sock->flag & SOCK_UNAVAIL) == 0;
|
||||
if (is_available != was_available) {
|
||||
BKE_ntree_update_tag_socket_availability(ntree, sock);
|
||||
if (is_available == was_available) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_available) {
|
||||
sock->flag &= ~SOCK_UNAVAIL;
|
||||
}
|
||||
else {
|
||||
sock->flag |= SOCK_UNAVAIL;
|
||||
}
|
||||
BKE_ntree_update_tag_socket_availability(ntree, sock);
|
||||
}
|
||||
|
||||
int nodeSocketLinkLimit(const bNodeSocket *sock)
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "NOD_node_declaration.hh"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "BLI_multi_value_map.hh"
|
||||
#include "BLI_resource_scope.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
namespace blender::bke::anonymous_attribute_inferencing {
|
||||
namespace aal = nodes::aal;
|
||||
using nodes::NodeDeclaration;
|
||||
|
||||
static const aal::RelationsInNode &get_relations_in_node(const bNode &node, ResourceScope &scope)
|
||||
{
|
||||
if (node.is_group()) {
|
||||
if (const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id)) {
|
||||
BLI_assert(group->runtime->anonymous_attribute_relations);
|
||||
return *group->runtime->anonymous_attribute_relations;
|
||||
}
|
||||
}
|
||||
if (const NodeDeclaration *node_decl = node.declaration()) {
|
||||
if (const aal::RelationsInNode *relations = node_decl->anonymous_attribute_relations()) {
|
||||
return *relations;
|
||||
}
|
||||
}
|
||||
return scope.construct<aal::RelationsInNode>();
|
||||
}
|
||||
|
||||
Array<const aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
|
||||
ResourceScope &scope)
|
||||
{
|
||||
const Span<const bNode *> nodes = tree.all_nodes();
|
||||
Array<const aal::RelationsInNode *> relations_by_node(nodes.size());
|
||||
for (const int i : nodes.index_range()) {
|
||||
relations_by_node[i] = &get_relations_in_node(*nodes[i], scope);
|
||||
}
|
||||
return relations_by_node;
|
||||
}
|
||||
|
||||
static bool socket_is_field(const bNodeSocket &socket)
|
||||
{
|
||||
return socket.display_shape == SOCK_DISPLAY_SHAPE_DIAMOND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start at a group output socket and find all linked group inputs.
|
||||
*/
|
||||
static Vector<int> find_linked_group_inputs(
|
||||
const bNodeTree &tree,
|
||||
const bNodeSocket &group_output_socket,
|
||||
const FunctionRef<Vector<int>(const bNodeSocket &)> get_linked_node_inputs)
|
||||
{
|
||||
Set<const bNodeSocket *> found_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
Vector<int> input_indices;
|
||||
|
||||
found_sockets.add_new(&group_output_socket);
|
||||
sockets_to_check.push(&group_output_socket);
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket &socket = *sockets_to_check.pop();
|
||||
if (socket.is_input()) {
|
||||
for (const bNodeLink *link : socket.directly_linked_links()) {
|
||||
if (link->is_used()) {
|
||||
const bNodeSocket &from_socket = *link->fromsock;
|
||||
if (found_sockets.add(&from_socket)) {
|
||||
sockets_to_check.push(&from_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const bNode &node = socket.owner_node();
|
||||
for (const int input_index : get_linked_node_inputs(socket)) {
|
||||
const bNodeSocket &input_socket = node.input_socket(input_index);
|
||||
if (input_socket.is_available()) {
|
||||
if (found_sockets.add(&input_socket)) {
|
||||
sockets_to_check.push(&input_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const bNode *node : tree.group_input_nodes()) {
|
||||
for (const bNodeSocket *socket : node->output_sockets()) {
|
||||
if (found_sockets.contains(socket)) {
|
||||
input_indices.append_non_duplicates(socket->index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return input_indices;
|
||||
}
|
||||
|
||||
static void infer_propagate_relations(const bNodeTree &tree,
|
||||
const Span<const aal::RelationsInNode *> relations_by_node,
|
||||
const bNode &group_output_node,
|
||||
aal::RelationsInNode &r_relations)
|
||||
{
|
||||
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
|
||||
if (group_output_socket->type != SOCK_GEOMETRY) {
|
||||
continue;
|
||||
}
|
||||
const Vector<int> input_indices = find_linked_group_inputs(
|
||||
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
|
||||
Vector<int> indices;
|
||||
for (const aal::PropagateRelation &relation :
|
||||
relations_by_node[output_socket.owner_node().index()]->propagate_relations) {
|
||||
if (relation.to_geometry_output == output_socket.index()) {
|
||||
indices.append(relation.from_geometry_input);
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
});
|
||||
for (const int input_index : input_indices) {
|
||||
aal::PropagateRelation relation;
|
||||
relation.from_geometry_input = input_index;
|
||||
relation.to_geometry_output = group_output_socket->index();
|
||||
r_relations.propagate_relations.append(relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void infer_reference_relations(const bNodeTree &tree,
|
||||
const Span<const aal::RelationsInNode *> relations_by_node,
|
||||
const bNode &group_output_node,
|
||||
aal::RelationsInNode &r_relations)
|
||||
{
|
||||
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
|
||||
if (!socket_is_field(*group_output_socket)) {
|
||||
continue;
|
||||
}
|
||||
const Vector<int> input_indices = find_linked_group_inputs(
|
||||
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
|
||||
Vector<int> indices;
|
||||
for (const aal::ReferenceRelation &relation :
|
||||
relations_by_node[output_socket.owner_node().index()]->reference_relations) {
|
||||
if (relation.to_field_output == output_socket.index()) {
|
||||
indices.append(relation.from_field_input);
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
});
|
||||
for (const int input_index : input_indices) {
|
||||
if (tree.runtime->field_inferencing_interface->inputs[input_index] !=
|
||||
nodes::InputSocketFieldType::None) {
|
||||
aal::ReferenceRelation relation;
|
||||
relation.from_field_input = input_index;
|
||||
relation.to_field_output = group_output_socket->index();
|
||||
r_relations.reference_relations.append(relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find group output geometries that contain anonymous attributes referenced by the field.
|
||||
* If `nullopt` is returned, the field does not depend on any anonymous attribute created in this
|
||||
* node tree.
|
||||
*/
|
||||
static std::optional<Vector<int>> find_available_on_outputs(
|
||||
const bNodeSocket &initial_group_output_socket,
|
||||
const bNode &group_output_node,
|
||||
const Span<const aal::RelationsInNode *> relations_by_node)
|
||||
{
|
||||
Set<const bNodeSocket *> geometry_sockets;
|
||||
|
||||
{
|
||||
/* Find the nodes that added anonymous attributes to the field. */
|
||||
Set<const bNodeSocket *> found_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
found_sockets.add_new(&initial_group_output_socket);
|
||||
sockets_to_check.push(&initial_group_output_socket);
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket &socket = *sockets_to_check.pop();
|
||||
if (socket.is_input()) {
|
||||
for (const bNodeLink *link : socket.directly_linked_links()) {
|
||||
if (link->is_used()) {
|
||||
const bNodeSocket &from_socket = *link->fromsock;
|
||||
if (found_sockets.add(&from_socket)) {
|
||||
sockets_to_check.push(&from_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const bNode &node = socket.owner_node();
|
||||
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
|
||||
for (const aal::AvailableRelation &relation : relations.available_relations) {
|
||||
if (socket.index() == relation.field_output) {
|
||||
const bNodeSocket &geometry_output = node.output_socket(relation.geometry_output);
|
||||
if (geometry_output.is_available()) {
|
||||
geometry_sockets.add(&geometry_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
|
||||
if (socket.index() == relation.to_field_output) {
|
||||
const bNodeSocket &field_input = node.input_socket(relation.from_field_input);
|
||||
if (field_input.is_available()) {
|
||||
if (found_sockets.add(&field_input)) {
|
||||
sockets_to_check.push(&field_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (geometry_sockets.is_empty()) {
|
||||
/* The field does not depend on any anonymous attribute created within this node tree. */
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/* Find the group output geometries that contain the anonymous attribute referenced by the field
|
||||
* output. */
|
||||
Set<const bNodeSocket *> found_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
for (const bNodeSocket *socket : geometry_sockets) {
|
||||
found_sockets.add_new(socket);
|
||||
sockets_to_check.push(socket);
|
||||
}
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket &socket = *sockets_to_check.pop();
|
||||
if (socket.is_input()) {
|
||||
const bNode &node = socket.owner_node();
|
||||
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
|
||||
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
|
||||
if (socket.index() == relation.from_geometry_input) {
|
||||
const bNodeSocket &output_socket = node.output_socket(relation.to_geometry_output);
|
||||
if (output_socket.is_available()) {
|
||||
if (found_sockets.add(&output_socket)) {
|
||||
sockets_to_check.push(&output_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const bNodeLink *link : socket.directly_linked_links()) {
|
||||
if (link->is_used()) {
|
||||
const bNodeSocket &to_socket = *link->tosock;
|
||||
if (found_sockets.add(&to_socket)) {
|
||||
sockets_to_check.push(&to_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<int> output_indices;
|
||||
for (const bNodeSocket *socket : group_output_node.input_sockets().drop_back(1)) {
|
||||
if (found_sockets.contains(socket)) {
|
||||
output_indices.append(socket->index());
|
||||
}
|
||||
}
|
||||
return output_indices;
|
||||
}
|
||||
|
||||
static void infer_available_relations(const Span<const aal::RelationsInNode *> relations_by_node,
|
||||
const bNode &group_output_node,
|
||||
aal::RelationsInNode &r_relations)
|
||||
{
|
||||
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
|
||||
if (!socket_is_field(*group_output_socket)) {
|
||||
continue;
|
||||
}
|
||||
const std::optional<Vector<int>> output_indices = find_available_on_outputs(
|
||||
*group_output_socket, group_output_node, relations_by_node);
|
||||
if (output_indices.has_value()) {
|
||||
if (output_indices->is_empty()) {
|
||||
r_relations.available_on_none.append(group_output_socket->index());
|
||||
}
|
||||
else {
|
||||
for (const int output_index : *output_indices) {
|
||||
aal::AvailableRelation relation;
|
||||
relation.field_output = group_output_socket->index();
|
||||
relation.geometry_output = output_index;
|
||||
r_relations.available_relations.append(relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all the geometry inputs that the field input may be evaluated on.
|
||||
*/
|
||||
static Vector<int> find_eval_on_inputs(const bNodeTree &tree,
|
||||
const int field_input_index,
|
||||
const Span<const aal::RelationsInNode *> relations_by_node)
|
||||
{
|
||||
const Span<const bNode *> group_input_nodes = tree.group_input_nodes();
|
||||
Set<const bNodeSocket *> geometry_sockets;
|
||||
|
||||
{
|
||||
/* Find all the nodes that evaluate the input field. */
|
||||
Set<const bNodeSocket *> found_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
for (const bNode *node : group_input_nodes) {
|
||||
const bNodeSocket &socket = node->output_socket(field_input_index);
|
||||
found_sockets.add_new(&socket);
|
||||
sockets_to_check.push(&socket);
|
||||
}
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket &socket = *sockets_to_check.pop();
|
||||
if (socket.is_input()) {
|
||||
const bNode &node = socket.owner_node();
|
||||
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
|
||||
for (const aal::EvalRelation &relation : relations.eval_relations) {
|
||||
if (socket.index() == relation.field_input) {
|
||||
const bNodeSocket &geometry_input = node.input_socket(relation.geometry_input);
|
||||
if (geometry_input.is_available()) {
|
||||
geometry_sockets.add(&geometry_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
|
||||
if (socket.index() == relation.from_field_input) {
|
||||
const bNodeSocket &field_output = node.output_socket(relation.to_field_output);
|
||||
if (field_output.is_available()) {
|
||||
if (found_sockets.add(&field_output)) {
|
||||
sockets_to_check.push(&field_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const bNodeLink *link : socket.directly_linked_links()) {
|
||||
if (link->is_used()) {
|
||||
const bNodeSocket &to_socket = *link->tosock;
|
||||
if (found_sockets.add(&to_socket)) {
|
||||
sockets_to_check.push(&to_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (geometry_sockets.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/* Find the group input geometries whose attributes are propagated to the nodes that evaluate the
|
||||
* field. */
|
||||
Set<const bNodeSocket *> found_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
Vector<int> geometry_input_indices;
|
||||
|
||||
for (const bNodeSocket *socket : geometry_sockets) {
|
||||
found_sockets.add_new(socket);
|
||||
sockets_to_check.push(socket);
|
||||
}
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket &socket = *sockets_to_check.pop();
|
||||
if (socket.is_input()) {
|
||||
for (const bNodeLink *link : socket.directly_linked_links()) {
|
||||
if (link->is_used()) {
|
||||
const bNodeSocket &from_socket = *link->fromsock;
|
||||
if (found_sockets.add(&from_socket)) {
|
||||
sockets_to_check.push(&from_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const bNode &node = socket.owner_node();
|
||||
if (node.is_group_input()) {
|
||||
geometry_input_indices.append_non_duplicates(socket.index());
|
||||
}
|
||||
else {
|
||||
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
|
||||
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
|
||||
if (socket.index() == relation.to_geometry_output) {
|
||||
const bNodeSocket &input_socket = node.input_socket(relation.from_geometry_input);
|
||||
if (input_socket.is_available()) {
|
||||
if (found_sockets.add(&input_socket)) {
|
||||
sockets_to_check.push(&input_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return geometry_input_indices;
|
||||
}
|
||||
|
||||
static void infer_eval_relations(const bNodeTree &tree,
|
||||
const Span<const aal::RelationsInNode *> relations_by_node,
|
||||
aal::RelationsInNode &r_relations)
|
||||
{
|
||||
for (const int input_index : tree.interface_inputs().index_range()) {
|
||||
if (tree.runtime->field_inferencing_interface->inputs[input_index] ==
|
||||
nodes::InputSocketFieldType::None) {
|
||||
continue;
|
||||
}
|
||||
const Vector<int> geometry_input_indices = find_eval_on_inputs(
|
||||
tree, input_index, relations_by_node);
|
||||
for (const int geometry_input : geometry_input_indices) {
|
||||
aal::EvalRelation relation;
|
||||
relation.field_input = input_index;
|
||||
relation.geometry_input = geometry_input;
|
||||
r_relations.eval_relations.append(std::move(relation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool update_anonymous_attribute_relations(bNodeTree &tree)
|
||||
{
|
||||
tree.ensure_topology_cache();
|
||||
|
||||
ResourceScope scope;
|
||||
Array<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
|
||||
|
||||
std::unique_ptr<aal::RelationsInNode> new_relations = std::make_unique<aal::RelationsInNode>();
|
||||
if (!tree.has_available_link_cycle()) {
|
||||
if (const bNode *group_output_node = tree.group_output_node()) {
|
||||
infer_propagate_relations(tree, relations_by_node, *group_output_node, *new_relations);
|
||||
infer_reference_relations(tree, relations_by_node, *group_output_node, *new_relations);
|
||||
infer_available_relations(relations_by_node, *group_output_node, *new_relations);
|
||||
}
|
||||
infer_eval_relations(tree, relations_by_node, *new_relations);
|
||||
}
|
||||
|
||||
const bool group_interface_changed = !tree.runtime->anonymous_attribute_relations ||
|
||||
*tree.runtime->anonymous_attribute_relations !=
|
||||
*new_relations;
|
||||
tree.runtime->anonymous_attribute_relations = std::move(new_relations);
|
||||
|
||||
return group_interface_changed;
|
||||
}
|
||||
|
||||
} // namespace blender::bke::anonymous_attribute_inferencing
|
|
@ -473,6 +473,9 @@ class NodeTreeMainUpdater {
|
|||
if (node_field_inferencing::update_field_inferencing(ntree)) {
|
||||
result.interface_changed = true;
|
||||
}
|
||||
if (anonymous_attribute_inferencing::update_anonymous_attribute_relations(ntree)) {
|
||||
result.interface_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
result.output_changed = this->check_if_output_changed(ntree);
|
||||
|
|
|
@ -73,9 +73,11 @@ typedef struct BVHTreeRayHit {
|
|||
} BVHTreeRayHit;
|
||||
|
||||
enum {
|
||||
/* Use a priority queue to process nodes in the optimal order (for slow callbacks) */
|
||||
BVH_OVERLAP_USE_THREADING = (1 << 0),
|
||||
BVH_OVERLAP_RETURN_PAIRS = (1 << 1),
|
||||
/* Use a specialized self-overlap traversal to only test and output every
|
||||
* pair once, rather than twice in different order as usual. */
|
||||
BVH_OVERLAP_SELF = (1 << 2),
|
||||
};
|
||||
enum {
|
||||
/* Use a priority queue to process nodes in the optimal order (for slow callbacks) */
|
||||
|
@ -163,6 +165,9 @@ bool BLI_bvhtree_update_node(
|
|||
BVHTree *tree, int index, const float co[3], const float co_moving[3], int numpoints);
|
||||
/**
|
||||
* Call #BLI_bvhtree_update_node() first for every node/point/triangle.
|
||||
*
|
||||
* Note that this does not rebalance the tree, so if the shape of the mesh changes
|
||||
* too much, operations on the tree may become suboptimal.
|
||||
*/
|
||||
void BLI_bvhtree_update_tree(BVHTree *tree);
|
||||
|
||||
|
@ -191,6 +196,12 @@ BVHTreeOverlap *BLI_bvhtree_overlap(const BVHTree *tree1,
|
|||
unsigned int *r_overlap_num,
|
||||
BVHTree_OverlapCallback callback,
|
||||
void *userdata);
|
||||
/** Compute overlaps of the tree with itself. This is faster than BLI_bvhtree_overlap
|
||||
* because it only tests and returns each symmetrical pair once. */
|
||||
BVHTreeOverlap *BLI_bvhtree_overlap_self(const BVHTree *tree,
|
||||
unsigned int *r_overlap_num,
|
||||
BVHTree_OverlapCallback callback,
|
||||
void *userdata);
|
||||
|
||||
int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_num);
|
||||
|
||||
|
|
|
@ -1164,8 +1164,8 @@ void box_minmax_bounds_m4(float min[3], float max[3], float boundbox[2][3], floa
|
|||
/** \name Mapping
|
||||
* \{ */
|
||||
|
||||
void map_to_tube(float *r_u, float *r_v, float x, float y, float z);
|
||||
void map_to_sphere(float *r_u, float *r_v, float x, float y, float z);
|
||||
bool map_to_tube(float *r_u, float *r_v, float x, float y, float z);
|
||||
bool map_to_sphere(float *r_u, float *r_v, float x, float y, float z);
|
||||
void map_to_plane_v2_v3v3(float r_co[2], const float co[3], const float no[3]);
|
||||
void map_to_plane_axis_angle_v2_v3v3fl(float r_co[2],
|
||||
const float co[3],
|
||||
|
|
|
@ -95,6 +95,7 @@ BLI_STATIC_ASSERT((sizeof(void *) == 8 && sizeof(BVHTree) <= 48) ||
|
|||
typedef struct BVHOverlapData_Shared {
|
||||
const BVHTree *tree1, *tree2;
|
||||
axis_t start_axis, stop_axis;
|
||||
bool use_self;
|
||||
|
||||
/* use for callbacks */
|
||||
BVHTree_OverlapCallback callback;
|
||||
|
@ -1231,6 +1232,62 @@ static bool tree_overlap_traverse_num(BVHOverlapData_Thread *data_thread,
|
|||
return false;
|
||||
}
|
||||
|
||||
/** Calls the appropriate recursive overlap traversal function. */
|
||||
static void tree_overlap_invoke_traverse(BVHOverlapData_Thread *data,
|
||||
const BVHNode *node1,
|
||||
const BVHNode *node2)
|
||||
{
|
||||
if (data->max_interactions) {
|
||||
tree_overlap_traverse_num(data, node1, node2);
|
||||
}
|
||||
else if (data->shared->callback) {
|
||||
tree_overlap_traverse_cb(data, node1, node2);
|
||||
}
|
||||
else {
|
||||
tree_overlap_traverse(data, node1, node2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Self-overlap traversal with callback. */
|
||||
static void tree_overlap_traverse_self_cb(BVHOverlapData_Thread *data_thread, const BVHNode *node)
|
||||
{
|
||||
for (int i = 0; i < node->node_num; i++) {
|
||||
/* Recursively compute self-overlap within each child. */
|
||||
tree_overlap_traverse_self_cb(data_thread, node->children[i]);
|
||||
|
||||
/* Compute overlap of pairs of children, testing each one only once (assume symmetry). */
|
||||
for (int j = i + 1; j < node->node_num; j++) {
|
||||
tree_overlap_traverse_cb(data_thread, node->children[i], node->children[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Self-overlap traversal without callback. */
|
||||
static void tree_overlap_traverse_self(BVHOverlapData_Thread *data_thread, const BVHNode *node)
|
||||
{
|
||||
for (int i = 0; i < node->node_num; i++) {
|
||||
/* Recursively compute self-overlap within each child. */
|
||||
tree_overlap_traverse_self(data_thread, node->children[i]);
|
||||
|
||||
/* Compute overlap of pairs of children, testing each one only once (assume symmetry). */
|
||||
for (int j = i + 1; j < node->node_num; j++) {
|
||||
tree_overlap_traverse(data_thread, node->children[i], node->children[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls the appropriate recursive self-overlap traversal. */
|
||||
static void tree_overlap_invoke_traverse_self(BVHOverlapData_Thread *data_thread,
|
||||
const BVHNode *node)
|
||||
{
|
||||
if (data_thread->shared->callback) {
|
||||
tree_overlap_traverse_self_cb(data_thread, node);
|
||||
}
|
||||
else {
|
||||
tree_overlap_traverse_self(data_thread, node);
|
||||
}
|
||||
}
|
||||
|
||||
int BLI_bvhtree_overlap_thread_num(const BVHTree *tree)
|
||||
{
|
||||
return (int)MIN2(tree->tree_type, tree->nodes[tree->leaf_num]->node_num);
|
||||
|
@ -1243,20 +1300,20 @@ static void bvhtree_overlap_task_cb(void *__restrict userdata,
|
|||
BVHOverlapData_Thread *data = &((BVHOverlapData_Thread *)userdata)[j];
|
||||
BVHOverlapData_Shared *data_shared = data->shared;
|
||||
|
||||
if (data->max_interactions) {
|
||||
tree_overlap_traverse_num(data,
|
||||
data_shared->tree1->nodes[data_shared->tree1->leaf_num]->children[j],
|
||||
data_shared->tree2->nodes[data_shared->tree2->leaf_num]);
|
||||
}
|
||||
else if (data_shared->callback) {
|
||||
tree_overlap_traverse_cb(data,
|
||||
data_shared->tree1->nodes[data_shared->tree1->leaf_num]->children[j],
|
||||
data_shared->tree2->nodes[data_shared->tree2->leaf_num]);
|
||||
const BVHNode *root1 = data_shared->tree1->nodes[data_shared->tree1->leaf_num];
|
||||
|
||||
if (data_shared->use_self) {
|
||||
/* This code matches one outer loop iteration within traverse_self. */
|
||||
tree_overlap_invoke_traverse_self(data, root1->children[j]);
|
||||
|
||||
for (int k = j + 1; k < root1->node_num; k++) {
|
||||
tree_overlap_invoke_traverse(data, root1->children[j], root1->children[k]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tree_overlap_traverse(data,
|
||||
data_shared->tree1->nodes[data_shared->tree1->leaf_num]->children[j],
|
||||
data_shared->tree2->nodes[data_shared->tree2->leaf_num]);
|
||||
const BVHNode *root2 = data_shared->tree2->nodes[data_shared->tree2->leaf_num];
|
||||
|
||||
tree_overlap_invoke_traverse(data, root1->children[j], root2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1273,9 +1330,12 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
|
|||
bool overlap_pairs = (flag & BVH_OVERLAP_RETURN_PAIRS) != 0;
|
||||
bool use_threading = (flag & BVH_OVERLAP_USE_THREADING) != 0 &&
|
||||
(tree1->leaf_num > KDOPBVH_THREAD_LEAF_THRESHOLD);
|
||||
bool use_self = (flag & BVH_OVERLAP_SELF) != 0;
|
||||
|
||||
/* 'RETURN_PAIRS' was not implemented without 'max_interactions'. */
|
||||
BLI_assert(overlap_pairs || max_interactions);
|
||||
/* Self-overlap does not support max interactions (it's not symmetrical). */
|
||||
BLI_assert(!use_self || (tree1 == tree2 && !max_interactions));
|
||||
|
||||
const int root_node_len = BLI_bvhtree_overlap_thread_num(tree1);
|
||||
const int thread_num = use_threading ? root_node_len : 1;
|
||||
|
@ -1293,6 +1353,10 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (UNLIKELY(use_self && tree1 != tree2)) {
|
||||
use_self = false;
|
||||
}
|
||||
|
||||
const BVHNode *root1 = tree1->nodes[tree1->leaf_num];
|
||||
const BVHNode *root2 = tree2->nodes[tree2->leaf_num];
|
||||
|
||||
|
@ -1308,6 +1372,7 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
|
|||
data_shared.tree2 = tree2;
|
||||
data_shared.start_axis = start_axis;
|
||||
data_shared.stop_axis = stop_axis;
|
||||
data_shared.use_self = use_self;
|
||||
|
||||
/* can be NULL */
|
||||
data_shared.callback = callback;
|
||||
|
@ -1317,7 +1382,7 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
|
|||
/* init BVHOverlapData_Thread */
|
||||
data[j].shared = &data_shared;
|
||||
data[j].overlap = overlap_pairs ? BLI_stack_new(sizeof(BVHTreeOverlap), __func__) : NULL;
|
||||
data[j].max_interactions = max_interactions;
|
||||
data[j].max_interactions = use_self ? 0 : max_interactions;
|
||||
|
||||
/* for callback */
|
||||
data[j].thread = j;
|
||||
|
@ -1329,16 +1394,11 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
|
|||
settings.min_iter_per_thread = 1;
|
||||
BLI_task_parallel_range(0, root_node_len, data, bvhtree_overlap_task_cb, &settings);
|
||||
}
|
||||
else if (use_self) {
|
||||
tree_overlap_invoke_traverse_self(data, root1);
|
||||
}
|
||||
else {
|
||||
if (max_interactions) {
|
||||
tree_overlap_traverse_num(data, root1, root2);
|
||||
}
|
||||
else if (callback) {
|
||||
tree_overlap_traverse_cb(data, root1, root2);
|
||||
}
|
||||
else {
|
||||
tree_overlap_traverse(data, root1, root2);
|
||||
}
|
||||
tree_overlap_invoke_traverse(data, root1, root2);
|
||||
}
|
||||
|
||||
if (overlap_pairs) {
|
||||
|
@ -1377,6 +1437,23 @@ BVHTreeOverlap *BLI_bvhtree_overlap(
|
|||
BVH_OVERLAP_USE_THREADING | BVH_OVERLAP_RETURN_PAIRS);
|
||||
}
|
||||
|
||||
BVHTreeOverlap *BLI_bvhtree_overlap_self(
|
||||
const BVHTree *tree,
|
||||
uint *r_overlap_num,
|
||||
/* optional callback to test the overlap before adding (must be thread-safe!) */
|
||||
BVHTree_OverlapCallback callback,
|
||||
void *userdata)
|
||||
{
|
||||
return BLI_bvhtree_overlap_ex(tree,
|
||||
tree,
|
||||
r_overlap_num,
|
||||
callback,
|
||||
userdata,
|
||||
0,
|
||||
BVH_OVERLAP_USE_THREADING | BVH_OVERLAP_RETURN_PAIRS |
|
||||
BVH_OVERLAP_SELF);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -4914,39 +4914,59 @@ void box_minmax_bounds_m4(float min[3], float max[3], float boundbox[2][3], floa
|
|||
|
||||
/********************************** Mapping **********************************/
|
||||
|
||||
void map_to_tube(float *r_u, float *r_v, const float x, const float y, const float z)
|
||||
static float snap_coordinate(float u)
|
||||
{
|
||||
float len;
|
||||
|
||||
*r_v = (z + 1.0f) / 2.0f;
|
||||
|
||||
len = sqrtf(x * x + y * y);
|
||||
if (len > 0.0f) {
|
||||
*r_u = (1.0f - (atan2f(x / len, y / len) / (float)M_PI)) / 2.0f;
|
||||
/* Adjust a coordinate value `u` to obtain a value inside the (closed) unit interval.
|
||||
* i.e. 0.0 <= snap_coordinate(u) <= 1.0.
|
||||
* Due to round-off errors, it is possible that the value of `u` may be close to the boundary of
|
||||
* the unit interval, but not exactly on it. In order to handle these cases, `snap_coordinate`
|
||||
* checks whether `u` is within `epsilon` of the boundary, and if so, it snaps the return value
|
||||
* to the boundary. */
|
||||
if (u < 0.0f) {
|
||||
u += 1.0f; /* Get back into the unit interval. */
|
||||
}
|
||||
else {
|
||||
*r_v = *r_u = 0.0f; /* to avoid un-initialized variables */
|
||||
BLI_assert(0.0f <= u);
|
||||
BLI_assert(u <= 1.0f);
|
||||
const float epsilon = 0.25f / 65536.0f; /* i.e. Quarter of a texel on a 65536 x 65536 texture. */
|
||||
if (u < epsilon) {
|
||||
return 0.0f; /* `u` is close to 0, just return 0. */
|
||||
}
|
||||
if (1.0f - epsilon < u) {
|
||||
return 1.0f; /* `u` is close to 1, just return 1. */
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
void map_to_sphere(float *r_u, float *r_v, const float x, const float y, const float z)
|
||||
bool map_to_tube(float *r_u, float *r_v, const float x, const float y, const float z)
|
||||
{
|
||||
float len;
|
||||
|
||||
len = sqrtf(x * x + y * y + z * z);
|
||||
if (len > 0.0f) {
|
||||
if (UNLIKELY(x == 0.0f && y == 0.0f)) {
|
||||
*r_u = 0.0f; /* Otherwise domain error. */
|
||||
}
|
||||
else {
|
||||
*r_u = (1.0f - atan2f(x, y) / (float)M_PI) / 2.0f;
|
||||
}
|
||||
|
||||
*r_v = 1.0f - saacos(z / len) / (float)M_PI;
|
||||
bool regular = true;
|
||||
if (x * x + y * y < 1e-6f * 1e-6f) {
|
||||
regular = false; /* We're too close to the cylinder's axis. */
|
||||
*r_u = 0.5f;
|
||||
}
|
||||
else {
|
||||
*r_v = *r_u = 0.0f; /* to avoid un-initialized variables */
|
||||
/* The "Regular" case, just compute the coordinate. */
|
||||
*r_u = snap_coordinate(atan2f(x, -y) / (float)(2.0f * M_PI));
|
||||
}
|
||||
*r_v = (z + 1.0f) / 2.0f;
|
||||
return regular;
|
||||
}
|
||||
|
||||
bool map_to_sphere(float *r_u, float *r_v, const float x, const float y, const float z)
|
||||
{
|
||||
bool regular = true;
|
||||
const float epsilon = 0.25f / 65536.0f; /* i.e. Quarter of a texel on a 65536 x 65536 texture. */
|
||||
const float len_xy = sqrtf(x * x + y * y);
|
||||
if (len_xy <= fabsf(z) * epsilon) {
|
||||
regular = false; /* We're on the line that runs through the north and south poles. */
|
||||
*r_u = 0.5f;
|
||||
}
|
||||
else {
|
||||
/* The "Regular" case, just compute the coordinate. */
|
||||
*r_u = snap_coordinate(atan2f(x, -y) / (float)(2.0f * M_PI));
|
||||
}
|
||||
*r_v = snap_coordinate(atan2f(len_xy, -z) / (float)M_PI);
|
||||
return regular;
|
||||
}
|
||||
|
||||
void map_to_plane_v2_v3v3(float r_co[2], const float co[3], const float no[3])
|
||||
|
|
|
@ -1604,7 +1604,7 @@ static void blo_cache_storage_entry_register(
|
|||
|
||||
/** Restore a cache data entry from old ID into new one, when reading some undo memfile. */
|
||||
static void blo_cache_storage_entry_restore_in_new(
|
||||
ID * /*id*/, const IDCacheKey *key, void **cache_p, uint flags, void *cache_storage_v)
|
||||
ID *id, const IDCacheKey *key, void **cache_p, uint flags, void *cache_storage_v)
|
||||
{
|
||||
BLOCacheStorage *cache_storage = static_cast<BLOCacheStorage *>(cache_storage_v);
|
||||
|
||||
|
@ -1618,6 +1618,15 @@ static void blo_cache_storage_entry_restore_in_new(
|
|||
return;
|
||||
}
|
||||
|
||||
/* Assume that when ID source is tagged as changed, its caches need to be cleared.
|
||||
* NOTE: This is mainly a work-around for some IDs, like Image, which use a non-depsgraph-handled
|
||||
* process for part of their updates.
|
||||
*/
|
||||
if (id->recalc & ID_RECALC_SOURCE) {
|
||||
*cache_p = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
BLOCacheStorageValue *storage_value = static_cast<BLOCacheStorageValue *>(
|
||||
BLI_ghash_lookup(cache_storage->cache_map, key));
|
||||
if (storage_value == nullptr) {
|
||||
|
|
|
@ -116,15 +116,16 @@ set(GLSL_SRC
|
|||
shaders/compositor_normalize.glsl
|
||||
shaders/compositor_parallel_reduction.glsl
|
||||
shaders/compositor_projector_lens_distortion.glsl
|
||||
shaders/compositor_read_pass.glsl
|
||||
shaders/compositor_realize_on_domain.glsl
|
||||
shaders/compositor_screen_lens_distortion.glsl
|
||||
shaders/compositor_set_alpha.glsl
|
||||
shaders/compositor_split_viewer.glsl
|
||||
shaders/compositor_symmetric_blur.glsl
|
||||
shaders/compositor_symmetric_blur_variable_size.glsl
|
||||
shaders/compositor_symmetric_separable_blur.glsl
|
||||
shaders/compositor_tone_map_photoreceptor.glsl
|
||||
shaders/compositor_tone_map_simple.glsl
|
||||
shaders/compositor_write_output.glsl
|
||||
|
||||
shaders/library/gpu_shader_compositor_alpha_over.glsl
|
||||
shaders/library/gpu_shader_compositor_blur_common.glsl
|
||||
|
@ -204,15 +205,16 @@ set(SRC_SHADER_CREATE_INFOS
|
|||
shaders/infos/compositor_normalize_info.hh
|
||||
shaders/infos/compositor_parallel_reduction_info.hh
|
||||
shaders/infos/compositor_projector_lens_distortion_info.hh
|
||||
shaders/infos/compositor_read_pass_info.hh
|
||||
shaders/infos/compositor_realize_on_domain_info.hh
|
||||
shaders/infos/compositor_screen_lens_distortion_info.hh
|
||||
shaders/infos/compositor_set_alpha_info.hh
|
||||
shaders/infos/compositor_split_viewer_info.hh
|
||||
shaders/infos/compositor_symmetric_blur_info.hh
|
||||
shaders/infos/compositor_symmetric_blur_variable_size_info.hh
|
||||
shaders/infos/compositor_symmetric_separable_blur_info.hh
|
||||
shaders/infos/compositor_tone_map_photoreceptor_info.hh
|
||||
shaders/infos/compositor_tone_map_simple_info.hh
|
||||
shaders/infos/compositor_write_output_info.hh
|
||||
)
|
||||
|
||||
set(SHADER_CREATE_INFOS_CONTENT "")
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
|
@ -41,8 +42,17 @@ class Context {
|
|||
/* Get the active compositing scene. */
|
||||
virtual const Scene *get_scene() const = 0;
|
||||
|
||||
/* Get the dimensions of the output. */
|
||||
virtual int2 get_output_size() = 0;
|
||||
/* Get the width and height of the render passes and of the output texture returned by the
|
||||
* get_input_texture and get_output_texture methods respectively. */
|
||||
virtual int2 get_render_size() const = 0;
|
||||
|
||||
/* Get the rectangular region representing the area of the input that the compositor will operate
|
||||
* on. Conversely, the compositor will only update the region of the output that corresponds to
|
||||
* the compositing region. In the base case, the compositing region covers the entirety of the
|
||||
* render region with a lower bound of zero and an upper bound of the render size returned by the
|
||||
* get_render_size method. In other cases, the compositing region might be a subset of the render
|
||||
* region. */
|
||||
virtual rcti get_compositing_region() const = 0;
|
||||
|
||||
/* Get the texture representing the output where the result of the compositor should be
|
||||
* written. This should be called by output nodes to get their target texture. */
|
||||
|
@ -60,6 +70,9 @@ class Context {
|
|||
* appropriate place, which can be directly in the UI or just logged to the output stream. */
|
||||
virtual void set_info_message(StringRef message) const = 0;
|
||||
|
||||
/* Get the size of the compositing region. See get_compositing_region(). */
|
||||
int2 get_compositing_region_size() const;
|
||||
|
||||
/* Get the current frame number of the active scene. */
|
||||
int get_frame_number() const;
|
||||
|
||||
|
|
|
@ -143,9 +143,9 @@ class Domain {
|
|||
|
||||
public:
|
||||
/* A size only constructor that sets the transformation to identity. */
|
||||
Domain(int2 size);
|
||||
Domain(const int2 &size);
|
||||
|
||||
Domain(int2 size, float3x3 transformation);
|
||||
Domain(const int2 &size, const float3x3 &transformation);
|
||||
|
||||
/* Transform the domain by the given transformation. This effectively pre-multiply the given
|
||||
* transformation by the current transformation of the domain. */
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_static_cache_manager.hh"
|
||||
#include "COM_static_shader_manager.hh"
|
||||
|
@ -11,6 +15,12 @@ Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool)
|
|||
{
|
||||
}
|
||||
|
||||
int2 Context::get_compositing_region_size() const
|
||||
{
|
||||
const rcti compositing_region = get_compositing_region();
|
||||
return int2(BLI_rcti_size_x(&compositing_region), BLI_rcti_size_y(&compositing_region));
|
||||
}
|
||||
|
||||
int Context::get_frame_number() const
|
||||
{
|
||||
return get_scene()->r.cfra;
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
Domain::Domain(int2 size) : size(size), transformation(float3x3::identity())
|
||||
Domain::Domain(const int2 &size) : size(size), transformation(float3x3::identity())
|
||||
{
|
||||
}
|
||||
|
||||
Domain::Domain(int2 size, float3x3 transformation) : size(size), transformation(transformation)
|
||||
Domain::Domain(const int2 &size, const float3x3 &transformation)
|
||||
: size(size), transformation(transformation)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec4 pass_color = texture_load(input_tx, texel + compositing_region_lower_bound);
|
||||
imageStore(output_img, texel, READ_EXPRESSION(pass_color));
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x);
|
||||
imageStore(output_img, texel, color);
|
||||
}
|
|
@ -10,5 +10,5 @@ void main()
|
|||
#endif
|
||||
vec4 color = condition ? texture_load(first_image_tx, texel) :
|
||||
texture_load(second_image_tx, texel);
|
||||
imageStore(output_img, texel, color);
|
||||
imageStore(output_img, texel + compositing_region_lower_bound, color);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec4 input_color = texture_load(input_tx, texel);
|
||||
|
||||
#if defined(DIRECT_OUTPUT)
|
||||
vec4 output_color = input_color;
|
||||
#elif defined(OPAQUE_OUTPUT)
|
||||
vec4 output_color = vec4(input_color.rgb, 1.0);
|
||||
#elif defined(ALPHA_OUTPUT)
|
||||
float alpha = texture_load(alpha_tx, texel).x;
|
||||
vec4 output_color = vec4(input_color.rgb, alpha);
|
||||
#endif
|
||||
|
||||
imageStore(output_img, texel + compositing_region_lower_bound, output_color);
|
||||
}
|
|
@ -61,9 +61,3 @@ GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float)
|
|||
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque)
|
||||
.additional_info("compositor_convert_shared")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)")
|
||||
.do_static_compilation(true);
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_read_pass_shared)
|
||||
.local_group_size(16, 16)
|
||||
.push_constant(Type::IVEC2, "compositing_region_lower_bound")
|
||||
.sampler(0, ImageType::FLOAT_2D, "input_tx")
|
||||
.compute_source("compositor_read_pass.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_read_pass)
|
||||
.additional_info("compositor_read_pass_shared")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.define("READ_EXPRESSION(pass_color)", "pass_color")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_read_pass_alpha)
|
||||
.additional_info("compositor_read_pass_shared")
|
||||
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.define("READ_EXPRESSION(pass_color)", "vec4(pass_color.a, vec3(0.0))")
|
||||
.do_static_compilation(true);
|
|
@ -1,11 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_set_alpha)
|
||||
.local_group_size(16, 16)
|
||||
.sampler(0, ImageType::FLOAT_2D, "image_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D, "alpha_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.compute_source("compositor_set_alpha.glsl")
|
||||
.do_static_compilation(true);
|
|
@ -6,6 +6,7 @@ GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared)
|
|||
.local_group_size(16, 16)
|
||||
.push_constant(Type::FLOAT, "split_ratio")
|
||||
.push_constant(Type::IVEC2, "view_size")
|
||||
.push_constant(Type::IVEC2, "compositing_region_lower_bound")
|
||||
.sampler(0, ImageType::FLOAT_2D, "first_image_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D, "second_image_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_write_output_shared)
|
||||
.local_group_size(16, 16)
|
||||
.push_constant(Type::IVEC2, "compositing_region_lower_bound")
|
||||
.sampler(0, ImageType::FLOAT_2D, "input_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
|
||||
.compute_source("compositor_write_output.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_write_output)
|
||||
.additional_info("compositor_write_output_shared")
|
||||
.define("DIRECT_OUTPUT")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_write_output_opaque)
|
||||
.additional_info("compositor_write_output_shared")
|
||||
.define("OPAQUE_OUTPUT")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_write_output_alpha)
|
||||
.additional_info("compositor_write_output_shared")
|
||||
.sampler(1, ImageType::FLOAT_2D, "alpha_tx")
|
||||
.define("ALPHA_OUTPUT")
|
||||
.do_static_compilation(true);
|
|
@ -2,16 +2,23 @@
|
|||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "IMB_colormanagement.h"
|
||||
|
@ -50,11 +57,68 @@ class Context : public realtime_compositor::Context {
|
|||
return DRW_context_state_get()->scene;
|
||||
}
|
||||
|
||||
int2 get_output_size() override
|
||||
int2 get_render_size() const override
|
||||
{
|
||||
return int2(float2(DRW_viewport_size_get()));
|
||||
}
|
||||
|
||||
/* Returns true if the viewport is in camera view and has an opaque passepartout, that is, the
|
||||
* area outside of the camera border is not visible. */
|
||||
bool is_opaque_camera_view() const
|
||||
{
|
||||
/* Check if the viewport is in camera view. */
|
||||
if (DRW_context_state_get()->rv3d->persp != RV3D_CAMOB) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the camera object that is currently in view is an actual camera. It is possible for
|
||||
* a non camera object to be used as a camera, in which case, there will be no passepartout or
|
||||
* any other camera setting, so those pseudo cameras can be ignored. */
|
||||
Object *camera_object = DRW_context_state_get()->v3d->camera;
|
||||
if (camera_object->type != OB_CAMERA) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the camera has passepartout active and is totally opaque. */
|
||||
Camera *cam = static_cast<Camera *>(camera_object->data);
|
||||
if (!(cam->flag & CAM_SHOWPASSEPARTOUT) || cam->passepartalpha != 1.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rcti get_compositing_region() const override
|
||||
{
|
||||
const int2 viewport_size = int2(float2(DRW_viewport_size_get()));
|
||||
const rcti render_region = rcti{0, viewport_size.x, 0, viewport_size.y};
|
||||
|
||||
/* If the camera view is not opaque, that means the content outside of the camera region is
|
||||
* visible to some extent, so it would make sense to include them in the compositing region.
|
||||
* Otherwise, we limit the compositing region to the visible camera region because anything
|
||||
* outside of the camera region will not be visible anyways. */
|
||||
if (!is_opaque_camera_view()) {
|
||||
return render_region;
|
||||
}
|
||||
|
||||
rctf camera_border;
|
||||
ED_view3d_calc_camera_border(DRW_context_state_get()->scene,
|
||||
DRW_context_state_get()->depsgraph,
|
||||
DRW_context_state_get()->region,
|
||||
DRW_context_state_get()->v3d,
|
||||
DRW_context_state_get()->rv3d,
|
||||
&camera_border,
|
||||
false);
|
||||
|
||||
rcti camera_region;
|
||||
BLI_rcti_rctf_copy_floor(&camera_region, &camera_border);
|
||||
|
||||
rcti visible_camera_region;
|
||||
BLI_rcti_isect(&render_region, &camera_region, &visible_camera_region);
|
||||
|
||||
return visible_camera_region;
|
||||
}
|
||||
|
||||
GPUTexture *get_output_texture() override
|
||||
{
|
||||
return DRW_viewport_texture_list_get()->color;
|
||||
|
@ -83,36 +147,36 @@ class Engine {
|
|||
TexturePool texture_pool_;
|
||||
Context context_;
|
||||
realtime_compositor::Evaluator evaluator_;
|
||||
/* Stores the viewport size at the time the last compositor evaluation happened. See the
|
||||
* update_viewport_size method for more information. */
|
||||
int2 last_viewport_size_;
|
||||
/* Stores the compositing region size at the time the last compositor evaluation happened. See
|
||||
* the update_compositing_region_size method for more information. */
|
||||
int2 last_compositing_region_size_;
|
||||
|
||||
public:
|
||||
Engine(char *info_message)
|
||||
: context_(texture_pool_, info_message),
|
||||
evaluator_(context_),
|
||||
last_viewport_size_(context_.get_output_size())
|
||||
last_compositing_region_size_(context_.get_compositing_region_size())
|
||||
{
|
||||
}
|
||||
|
||||
/* Update the viewport size and evaluate the compositor. */
|
||||
/* Update the compositing region size and evaluate the compositor. */
|
||||
void draw()
|
||||
{
|
||||
update_viewport_size();
|
||||
update_compositing_region_size();
|
||||
evaluator_.evaluate();
|
||||
}
|
||||
|
||||
/* If the size of the viewport changed from the last time the compositor was evaluated, update
|
||||
* the viewport size and reset the evaluator. That's because the evaluator compiles the node tree
|
||||
* in a manner that is specifically optimized for the size of the viewport. This should be called
|
||||
* before evaluating the compositor. */
|
||||
void update_viewport_size()
|
||||
/* If the size of the compositing region changed from the last time the compositor was evaluated,
|
||||
* update the last compositor region size and reset the evaluator. That's because the evaluator
|
||||
* compiles the node tree in a manner that is specifically optimized for the size of the
|
||||
* compositing region. This should be called before evaluating the compositor. */
|
||||
void update_compositing_region_size()
|
||||
{
|
||||
if (last_viewport_size_ == context_.get_output_size()) {
|
||||
if (last_compositing_region_size_ == context_.get_compositing_region_size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_viewport_size_ = context_.get_output_size();
|
||||
last_compositing_region_size_ = context_.get_compositing_region_size();
|
||||
|
||||
evaluator_.reset();
|
||||
}
|
||||
|
|
|
@ -1244,10 +1244,6 @@ static void drw_engines_enable_editors(void)
|
|||
|
||||
static bool is_compositor_enabled(void)
|
||||
{
|
||||
if (!U.experimental.use_realtime_compositor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DST.draw_ctx.v3d->shading.use_compositor == V3D_SHADING_USE_COMPOSITOR_DISABLED) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -347,7 +347,7 @@ static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
|
|||
data.mlooptri = mr->mlooptri;
|
||||
data.epsilon = BLI_bvhtree_get_epsilon(tree);
|
||||
|
||||
BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data);
|
||||
BVHTreeOverlap *overlap = BLI_bvhtree_overlap_self(tree, &overlap_len, bvh_overlap_cb, &data);
|
||||
if (overlap) {
|
||||
for (int i = 0; i < overlap_len; i++) {
|
||||
const MPoly *f_hit_pair[2] = {
|
||||
|
|
|
@ -2845,12 +2845,15 @@ static bool rename_anim_channels(bAnimContext *ac, int channel_index)
|
|||
int filter;
|
||||
bool success = false;
|
||||
|
||||
/* get the channel that was clicked on */
|
||||
/* filter channels */
|
||||
/* Filter relevant channels (note that grease-pencil/annotations are not displayed in Graph
|
||||
* Editor). */
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
|
||||
if (ELEM(ac->datatype, ANIMCONT_FCURVES, ANIMCONT_NLA)) {
|
||||
filter |= ANIMFILTER_FCURVESONLY;
|
||||
}
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
|
||||
|
||||
/* get channel from index */
|
||||
/* Get channel that was clicked on from index. */
|
||||
ale = BLI_findlink(&anim_data, channel_index);
|
||||
if (ale == NULL) {
|
||||
/* channel not found */
|
||||
|
|
|
@ -377,6 +377,40 @@ void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor
|
|||
|
||||
/* ---------------- */
|
||||
|
||||
void ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
|
||||
{
|
||||
const BezTriple left_key = fcurve_segment_start_get(fcu, segment->start_index);
|
||||
const float left_x = left_key.vec[1][0];
|
||||
const float left_y = left_key.vec[1][1];
|
||||
|
||||
const BezTriple right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
|
||||
|
||||
const float key_x_range = right_key.vec[1][0] - left_x;
|
||||
const float key_y_range = right_key.vec[1][1] - left_y;
|
||||
|
||||
/* In order to have a curve that favors the right key, the curve needs to be mirrored in x and y.
|
||||
* Having an exponent that is a fraction of 1 would produce a similar but inferior result. */
|
||||
const bool inverted = factor > 0.5;
|
||||
const float exponent = 1 + fabs(factor * 2 - 1) * 4;
|
||||
|
||||
for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
|
||||
/* For easy calculation of the curve, the values are normalized. */
|
||||
const float normalized_x = (fcu->bezt[i].vec[1][0] - left_x) / key_x_range;
|
||||
|
||||
float normalized_y = 0;
|
||||
if (inverted) {
|
||||
normalized_y = 1 - pow(1 - normalized_x, exponent);
|
||||
}
|
||||
else {
|
||||
normalized_y = pow(normalized_x, exponent);
|
||||
}
|
||||
|
||||
fcu->bezt[i].vec[1][1] = left_y + normalized_y * key_y_range;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- */
|
||||
|
||||
void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
|
||||
{
|
||||
BezTriple left_bezt = fcurve_segment_start_get(fcu, segment->start_index);
|
||||
|
|
|
@ -66,9 +66,11 @@ typedef struct PoseBlendData {
|
|||
/* For temp-loading the Action from the pose library. */
|
||||
AssetTempIDConsumer *temp_id_consumer;
|
||||
|
||||
/* Blend factor, interval [-1, 1] for interpolating between current and given pose.
|
||||
* Positive factors will blend in `act`, whereas negative factors will blend in `act_flipped`. */
|
||||
/* Blend factor for interpolating between current and given pose.
|
||||
* 1.0 means "100% pose asset". Negative values and values > 1.0 will be used as-is, and can
|
||||
* cause interesting effects. */
|
||||
float blend_factor;
|
||||
bool is_flipped;
|
||||
struct PoseBackup *pose_backup;
|
||||
|
||||
Object *ob; /* Object to work on. */
|
||||
|
@ -85,11 +87,11 @@ typedef struct PoseBlendData {
|
|||
} PoseBlendData;
|
||||
|
||||
/** Return the bAction that should be blended.
|
||||
* This is either pbd->act or pbd->act_flipped, depending on the sign of the blend factor.
|
||||
* This is either pbd->act or pbd->act_flipped, depending on is_flipped.
|
||||
*/
|
||||
static bAction *poselib_action_to_blend(PoseBlendData *pbd)
|
||||
{
|
||||
return (pbd->blend_factor >= 0) ? pbd->act : pbd->act_flipped;
|
||||
return pbd->is_flipped ? pbd->act_flipped : pbd->act;
|
||||
}
|
||||
|
||||
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
|
||||
|
@ -177,27 +179,32 @@ static void poselib_blend_apply(bContext *C, wmOperator *op)
|
|||
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
|
||||
bAction *to_blend = poselib_action_to_blend(pbd);
|
||||
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, fabs(pbd->blend_factor));
|
||||
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, pbd->blend_factor);
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
|
||||
static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
|
||||
{
|
||||
const bool sign_changed = signf(new_factor) != signf(pbd->blend_factor);
|
||||
if (sign_changed) {
|
||||
/* The zero point was crossed, meaning that the pose will be flipped. This means the pose
|
||||
* backup has to change, as it only contains the bones for one side. */
|
||||
BKE_pose_backup_restore(pbd->pose_backup);
|
||||
BKE_pose_backup_free(pbd->pose_backup);
|
||||
pbd->blend_factor = new_factor;
|
||||
pbd->needs_redraw = true;
|
||||
}
|
||||
|
||||
static void poselib_set_flipped(PoseBlendData *pbd, const bool new_flipped)
|
||||
{
|
||||
if (pbd->is_flipped == new_flipped) {
|
||||
return;
|
||||
}
|
||||
|
||||
pbd->blend_factor = CLAMPIS(new_factor, -1.0f, 1.0f);
|
||||
/* The pose will toggle between flipped and normal. This means the pose
|
||||
* backup has to change, as it only contains the bones for one side. */
|
||||
BKE_pose_backup_restore(pbd->pose_backup);
|
||||
BKE_pose_backup_free(pbd->pose_backup);
|
||||
|
||||
pbd->is_flipped = new_flipped;
|
||||
pbd->needs_redraw = true;
|
||||
|
||||
if (sign_changed) {
|
||||
poselib_backup_posecopy(pbd);
|
||||
}
|
||||
poselib_backup_posecopy(pbd);
|
||||
}
|
||||
|
||||
/* Return operator return value. */
|
||||
|
@ -220,6 +227,9 @@ static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const
|
|||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Ctrl manages the 'flipped' state. */
|
||||
poselib_set_flipped(pbd, event->modifier & KM_CTRL);
|
||||
|
||||
/* only accept 'press' event, and ignore 'release', so that we don't get double actions */
|
||||
if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
|
@ -318,14 +328,12 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Passing `flipped=True` is the same as flipping the sign of the blend factor. */
|
||||
const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped");
|
||||
const float multiply_factor = apply_flipped ? -1.0f : 1.0f;
|
||||
pbd->blend_factor = multiply_factor * RNA_float_get(op->ptr, "blend_factor");
|
||||
pbd->is_flipped = RNA_boolean_get(op->ptr, "flipped");
|
||||
pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
|
||||
|
||||
/* Only construct the flipped pose if there is a chance it's actually needed. */
|
||||
const bool is_interactive = (event != NULL);
|
||||
if (is_interactive || pbd->blend_factor < 0) {
|
||||
if (is_interactive || pbd->is_flipped) {
|
||||
pbd->act_flipped = flip_pose(C, ob, pbd->act);
|
||||
}
|
||||
|
||||
|
@ -351,7 +359,8 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
|
|||
pbd->slider = ED_slider_create(C);
|
||||
ED_slider_init(pbd->slider, event);
|
||||
ED_slider_factor_set(pbd->slider, pbd->blend_factor);
|
||||
ED_slider_allow_overshoot_set(pbd->slider, false);
|
||||
ED_slider_allow_overshoot_set(pbd->slider, true);
|
||||
ED_slider_allow_increments_set(pbd->slider, false);
|
||||
ED_slider_is_bidirectional_set(pbd->slider, true);
|
||||
}
|
||||
|
||||
|
@ -394,8 +403,8 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op)
|
|||
poselib_keytag_pose(C, scene, pbd);
|
||||
|
||||
/* Ensure the redo panel has the actually-used value, instead of the initial value. */
|
||||
RNA_float_set(op->ptr, "blend_factor", fabs(pbd->blend_factor));
|
||||
RNA_boolean_set(op->ptr, "flipped", pbd->blend_factor < 0);
|
||||
RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor);
|
||||
RNA_boolean_set(op->ptr, "flipped", pbd->is_flipped);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -485,7 +494,11 @@ static int poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event
|
|||
strcpy(tab_string, TIP_("[Tab] - Show blended pose"));
|
||||
}
|
||||
|
||||
BLI_snprintf(status_string, sizeof(status_string), "%s | %s", tab_string, slider_string);
|
||||
BLI_snprintf(status_string,
|
||||
sizeof(status_string),
|
||||
"%s | %s | [Ctrl] - Flip Pose",
|
||||
tab_string,
|
||||
slider_string);
|
||||
ED_workspace_status_text(C, status_string);
|
||||
|
||||
poselib_blend_apply(C, op);
|
||||
|
@ -568,20 +581,18 @@ void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
|
|||
RNA_def_float_factor(ot->srna,
|
||||
"blend_factor",
|
||||
1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
"Blend Factor",
|
||||
"Amount that the pose is applied on top of the existing poses. A negative "
|
||||
"value will apply the pose flipped over the X-axis",
|
||||
"value will subtract the pose instead of adding it",
|
||||
-1.0f,
|
||||
1.0f);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna,
|
||||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis. This is the same as "
|
||||
"passing a negative `blend_factor`");
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
|
@ -608,11 +619,11 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
|||
prop = RNA_def_float_factor(ot->srna,
|
||||
"blend_factor",
|
||||
0.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
"Blend Factor",
|
||||
"Amount that the pose is applied on top of the existing poses. A "
|
||||
"negative value will apply the pose flipped over the X-axis",
|
||||
"negative value will subtract the pose instead of adding it",
|
||||
-1.0f,
|
||||
1.0f);
|
||||
/* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
|
||||
|
@ -624,8 +635,7 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
|||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis. This is the "
|
||||
"same as passing a negative `blend_factor`");
|
||||
"When enabled, applies the pose flipped over the X-axis");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue