WIP: Functions: new local allocator for better memory reuse and performance #104630
|
@ -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()
|
||||
|
|
|
@ -402,6 +402,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 +467,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
|
||||
|
||||
|
@ -1108,7 +1111,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" )
|
||||
|
@ -1217,8 +1222,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 +1486,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 +1582,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 +1603,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 +1630,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 +1656,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 +1857,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 +1912,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 +2108,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 +2243,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 +2267,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 +2321,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 +2337,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 +2925,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 +2952,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 +3001,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 +3040,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
|
||||
|
@ -3147,7 +3212,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 +3246,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 +4298,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 +4487,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
|
||||
|
@ -4928,10 +5022,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
|
||||
|
@ -5582,9 +5678,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
|
||||
|
|
|
@ -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,7 @@ 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 +113,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,7 @@ 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,6 +57,14 @@ 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;
|
||||
|
@ -66,13 +74,15 @@ class DeviceInfo {
|
|||
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 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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -444,7 +456,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 +469,12 @@ 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_++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
|
@ -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,28 +128,28 @@ 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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4a581c54af9b92cb670d750951b9382160f10f3e
|
||||
Subproject commit 7084c4ecd97d93459d9d23fd90f81589b09be5df
|
|
@ -1 +1 @@
|
|||
Subproject commit 0b0052bd53ad8249ed07dfb87705c338af698bde
|
||||
Subproject commit a9d4443c244f89399ec4bcc427e05a07950528cc
|
|
@ -1 +1 @@
|
|||
Subproject commit 96143b1a8b037ea3c81f065f557025db9fe1ace3
|
||||
Subproject commit bdcfdd47ec3451822b21d1cff2ea2db751093c9a
|
|
@ -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):
|
||||
|
|
|
@ -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 intrinsicly 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
|
||||
|
|
|
@ -11,6 +11,8 @@ struct Mesh;
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
@ -330,7 +337,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -336,7 +336,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();
|
||||
|
@ -346,7 +346,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)) {
|
||||
|
@ -396,7 +396,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 =
|
||||
|
@ -443,7 +443,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)) {
|
||||
|
@ -462,7 +462,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)) {
|
||||
|
@ -487,7 +487,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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -331,18 +331,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) {
|
||||
|
@ -629,7 +630,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);
|
||||
|
||||
|
@ -716,7 +718,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);
|
||||
|
@ -753,7 +756,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;
|
||||
|
@ -797,10 +801,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) {
|
||||
|
@ -2460,7 +2460,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) {
|
||||
|
@ -2957,9 +2957,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);
|
||||
|
@ -2968,7 +2968,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;
|
||||
}
|
||||
|
@ -3147,20 +3147,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++) {
|
||||
|
|
|
@ -997,9 +997,11 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
|
|||
|
||||
void set_all(Span<float> src) override
|
||||
{
|
||||
for (const int64_t index : src.index_range()) {
|
||||
this->set(index, src[index]);
|
||||
}
|
||||
threading::parallel_for(src.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
this->set(i, src[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void materialize(IndexMask mask, MutableSpan<float> r_span) const override
|
||||
|
@ -1007,14 +1009,16 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
|
|||
if (dverts_ == nullptr) {
|
||||
return r_span.fill_indices(mask, 0.0f);
|
||||
}
|
||||
for (const int64_t index : mask) {
|
||||
if (const MDeformWeight *weight = this->find_weight_at_index(index)) {
|
||||
r_span[index] = weight->weight;
|
||||
threading::parallel_for(mask.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int64_t i : mask.slice(range)) {
|
||||
if (const MDeformWeight *weight = this->find_weight_at_index(i)) {
|
||||
r_span[i] = weight->weight;
|
||||
}
|
||||
else {
|
||||
r_span[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
r_span[index] = 0.0f;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void materialize_to_uninitialized(IndexMask mask, MutableSpan<float> r_span) const override
|
||||
|
@ -1051,7 +1055,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);
|
||||
|
@ -1075,7 +1079,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);
|
||||
|
@ -1096,7 +1100,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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -839,7 +839,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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "BKE_layer.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
#include "BKE_unit.h"
|
||||
|
||||
|
@ -64,6 +65,7 @@
|
|||
|
||||
#include "ED_armature.h"
|
||||
#include "ED_keyframes_keylist.h"
|
||||
#include "ED_keyframing.h"
|
||||
#include "ED_markers.h"
|
||||
#include "ED_numinput.h"
|
||||
#include "ED_screen.h"
|
||||
|
@ -1768,18 +1770,6 @@ typedef enum ePosePropagate_Termination {
|
|||
POSE_PROPAGATE_SELECTED_MARKERS,
|
||||
} ePosePropagate_Termination;
|
||||
|
||||
/**
|
||||
* Termination data needed for some modes -
|
||||
* assumes only one of these entries will be needed at a time.
|
||||
*/
|
||||
typedef union tPosePropagate_ModeData {
|
||||
/** Smart holds + before frame: frame number to stop on. */
|
||||
float end_frame;
|
||||
|
||||
/** Selected markers: listbase for CfraElem's marking these frames. */
|
||||
ListBase sel_markers;
|
||||
} tPosePropagate_ModeData;
|
||||
|
||||
/* --------------------------------- */
|
||||
|
||||
/**
|
||||
|
@ -1862,80 +1852,11 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
|
|||
return endFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference value from F-Curve using RNA.
|
||||
*/
|
||||
static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
|
||||
{
|
||||
PointerRNA id_ptr, ptr;
|
||||
PropertyRNA *prop;
|
||||
bool found = false;
|
||||
|
||||
/* Base pointer is always the `object -> id_ptr`. */
|
||||
RNA_id_pointer_create(&ob->id, &id_ptr);
|
||||
|
||||
/* Resolve the property. */
|
||||
if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
|
||||
if (RNA_property_array_check(prop)) {
|
||||
/* Array. */
|
||||
if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
|
||||
found = true;
|
||||
switch (RNA_property_type(prop)) {
|
||||
case PROP_BOOLEAN:
|
||||
*value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
|
||||
break;
|
||||
case PROP_INT:
|
||||
*value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
|
||||
break;
|
||||
case PROP_FLOAT:
|
||||
*value = RNA_property_float_get_index(&ptr, prop, fcu->array_index);
|
||||
break;
|
||||
default:
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Not an array. */
|
||||
found = true;
|
||||
switch (RNA_property_type(prop)) {
|
||||
case PROP_BOOLEAN:
|
||||
*value = (float)RNA_property_boolean_get(&ptr, prop);
|
||||
break;
|
||||
case PROP_INT:
|
||||
*value = (float)RNA_property_int_get(&ptr, prop);
|
||||
break;
|
||||
case PROP_ENUM:
|
||||
*value = (float)RNA_property_enum_get(&ptr, prop);
|
||||
break;
|
||||
case PROP_FLOAT:
|
||||
*value = RNA_property_float_get(&ptr, prop);
|
||||
break;
|
||||
default:
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate just works along each F-Curve in turn.
|
||||
*/
|
||||
static void pose_propagate_fcurve(
|
||||
wmOperator *op, Object *ob, FCurve *fcu, float startFrame, tPosePropagate_ModeData modeData)
|
||||
static void pose_propagate_fcurve(FCurve *fcu, float start_frame, const float end_frame)
|
||||
{
|
||||
const int mode = RNA_enum_get(op->ptr, "mode");
|
||||
|
||||
BezTriple *bezt;
|
||||
float refVal = 0.0f;
|
||||
bool keyExists;
|
||||
int i;
|
||||
bool first = true;
|
||||
|
||||
/* Skip if no keyframes to edit. */
|
||||
if ((fcu->bezt == NULL) || (fcu->totvert < 2)) {
|
||||
return;
|
||||
|
@ -1944,74 +1865,32 @@ static void pose_propagate_fcurve(
|
|||
/* Find the reference value from bones directly, which means that the user
|
||||
* doesn't need to firstly keyframe the pose (though this doesn't mean that
|
||||
* they can't either). */
|
||||
if (!pose_propagate_get_refVal(ob, fcu, &refVal)) {
|
||||
return;
|
||||
}
|
||||
float refVal = evaluate_fcurve(fcu, start_frame);
|
||||
|
||||
/* Find the first keyframe to start propagating from:
|
||||
* - if there's a keyframe on the current frame, we probably want to save this value there too
|
||||
* since it may be as of yet un-keyed
|
||||
* - if starting before the starting frame, don't touch the key, as it may have had some valid
|
||||
* values
|
||||
* - if only doing selected keyframes, start from the first one
|
||||
*/
|
||||
if (mode != POSE_PROPAGATE_SELECTED_KEYS) {
|
||||
const int match = BKE_fcurve_bezt_binarysearch_index(
|
||||
fcu->bezt, startFrame, fcu->totvert, &keyExists);
|
||||
bool keyExists;
|
||||
const int match = BKE_fcurve_bezt_binarysearch_index(
|
||||
fcu->bezt, start_frame, fcu->totvert, &keyExists);
|
||||
|
||||
if (fcu->bezt[match].vec[1][0] < startFrame) {
|
||||
i = match + 1;
|
||||
}
|
||||
else {
|
||||
i = match;
|
||||
}
|
||||
int i;
|
||||
if (fcu->bezt[match].vec[1][0] < start_frame) {
|
||||
i = match + 1;
|
||||
}
|
||||
else {
|
||||
/* Selected - start from first keyframe. */
|
||||
i = 0;
|
||||
i = match;
|
||||
}
|
||||
|
||||
BezTriple *bezt;
|
||||
for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
|
||||
/* Additional termination conditions based on the operator 'mode' property go here. */
|
||||
if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
|
||||
/* Stop if keyframe is outside the accepted range. */
|
||||
if (bezt->vec[1][0] > modeData.end_frame) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (mode == POSE_PROPAGATE_NEXT_KEY) {
|
||||
/* Stop after the first keyframe has been processed. */
|
||||
if (first == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (mode == POSE_PROPAGATE_LAST_KEY) {
|
||||
/* Only affect this frame if it will be the last one. */
|
||||
if (i != (fcu->totvert - 1)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
|
||||
/* Only allow if there's a marker on this frame. */
|
||||
CfraElem *ce = NULL;
|
||||
|
||||
/* Stop on matching marker if there is one. */
|
||||
for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
|
||||
if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip this keyframe if no marker. */
|
||||
if (ce == NULL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (mode == POSE_PROPAGATE_SELECTED_KEYS) {
|
||||
/* Only allow if this keyframe is already selected - skip otherwise. */
|
||||
if (BEZT_ISSEL_ANY(bezt) == 0) {
|
||||
continue;
|
||||
}
|
||||
/* Stop if keyframe is outside the accepted range. */
|
||||
if (bezt->vec[1][0] > end_frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Just flatten handles, since values will now be the same either side. */
|
||||
|
@ -2020,10 +1899,123 @@ static void pose_propagate_fcurve(
|
|||
|
||||
/* Select keyframe to indicate that it's been changed. */
|
||||
bezt->f2 |= SELECT;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct FrameLink {
|
||||
struct FrameLink *next, *prev;
|
||||
float frame;
|
||||
} FrameLink;
|
||||
|
||||
static void propagate_curve_values(ListBase /*tPChanFCurveLink*/ *pflinks,
|
||||
const float source_frame,
|
||||
ListBase /*FrameLink*/ *target_frames)
|
||||
{
|
||||
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
|
||||
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
|
||||
FCurve *fcu = (FCurve *)ld->data;
|
||||
const float current_fcu_value = evaluate_fcurve(fcu, source_frame);
|
||||
LISTBASE_FOREACH (FrameLink *, target_frame, target_frames) {
|
||||
insert_vert_fcurve(
|
||||
fcu, target_frame->frame, current_fcu_value, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NEEDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float find_next_key(ListBase *pflinks, const float start_frame)
|
||||
{
|
||||
float target_frame = FLT_MAX;
|
||||
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
|
||||
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
|
||||
FCurve *fcu = (FCurve *)ld->data;
|
||||
bool replace;
|
||||
int current_frame_index = BKE_fcurve_bezt_binarysearch_index(
|
||||
fcu->bezt, start_frame, fcu->totvert, &replace);
|
||||
if (replace) {
|
||||
const int bezt_index = min_ii(current_frame_index + 1, fcu->totvert - 1);
|
||||
target_frame = min_ff(target_frame, fcu->bezt[bezt_index].vec[1][0]);
|
||||
}
|
||||
else {
|
||||
target_frame = min_ff(target_frame, fcu->bezt[current_frame_index].vec[1][0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target_frame;
|
||||
}
|
||||
|
||||
static float find_last_key(ListBase *pflinks)
|
||||
{
|
||||
float target_frame = FLT_MIN;
|
||||
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
|
||||
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
|
||||
FCurve *fcu = (FCurve *)ld->data;
|
||||
target_frame = max_ff(target_frame, fcu->bezt[fcu->totvert - 1].vec[1][0]);
|
||||
}
|
||||
}
|
||||
|
||||
return target_frame;
|
||||
}
|
||||
|
||||
static void get_selected_marker_positions(Scene *scene, ListBase /*FrameLink*/ *target_frames)
|
||||
{
|
||||
ListBase selected_markers = {NULL, NULL};
|
||||
ED_markers_make_cfra_list(&scene->markers, &selected_markers, SELECT);
|
||||
LISTBASE_FOREACH (CfraElem *, marker, &selected_markers) {
|
||||
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Marker Key Link");
|
||||
link->frame = marker->cfra;
|
||||
BLI_addtail(target_frames, link);
|
||||
}
|
||||
BLI_freelistN(&selected_markers);
|
||||
}
|
||||
|
||||
static void get_keyed_frames_in_range(ListBase *pflinks,
|
||||
const float start_frame,
|
||||
const float end_frame,
|
||||
ListBase /*FrameLink*/ *target_frames)
|
||||
{
|
||||
struct AnimKeylist *keylist = ED_keylist_create();
|
||||
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
|
||||
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
|
||||
FCurve *fcu = (FCurve *)ld->data;
|
||||
fcurve_to_keylist(NULL, fcu, keylist, 0);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (ActKeyColumn *, column, ED_keylist_listbase(keylist)) {
|
||||
if (column->cfra <= start_frame) {
|
||||
continue;
|
||||
}
|
||||
if (column->cfra > end_frame) {
|
||||
break;
|
||||
}
|
||||
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Marker Key Link");
|
||||
link->frame = column->cfra;
|
||||
BLI_addtail(target_frames, link);
|
||||
}
|
||||
ED_keylist_free(keylist);
|
||||
}
|
||||
|
||||
static void get_selected_frames(ListBase *pflinks, ListBase /*FrameLink*/ *target_frames)
|
||||
{
|
||||
struct AnimKeylist *keylist = ED_keylist_create();
|
||||
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
|
||||
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
|
||||
FCurve *fcu = (FCurve *)ld->data;
|
||||
fcurve_to_keylist(NULL, fcu, keylist, 0);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (ActKeyColumn *, column, ED_keylist_listbase(keylist)) {
|
||||
if (!column->sel) {
|
||||
continue;
|
||||
}
|
||||
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Marker Key Link");
|
||||
link->frame = column->cfra;
|
||||
BLI_addtail(target_frames, link);
|
||||
}
|
||||
ED_keylist_free(keylist);
|
||||
}
|
||||
|
||||
/* --------------------------------- */
|
||||
|
||||
static int pose_propagate_exec(bContext *C, wmOperator *op)
|
||||
|
@ -2033,9 +2025,7 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
|
|||
View3D *v3d = CTX_wm_view3d(C);
|
||||
|
||||
ListBase pflinks = {NULL, NULL};
|
||||
tPChanFCurveLink *pfl;
|
||||
|
||||
tPosePropagate_ModeData modeData;
|
||||
const int mode = RNA_enum_get(op->ptr, "mode");
|
||||
|
||||
/* Isolate F-Curves related to the selected bones. */
|
||||
|
@ -2049,40 +2039,73 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Mode-specific data preprocessing (requiring no access to curves). */
|
||||
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
|
||||
/* Get a list of selected markers. */
|
||||
ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
|
||||
}
|
||||
else {
|
||||
/* Assume everything else wants endFrame. */
|
||||
modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
|
||||
}
|
||||
const float end_frame = RNA_float_get(op->ptr, "end_frame");
|
||||
const float current_frame = BKE_scene_frame_get(scene);
|
||||
|
||||
/* For each bone, perform the copying required. */
|
||||
for (pfl = pflinks.first; pfl; pfl = pfl->next) {
|
||||
LinkData *ld;
|
||||
ListBase target_frames = {NULL, NULL};
|
||||
|
||||
/* Mode-specific data preprocessing (requiring access to all curves). */
|
||||
if (mode == POSE_PROPAGATE_SMART_HOLDS) {
|
||||
/* We store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
|
||||
* from the keyframe that occurs after the current frame. */
|
||||
modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)scene->r.cfra);
|
||||
switch (mode) {
|
||||
case POSE_PROPAGATE_NEXT_KEY: {
|
||||
float target_frame = find_next_key(&pflinks, current_frame);
|
||||
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Next Key Link");
|
||||
link->frame = target_frame;
|
||||
BLI_addtail(&target_frames, link);
|
||||
propagate_curve_values(&pflinks, current_frame, &target_frames);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Go through propagating pose to keyframes, curve by curve. */
|
||||
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
|
||||
pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)scene->r.cfra, modeData);
|
||||
case POSE_PROPAGATE_LAST_KEY: {
|
||||
float target_frame = find_last_key(&pflinks);
|
||||
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Last Key Link");
|
||||
link->frame = target_frame;
|
||||
BLI_addtail(&target_frames, link);
|
||||
propagate_curve_values(&pflinks, current_frame, &target_frames);
|
||||
break;
|
||||
}
|
||||
|
||||
case POSE_PROPAGATE_SELECTED_MARKERS: {
|
||||
get_selected_marker_positions(scene, &target_frames);
|
||||
propagate_curve_values(&pflinks, current_frame, &target_frames);
|
||||
break;
|
||||
}
|
||||
|
||||
case POSE_PROPAGATE_BEFORE_END: {
|
||||
get_keyed_frames_in_range(&pflinks, current_frame, FLT_MAX, &target_frames);
|
||||
propagate_curve_values(&pflinks, current_frame, &target_frames);
|
||||
break;
|
||||
}
|
||||
case POSE_PROPAGATE_BEFORE_FRAME: {
|
||||
get_keyed_frames_in_range(&pflinks, current_frame, end_frame, &target_frames);
|
||||
propagate_curve_values(&pflinks, current_frame, &target_frames);
|
||||
break;
|
||||
}
|
||||
case POSE_PROPAGATE_SELECTED_KEYS: {
|
||||
get_selected_frames(&pflinks, &target_frames);
|
||||
propagate_curve_values(&pflinks, current_frame, &target_frames);
|
||||
break;
|
||||
}
|
||||
case POSE_PROPAGATE_SMART_HOLDS: {
|
||||
/* For each bone, perform the copying required. */
|
||||
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, &pflinks) {
|
||||
/* Mode-specific data preprocessing (requiring access to all curves). */
|
||||
/* We store in endFrame the end frame of the "long keyframe" (i.e. a held value)
|
||||
* starting from the keyframe that occurs after the current frame. */
|
||||
const int smart_end_frame = pose_propagate_get_boneHoldEndFrame(pfl, current_frame);
|
||||
|
||||
/* Go through propagating pose to keyframes, curve by curve. */
|
||||
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
|
||||
pose_propagate_fcurve((FCurve *)ld->data, current_frame, smart_end_frame);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_freelistN(&target_frames);
|
||||
|
||||
/* Free temp data. */
|
||||
poseAnim_mapping_free(&pflinks);
|
||||
|
||||
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
|
||||
BLI_freelistN(&modeData.sel_markers);
|
||||
}
|
||||
|
||||
/* Updates + notifiers. */
|
||||
FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
|
||||
poseAnim_mapping_refresh(C, scene, ob);
|
||||
|
|
|
@ -497,7 +497,8 @@ static bool get_closest_vertex_to_point_in_nurbs(const ViewContext *vc,
|
|||
int handle_display = vc->v3d->overlay.handle_display;
|
||||
if (handle_display == CURVE_HANDLE_NONE ||
|
||||
(handle_display == CURVE_HANDLE_SELECTED && !BEZT_ISSEL_ANY(bezt))) {
|
||||
start = 1, end = 2;
|
||||
start = 1;
|
||||
end = 2;
|
||||
}
|
||||
|
||||
/* Loop over each of the 3 points of the #BezTriple and update data of closest bezt. */
|
||||
|
@ -1169,7 +1170,8 @@ static void move_segment(ViewContext *vc, MoveSegmentData *seg_data, const wmEve
|
|||
BezTriple *temp_bezt = bezt2;
|
||||
bezt2 = bezt1;
|
||||
bezt1 = temp_bezt;
|
||||
h1 = 0, h2 = 2;
|
||||
h1 = 0;
|
||||
h2 = 2;
|
||||
}
|
||||
|
||||
const float t = max_ff(min_ff(seg_data->t, 0.9f), 0.1f);
|
||||
|
|
|
@ -69,73 +69,79 @@ bool ED_space_clip_maskedit_mask_poll(struct bContext *C);
|
|||
* - Mask has visible and editable splines. */
|
||||
bool ED_space_clip_maskedit_mask_visible_splines_poll(struct bContext *C);
|
||||
|
||||
void ED_space_clip_get_size(struct SpaceClip *sc, int *width, int *height);
|
||||
void ED_space_clip_get_size_fl(struct SpaceClip *sc, float size[2]);
|
||||
void ED_space_clip_get_zoom(struct SpaceClip *sc,
|
||||
struct ARegion *region,
|
||||
void ED_space_clip_get_size(const struct SpaceClip *sc, int *width, int *height);
|
||||
void ED_space_clip_get_size_fl(const struct SpaceClip *sc, float size[2]);
|
||||
void ED_space_clip_get_zoom(const struct SpaceClip *sc,
|
||||
const struct ARegion *region,
|
||||
float *zoomx,
|
||||
float *zoomy);
|
||||
void ED_space_clip_get_aspect(struct SpaceClip *sc, float *aspx, float *aspy);
|
||||
void ED_space_clip_get_aspect_dimension_aware(struct SpaceClip *sc, float *aspx, float *aspy);
|
||||
void ED_space_clip_get_aspect(const struct SpaceClip *sc, float *aspx, float *aspy);
|
||||
void ED_space_clip_get_aspect_dimension_aware(const struct SpaceClip *sc,
|
||||
float *aspx,
|
||||
float *aspy);
|
||||
|
||||
/**
|
||||
* Return current frame number in clip space.
|
||||
*/
|
||||
int ED_space_clip_get_clip_frame_number(struct SpaceClip *sc);
|
||||
int ED_space_clip_get_clip_frame_number(const struct SpaceClip *sc);
|
||||
|
||||
struct ImBuf *ED_space_clip_get_buffer(struct SpaceClip *sc);
|
||||
struct ImBuf *ED_space_clip_get_stable_buffer(struct SpaceClip *sc,
|
||||
struct ImBuf *ED_space_clip_get_buffer(const struct SpaceClip *sc);
|
||||
struct ImBuf *ED_space_clip_get_stable_buffer(const struct SpaceClip *sc,
|
||||
float loc[2],
|
||||
float *scale,
|
||||
float *angle);
|
||||
|
||||
bool ED_space_clip_get_position(struct SpaceClip *sc,
|
||||
struct ARegion *region,
|
||||
bool ED_space_clip_get_position(const struct SpaceClip *sc,
|
||||
const struct ARegion *region,
|
||||
int mval[2],
|
||||
float fpos[2]);
|
||||
/**
|
||||
* Returns color in linear space, matching #ED_space_image_color_sample().
|
||||
*/
|
||||
bool ED_space_clip_color_sample(struct SpaceClip *sc,
|
||||
struct ARegion *region,
|
||||
bool ED_space_clip_color_sample(const struct SpaceClip *sc,
|
||||
const struct ARegion *region,
|
||||
const int mval[2],
|
||||
float r_col[3]);
|
||||
|
||||
void ED_clip_update_frame(const struct Main *mainp, int cfra);
|
||||
bool ED_clip_view_selection(const struct bContext *C, struct ARegion *region, bool fit);
|
||||
bool ED_clip_view_selection(const struct bContext *C, const struct ARegion *region, bool fit);
|
||||
|
||||
void ED_clip_select_all(struct SpaceClip *sc, int action, bool *r_has_selection);
|
||||
void ED_clip_select_all(const struct SpaceClip *sc, int action, bool *r_has_selection);
|
||||
bool ED_clip_can_select(struct bContext *C);
|
||||
|
||||
void ED_clip_point_undistorted_pos(struct SpaceClip *sc, const float co[2], float r_co[2]);
|
||||
void ED_clip_point_stable_pos(
|
||||
struct SpaceClip *sc, struct ARegion *region, float x, float y, float *xr, float *yr);
|
||||
void ED_clip_point_undistorted_pos(const struct SpaceClip *sc, const float co[2], float r_co[2]);
|
||||
void ED_clip_point_stable_pos(const struct SpaceClip *sc,
|
||||
const struct ARegion *region,
|
||||
float x,
|
||||
float y,
|
||||
float *xr,
|
||||
float *yr);
|
||||
/**
|
||||
* \brief the reverse of #ED_clip_point_stable_pos(), gets the marker region coords.
|
||||
* better name here? view_to_track / track_to_view or so?
|
||||
*/
|
||||
void ED_clip_point_stable_pos__reverse(struct SpaceClip *sc,
|
||||
struct ARegion *region,
|
||||
void ED_clip_point_stable_pos__reverse(const struct SpaceClip *sc,
|
||||
const struct ARegion *region,
|
||||
const float co[2],
|
||||
float r_co[2]);
|
||||
/**
|
||||
* Takes `event->mval`.
|
||||
*/
|
||||
void ED_clip_mouse_pos(struct SpaceClip *sc,
|
||||
struct ARegion *region,
|
||||
void ED_clip_mouse_pos(const struct SpaceClip *sc,
|
||||
const struct ARegion *region,
|
||||
const int mval[2],
|
||||
float co[2]);
|
||||
|
||||
bool ED_space_clip_check_show_trackedit(struct SpaceClip *sc);
|
||||
bool ED_space_clip_check_show_maskedit(struct SpaceClip *sc);
|
||||
bool ED_space_clip_check_show_trackedit(const struct SpaceClip *sc);
|
||||
bool ED_space_clip_check_show_maskedit(const struct SpaceClip *sc);
|
||||
|
||||
struct MovieClip *ED_space_clip_get_clip(struct SpaceClip *sc);
|
||||
struct MovieClip *ED_space_clip_get_clip(const struct SpaceClip *sc);
|
||||
void ED_space_clip_set_clip(struct bContext *C,
|
||||
struct bScreen *screen,
|
||||
struct SpaceClip *sc,
|
||||
struct MovieClip *clip);
|
||||
|
||||
struct Mask *ED_space_clip_get_mask(struct SpaceClip *sc);
|
||||
struct Mask *ED_space_clip_get_mask(const struct SpaceClip *sc);
|
||||
void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mask *mask);
|
||||
|
||||
/* Locked state is used to preserve current clip editor viewport upon changes. Example usage:
|
||||
|
|
|
@ -411,6 +411,7 @@ void blend_to_neighbor_fcurve_segment(struct FCurve *fcu,
|
|||
struct FCurveSegment *segment,
|
||||
float factor);
|
||||
void breakdown_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor);
|
||||
void ease_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor);
|
||||
bool decimate_fcurve(struct bAnimListElem *ale, float remove_ratio, float error_sq_max);
|
||||
void blend_to_default_fcurve(struct PointerRNA *id_ptr, struct FCurve *fcu, float factor);
|
||||
/**
|
||||
|
|
|
@ -135,7 +135,8 @@ static uiTooltipField *text_field_add(uiTooltipData *data,
|
|||
uiTooltipField *field = text_field_add_only(data);
|
||||
field->format = {};
|
||||
field->format.style = style;
|
||||
field->format.color_id = color, field->format.is_pad = is_pad;
|
||||
field->format.color_id = color;
|
||||
field->format.is_pad = is_pad;
|
||||
return field;
|
||||
}
|
||||
|
||||
|
|
|
@ -1484,5 +1484,6 @@ void ED_mesh_split_faces(Mesh *mesh)
|
|||
return;
|
||||
}
|
||||
|
||||
geometry::split_edges(*mesh, split_mask);
|
||||
const bke::AnonymousAttributePropagationInfo propagation_info;
|
||||
geometry::split_edges(*mesh, split_mask, propagation_info);
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ bool ED_space_clip_maskedit_mask_visible_splines_poll(bContext *C)
|
|||
/** \name Common Editing Functions
|
||||
* \{ */
|
||||
|
||||
void ED_space_clip_get_size(SpaceClip *sc, int *width, int *height)
|
||||
void ED_space_clip_get_size(const SpaceClip *sc, int *width, int *height)
|
||||
{
|
||||
if (sc->clip) {
|
||||
BKE_movieclip_get_size(sc->clip, &sc->user, width, height);
|
||||
|
@ -153,7 +153,7 @@ void ED_space_clip_get_size(SpaceClip *sc, int *width, int *height)
|
|||
}
|
||||
}
|
||||
|
||||
void ED_space_clip_get_size_fl(SpaceClip *sc, float size[2])
|
||||
void ED_space_clip_get_size_fl(const SpaceClip *sc, float size[2])
|
||||
{
|
||||
int size_i[2];
|
||||
ED_space_clip_get_size(sc, &size_i[0], &size_i[1]);
|
||||
|
@ -161,7 +161,7 @@ void ED_space_clip_get_size_fl(SpaceClip *sc, float size[2])
|
|||
size[1] = size_i[1];
|
||||
}
|
||||
|
||||
void ED_space_clip_get_zoom(SpaceClip *sc, ARegion *region, float *zoomx, float *zoomy)
|
||||
void ED_space_clip_get_zoom(const SpaceClip *sc, const ARegion *region, float *zoomx, float *zoomy)
|
||||
{
|
||||
int width, height;
|
||||
|
||||
|
@ -173,7 +173,7 @@ void ED_space_clip_get_zoom(SpaceClip *sc, ARegion *region, float *zoomx, float
|
|||
(BLI_rctf_size_y(®ion->v2d.cur) * height);
|
||||
}
|
||||
|
||||
void ED_space_clip_get_aspect(SpaceClip *sc, float *aspx, float *aspy)
|
||||
void ED_space_clip_get_aspect(const SpaceClip *sc, float *aspx, float *aspy)
|
||||
{
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
|
||||
|
@ -194,7 +194,7 @@ void ED_space_clip_get_aspect(SpaceClip *sc, float *aspx, float *aspy)
|
|||
}
|
||||
}
|
||||
|
||||
void ED_space_clip_get_aspect_dimension_aware(SpaceClip *sc, float *aspx, float *aspy)
|
||||
void ED_space_clip_get_aspect_dimension_aware(const SpaceClip *sc, float *aspx, float *aspy)
|
||||
{
|
||||
int w, h;
|
||||
|
||||
|
@ -228,7 +228,7 @@ void ED_space_clip_get_aspect_dimension_aware(SpaceClip *sc, float *aspx, float
|
|||
}
|
||||
}
|
||||
|
||||
int ED_space_clip_get_clip_frame_number(SpaceClip *sc)
|
||||
int ED_space_clip_get_clip_frame_number(const SpaceClip *sc)
|
||||
{
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
|
||||
|
@ -236,7 +236,7 @@ int ED_space_clip_get_clip_frame_number(SpaceClip *sc)
|
|||
return BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr);
|
||||
}
|
||||
|
||||
ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
|
||||
ImBuf *ED_space_clip_get_buffer(const SpaceClip *sc)
|
||||
{
|
||||
if (sc->clip) {
|
||||
ImBuf *ibuf;
|
||||
|
@ -255,7 +255,10 @@ ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ImBuf *ED_space_clip_get_stable_buffer(SpaceClip *sc, float loc[2], float *scale, float *angle)
|
||||
ImBuf *ED_space_clip_get_stable_buffer(const SpaceClip *sc,
|
||||
float loc[2],
|
||||
float *scale,
|
||||
float *angle)
|
||||
{
|
||||
if (sc->clip) {
|
||||
ImBuf *ibuf;
|
||||
|
@ -275,8 +278,8 @@ ImBuf *ED_space_clip_get_stable_buffer(SpaceClip *sc, float loc[2], float *scale
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool ED_space_clip_get_position(struct SpaceClip *sc,
|
||||
struct ARegion *region,
|
||||
bool ED_space_clip_get_position(const SpaceClip *sc,
|
||||
const ARegion *region,
|
||||
int mval[2],
|
||||
float fpos[2])
|
||||
{
|
||||
|
@ -292,7 +295,10 @@ bool ED_space_clip_get_position(struct SpaceClip *sc,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, const int mval[2], float r_col[3])
|
||||
bool ED_space_clip_color_sample(const SpaceClip *sc,
|
||||
const ARegion *region,
|
||||
const int mval[2],
|
||||
float r_col[3])
|
||||
{
|
||||
ImBuf *ibuf;
|
||||
float fx, fy, co[2];
|
||||
|
@ -355,7 +361,7 @@ void ED_clip_update_frame(const Main *mainp, int cfra)
|
|||
}
|
||||
}
|
||||
|
||||
bool ED_clip_view_selection(const bContext *C, ARegion *UNUSED(region), bool fit)
|
||||
bool ED_clip_view_selection(const bContext *C, const ARegion *UNUSED(region), bool fit)
|
||||
{
|
||||
float offset_x, offset_y;
|
||||
float zoom;
|
||||
|
@ -371,7 +377,7 @@ bool ED_clip_view_selection(const bContext *C, ARegion *UNUSED(region), bool fit
|
|||
return true;
|
||||
}
|
||||
|
||||
void ED_clip_select_all(SpaceClip *sc, int action, bool *r_has_selection)
|
||||
void ED_clip_select_all(const SpaceClip *sc, int action, bool *r_has_selection)
|
||||
{
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
|
||||
|
@ -460,7 +466,7 @@ void ED_clip_select_all(SpaceClip *sc, int action, bool *r_has_selection)
|
|||
}
|
||||
}
|
||||
|
||||
void ED_clip_point_undistorted_pos(SpaceClip *sc, const float co[2], float r_co[2])
|
||||
void ED_clip_point_undistorted_pos(const SpaceClip *sc, const float co[2], float r_co[2])
|
||||
{
|
||||
copy_v2_v2(r_co, co);
|
||||
|
||||
|
@ -482,7 +488,7 @@ void ED_clip_point_undistorted_pos(SpaceClip *sc, const float co[2], float r_co[
|
|||
}
|
||||
|
||||
void ED_clip_point_stable_pos(
|
||||
SpaceClip *sc, ARegion *region, float x, float y, float *xr, float *yr)
|
||||
const SpaceClip *sc, const ARegion *region, float x, float y, float *xr, float *yr)
|
||||
{
|
||||
int sx, sy, width, height;
|
||||
float zoomx, zoomy, pos[3], imat[4][4];
|
||||
|
@ -515,8 +521,8 @@ void ED_clip_point_stable_pos(
|
|||
}
|
||||
}
|
||||
|
||||
void ED_clip_point_stable_pos__reverse(SpaceClip *sc,
|
||||
ARegion *region,
|
||||
void ED_clip_point_stable_pos__reverse(const SpaceClip *sc,
|
||||
const ARegion *region,
|
||||
const float co[2],
|
||||
float r_co[2])
|
||||
{
|
||||
|
@ -539,12 +545,12 @@ void ED_clip_point_stable_pos__reverse(SpaceClip *sc,
|
|||
r_co[1] = (pos[1] * height * zoomy) + (float)sy;
|
||||
}
|
||||
|
||||
void ED_clip_mouse_pos(SpaceClip *sc, ARegion *region, const int mval[2], float co[2])
|
||||
void ED_clip_mouse_pos(const SpaceClip *sc, const ARegion *region, const int mval[2], float co[2])
|
||||
{
|
||||
ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &co[0], &co[1]);
|
||||
}
|
||||
|
||||
bool ED_space_clip_check_show_trackedit(SpaceClip *sc)
|
||||
bool ED_space_clip_check_show_trackedit(const SpaceClip *sc)
|
||||
{
|
||||
if (sc) {
|
||||
return sc->mode == SC_MODE_TRACKING;
|
||||
|
@ -553,7 +559,7 @@ bool ED_space_clip_check_show_trackedit(SpaceClip *sc)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ED_space_clip_check_show_maskedit(SpaceClip *sc)
|
||||
bool ED_space_clip_check_show_maskedit(const SpaceClip *sc)
|
||||
{
|
||||
if (sc) {
|
||||
return sc->mode == SC_MODE_MASKEDIT;
|
||||
|
@ -568,7 +574,7 @@ bool ED_space_clip_check_show_maskedit(SpaceClip *sc)
|
|||
/** \name Clip Editing Functions
|
||||
* \{ */
|
||||
|
||||
MovieClip *ED_space_clip_get_clip(SpaceClip *sc)
|
||||
MovieClip *ED_space_clip_get_clip(const SpaceClip *sc)
|
||||
{
|
||||
return sc->clip;
|
||||
}
|
||||
|
@ -629,7 +635,7 @@ void ED_space_clip_set_clip(bContext *C, bScreen *screen, SpaceClip *sc, MovieCl
|
|||
/** \name Masking Editing Functions
|
||||
* \{ */
|
||||
|
||||
Mask *ED_space_clip_get_mask(SpaceClip *sc)
|
||||
Mask *ED_space_clip_get_mask(const SpaceClip *sc)
|
||||
{
|
||||
return sc->mask_info.mask;
|
||||
}
|
||||
|
|
|
@ -113,6 +113,7 @@ void GRAPH_OT_delete(struct wmOperatorType *ot);
|
|||
void GRAPH_OT_clean(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_blend_to_neighbor(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_breakdown(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_ease(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_decimate(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_blend_to_default(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_sample(struct wmOperatorType *ot);
|
||||
|
|
|
@ -462,6 +462,7 @@ void graphedit_operatortypes(void)
|
|||
WM_operatortype_append(GRAPH_OT_decimate);
|
||||
WM_operatortype_append(GRAPH_OT_blend_to_neighbor);
|
||||
WM_operatortype_append(GRAPH_OT_breakdown);
|
||||
WM_operatortype_append(GRAPH_OT_ease);
|
||||
WM_operatortype_append(GRAPH_OT_blend_to_default);
|
||||
WM_operatortype_append(GRAPH_OT_euler_filter);
|
||||
WM_operatortype_append(GRAPH_OT_delete);
|
||||
|
|
|
@ -927,3 +927,129 @@ void GRAPH_OT_blend_to_default(wmOperatorType *ot)
|
|||
1.0f);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Ease Operator
|
||||
* \{ */
|
||||
|
||||
static void ease_graph_keys(bAnimContext *ac, const float factor)
|
||||
{
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
|
||||
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
|
||||
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
ListBase segments = find_fcurve_segments(fcu);
|
||||
|
||||
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
|
||||
ease_fcurve_segment(fcu, segment, factor);
|
||||
}
|
||||
|
||||
ale->update |= ANIM_UPDATE_DEFAULT;
|
||||
BLI_freelistN(&segments);
|
||||
}
|
||||
|
||||
ANIM_animdata_update(ac, &anim_data);
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
static void ease_draw_status_header(bContext *C, tGraphSliderOp *gso)
|
||||
{
|
||||
char status_str[UI_MAX_DRAW_STR];
|
||||
char mode_str[32];
|
||||
char slider_string[UI_MAX_DRAW_STR];
|
||||
|
||||
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
|
||||
|
||||
strcpy(mode_str, TIP_("Ease Keys"));
|
||||
|
||||
if (hasNumInput(&gso->num)) {
|
||||
char str_ofs[NUM_STR_REP_LEN];
|
||||
|
||||
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
|
||||
|
||||
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
|
||||
}
|
||||
else {
|
||||
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
|
||||
}
|
||||
|
||||
ED_workspace_status_text(C, status_str);
|
||||
}
|
||||
|
||||
static void ease_modal_update(bContext *C, wmOperator *op)
|
||||
{
|
||||
tGraphSliderOp *gso = op->customdata;
|
||||
|
||||
ease_draw_status_header(C, gso);
|
||||
|
||||
/* Reset keyframes to the state at invoke. */
|
||||
reset_bezts(gso);
|
||||
const float factor = slider_factor_get_and_remember(op);
|
||||
ease_graph_keys(&gso->ac, factor);
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
||||
}
|
||||
|
||||
static int ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const int invoke_result = graph_slider_invoke(C, op, event);
|
||||
|
||||
if (invoke_result == OPERATOR_CANCELLED) {
|
||||
return invoke_result;
|
||||
}
|
||||
|
||||
tGraphSliderOp *gso = op->customdata;
|
||||
gso->modal_update = ease_modal_update;
|
||||
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
|
||||
ease_draw_status_header(C, gso);
|
||||
|
||||
return invoke_result;
|
||||
}
|
||||
|
||||
static int ease_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bAnimContext ac;
|
||||
|
||||
/* Get editor data. */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const float factor = RNA_float_get(op->ptr, "factor");
|
||||
|
||||
ease_graph_keys(&ac, factor);
|
||||
|
||||
/* Set notifier that keyframes have changed. */
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GRAPH_OT_ease(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Ease Keyframes";
|
||||
ot->idname = "GRAPH_OT_ease";
|
||||
ot->description = "Align keyframes on a ease-in or ease-out curve";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->invoke = ease_invoke;
|
||||
ot->modal = graph_slider_modal;
|
||||
ot->exec = ease_exec;
|
||||
ot->poll = graphop_editable_keyframes_poll;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_float_factor(ot->srna,
|
||||
"factor",
|
||||
0.5f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
"Curve Bend",
|
||||
"Control the bend of the curve",
|
||||
0.0f,
|
||||
1.0f);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -288,32 +288,27 @@ struct LinkAndPosition {
|
|||
float2 multi_socket_position;
|
||||
};
|
||||
|
||||
static void sort_multi_input_socket_links_with_drag(bNode &node,
|
||||
static void sort_multi_input_socket_links_with_drag(bNodeSocket &socket,
|
||||
bNodeLink &drag_link,
|
||||
const float2 &cursor)
|
||||
{
|
||||
for (bNodeSocket *socket : node.input_sockets()) {
|
||||
if (!socket->is_multi_input()) {
|
||||
continue;
|
||||
}
|
||||
const float2 &socket_location = {socket->runtime->locx, socket->runtime->locy};
|
||||
const float2 &socket_location = {socket.runtime->locx, socket.runtime->locy};
|
||||
|
||||
Vector<LinkAndPosition, 8> links;
|
||||
for (bNodeLink *link : socket->directly_linked_links()) {
|
||||
const float2 location = node_link_calculate_multi_input_position(
|
||||
socket_location, link->multi_input_socket_index, link->tosock->runtime->total_inputs);
|
||||
links.append({link, location});
|
||||
};
|
||||
Vector<LinkAndPosition, 8> links;
|
||||
for (bNodeLink *link : socket.directly_linked_links()) {
|
||||
const float2 location = node_link_calculate_multi_input_position(
|
||||
socket_location, link->multi_input_socket_index, link->tosock->runtime->total_inputs);
|
||||
links.append({link, location});
|
||||
};
|
||||
|
||||
links.append({&drag_link, cursor});
|
||||
links.append({&drag_link, cursor});
|
||||
|
||||
std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
|
||||
return a.multi_socket_position.y < b.multi_socket_position.y;
|
||||
});
|
||||
std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
|
||||
return a.multi_socket_position.y < b.multi_socket_position.y;
|
||||
});
|
||||
|
||||
for (const int i : links.index_range()) {
|
||||
links[i].link->multi_input_socket_index = i;
|
||||
}
|
||||
for (const int i : links.index_range()) {
|
||||
links[i].link->multi_input_socket_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -972,8 +967,8 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
|
|||
existing_link_connected_to_fromsock->multi_input_socket_index;
|
||||
continue;
|
||||
}
|
||||
if (link.tosock && link.tosock->flag & SOCK_MULTI_INPUT) {
|
||||
sort_multi_input_socket_links_with_drag(tnode, link, cursor);
|
||||
if (tsock && tsock->is_multi_input()) {
|
||||
sort_multi_input_socket_links_with_drag(*tsock, link, cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,13 +202,18 @@ static void recalcData_sequencer_image(TransInfo *t)
|
|||
/* Origin. */
|
||||
float origin[2];
|
||||
copy_v2_v2(origin, td2d->loc);
|
||||
i++, td++, td2d++;
|
||||
i++;
|
||||
td++;
|
||||
td2d++;
|
||||
|
||||
/* X and Y control points used to read scale and rotation. */
|
||||
float handle_x[2];
|
||||
copy_v2_v2(handle_x, td2d->loc);
|
||||
sub_v2_v2(handle_x, origin);
|
||||
i++, td++, td2d++;
|
||||
i++;
|
||||
td++;
|
||||
td2d++;
|
||||
|
||||
float handle_y[2];
|
||||
copy_v2_v2(handle_y, td2d->loc);
|
||||
sub_v2_v2(handle_y, origin);
|
||||
|
|
|
@ -1098,7 +1098,7 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
const EnumPropertyItem pack_margin_method[] = {
|
||||
static const EnumPropertyItem pack_margin_method_items[] = {
|
||||
{ED_UVPACK_MARGIN_SCALED,
|
||||
"SCALED",
|
||||
0,
|
||||
|
@ -1139,8 +1139,12 @@ void UV_OT_pack_islands(wmOperatorType *ot)
|
|||
/* properties */
|
||||
RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", "");
|
||||
RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
|
||||
RNA_def_enum(
|
||||
ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", "");
|
||||
RNA_def_enum(ot->srna,
|
||||
"margin_method",
|
||||
pack_margin_method_items,
|
||||
ED_UVPACK_MARGIN_SCALED,
|
||||
"Margin Method",
|
||||
"");
|
||||
RNA_def_float_factor(
|
||||
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
|
||||
}
|
||||
|
@ -2077,8 +2081,12 @@ void UV_OT_unwrap(wmOperatorType *ot)
|
|||
0,
|
||||
"Use Subdivision Surface",
|
||||
"Map UVs taking vertex position after Subdivision Surface modifier has been applied");
|
||||
RNA_def_enum(
|
||||
ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", "");
|
||||
RNA_def_enum(ot->srna,
|
||||
"margin_method",
|
||||
pack_margin_method_items,
|
||||
ED_UVPACK_MARGIN_SCALED,
|
||||
"Margin Method",
|
||||
"");
|
||||
RNA_def_float_factor(
|
||||
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
|
||||
}
|
||||
|
@ -2444,8 +2452,12 @@ void UV_OT_smart_project(wmOperatorType *ot)
|
|||
DEG2RADF(89.0f));
|
||||
RNA_def_property_float_default(prop, DEG2RADF(66.0f));
|
||||
|
||||
RNA_def_enum(
|
||||
ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", "");
|
||||
RNA_def_enum(ot->srna,
|
||||
"margin_method",
|
||||
pack_margin_method_items,
|
||||
ED_UVPACK_MARGIN_SCALED,
|
||||
"Margin Method",
|
||||
"");
|
||||
RNA_def_float(ot->srna,
|
||||
"island_margin",
|
||||
0.0f,
|
||||
|
|
|
@ -53,6 +53,10 @@ class Socket : NonCopyable, NonMovable {
|
|||
* Index of the socket. E.g. 0 for the first input and the first output socket.
|
||||
*/
|
||||
int index_in_node_;
|
||||
/**
|
||||
* Index of the socket in the entire graph. Every socket has a different index.
|
||||
*/
|
||||
int index_in_graph_;
|
||||
|
||||
friend Graph;
|
||||
|
||||
|
@ -61,6 +65,7 @@ class Socket : NonCopyable, NonMovable {
|
|||
bool is_output() const;
|
||||
|
||||
int index() const;
|
||||
int index_in_graph() const;
|
||||
|
||||
InputSocket &as_input();
|
||||
OutputSocket &as_output();
|
||||
|
@ -179,6 +184,20 @@ class DummyDebugInfo {
|
|||
virtual std::string output_name(const int i) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Just stores a string per socket in a dummy node.
|
||||
*/
|
||||
class SimpleDummyDebugInfo : public DummyDebugInfo {
|
||||
public:
|
||||
std::string name;
|
||||
Vector<std::string> input_names;
|
||||
Vector<std::string> output_names;
|
||||
|
||||
std::string node_name() const override;
|
||||
std::string input_name(const int i) const override;
|
||||
std::string output_name(const int i) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* A #Node that does *not* correspond to a #LazyFunction. Instead it can be used to indicate inputs
|
||||
* and outputs of the entire graph. It can have an arbitrary number of inputs and outputs.
|
||||
|
@ -205,6 +224,11 @@ class Graph : NonCopyable, NonMovable {
|
|||
* Contains all nodes in the graph so that it is efficient to iterate over them.
|
||||
*/
|
||||
Vector<Node *> nodes_;
|
||||
/**
|
||||
* Number of sockets in the graph. Can be used as array size when indexing using
|
||||
* `Socket::index_in_graph`.
|
||||
*/
|
||||
int socket_num_ = 0;
|
||||
|
||||
public:
|
||||
~Graph();
|
||||
|
@ -213,6 +237,7 @@ class Graph : NonCopyable, NonMovable {
|
|||
* Get all nodes in the graph. The index in the span corresponds to #Node::index_in_graph.
|
||||
*/
|
||||
Span<const Node *> nodes() const;
|
||||
Span<Node *> nodes();
|
||||
|
||||
/**
|
||||
* Add a new function node with sockets that match the passed in #LazyFunction.
|
||||
|
@ -232,10 +257,24 @@ class Graph : NonCopyable, NonMovable {
|
|||
*/
|
||||
void add_link(OutputSocket &from, InputSocket &to);
|
||||
|
||||
/**
|
||||
* If the socket is linked, remove the link.
|
||||
*/
|
||||
void clear_origin(InputSocket &socket);
|
||||
|
||||
/**
|
||||
* Make sure that #Node::index_in_graph is up to date.
|
||||
*/
|
||||
void update_node_indices();
|
||||
/**
|
||||
* Make sure that #Socket::index_in_graph is up to date.
|
||||
*/
|
||||
void update_socket_indices();
|
||||
|
||||
/**
|
||||
* Number of sockets in the graph.
|
||||
*/
|
||||
int socket_num() const;
|
||||
|
||||
/**
|
||||
* Can be used to assert that #update_node_indices has been called.
|
||||
|
@ -280,6 +319,11 @@ inline int Socket::index() const
|
|||
return index_in_node_;
|
||||
}
|
||||
|
||||
inline int Socket::index_in_graph() const
|
||||
{
|
||||
return index_in_graph_;
|
||||
}
|
||||
|
||||
inline InputSocket &Socket::as_input()
|
||||
{
|
||||
BLI_assert(this->is_input());
|
||||
|
@ -445,6 +489,16 @@ inline Span<const Node *> Graph::nodes() const
|
|||
return nodes_;
|
||||
}
|
||||
|
||||
inline Span<Node *> Graph::nodes()
|
||||
{
|
||||
return nodes_;
|
||||
}
|
||||
|
||||
inline int Graph::socket_num() const
|
||||
{
|
||||
return socket_num_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::fn::lazy_function
|
||||
|
|
|
@ -86,6 +86,14 @@ void Graph::add_link(OutputSocket &from, InputSocket &to)
|
|||
from.targets_.append(&to);
|
||||
}
|
||||
|
||||
void Graph::clear_origin(InputSocket &socket)
|
||||
{
|
||||
if (socket.origin_ != nullptr) {
|
||||
socket.origin_->targets_.remove_first_occurrence_and_reorder(&socket);
|
||||
socket.origin_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Graph::update_node_indices()
|
||||
{
|
||||
for (const int i : nodes_.index_range()) {
|
||||
|
@ -93,6 +101,20 @@ void Graph::update_node_indices()
|
|||
}
|
||||
}
|
||||
|
||||
void Graph::update_socket_indices()
|
||||
{
|
||||
int socket_counter = 0;
|
||||
for (const int i : nodes_.index_range()) {
|
||||
for (InputSocket *socket : nodes_[i]->inputs()) {
|
||||
socket->index_in_graph_ = socket_counter++;
|
||||
}
|
||||
for (OutputSocket *socket : nodes_[i]->outputs()) {
|
||||
socket->index_in_graph_ = socket_counter++;
|
||||
}
|
||||
}
|
||||
socket_num_ = socket_counter;
|
||||
}
|
||||
|
||||
bool Graph::node_indices_are_valid() const
|
||||
{
|
||||
for (const int i : nodes_.index_range()) {
|
||||
|
@ -152,6 +174,21 @@ std::string DummyDebugInfo::output_name(const int /*i*/) const
|
|||
return fallback_name;
|
||||
}
|
||||
|
||||
std::string SimpleDummyDebugInfo::node_name() const
|
||||
{
|
||||
return this->name;
|
||||
}
|
||||
|
||||
std::string SimpleDummyDebugInfo::input_name(const int i) const
|
||||
{
|
||||
return this->input_names[i];
|
||||
}
|
||||
|
||||
std::string SimpleDummyDebugInfo::output_name(const int i) const
|
||||
{
|
||||
return this->output_names[i];
|
||||
}
|
||||
|
||||
std::string Graph::ToDotOptions::socket_name(const Socket &socket) const
|
||||
{
|
||||
return socket.name();
|
||||
|
|
|
@ -1080,6 +1080,12 @@ class Executor {
|
|||
|
||||
bool try_enable_multi_threading()
|
||||
{
|
||||
#ifndef WITH_TBB
|
||||
/* The non-tbb task pool has the property that it immediately executes tasks under some
|
||||
* circumstances. This is not supported here because tasks might be scheduled while another
|
||||
* node is in the middle of being executed on the same thread. */
|
||||
return false;
|
||||
#endif
|
||||
if (this->use_multi_threading()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,15 +9,19 @@
|
|||
|
||||
namespace blender::geometry {
|
||||
|
||||
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
|
||||
IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
const VArray<int> &counts,
|
||||
bool limit_radius);
|
||||
bke::CurvesGeometry fillet_curves_poly(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
const VArray<int> &counts,
|
||||
bool limit_radius,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
|
||||
IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
bool limit_radius);
|
||||
bke::CurvesGeometry fillet_curves_bezier(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
bool limit_radius,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -4,10 +4,14 @@
|
|||
|
||||
#include "BLI_index_mask.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
struct Mesh;
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
void split_edges(Mesh &mesh, IndexMask mask);
|
||||
void split_edges(Mesh &mesh,
|
||||
IndexMask mask,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -19,11 +19,16 @@ namespace blender::geometry {
|
|||
* intersections of more than three edges will become breaks in curves. Attributes that
|
||||
* are not built-in on meshes and not curves are transferred to the result curve.
|
||||
*/
|
||||
bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection);
|
||||
bke::CurvesGeometry mesh_to_curve_convert(
|
||||
const Mesh &mesh,
|
||||
const IndexMask selection,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
|
||||
Span<int> vert_indices,
|
||||
Span<int> curve_offsets,
|
||||
IndexRange cyclic_curves);
|
||||
bke::CurvesGeometry create_curve_from_vert_indices(
|
||||
const Mesh &mesh,
|
||||
Span<int> vert_indices,
|
||||
Span<int> curve_offsets,
|
||||
IndexRange cyclic_curves,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "BLI_index_mask.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
#pragma once
|
||||
|
||||
struct PointCloud;
|
||||
|
@ -17,8 +19,10 @@ namespace blender::geometry {
|
|||
* Merge selected points into other selected points within the \a merge_distance. The merged
|
||||
* indices favor speed over accuracy, since the results will depend on the order of the points.
|
||||
*/
|
||||
PointCloud *point_merge_by_distance(const PointCloud &src_points,
|
||||
const float merge_distance,
|
||||
const IndexMask selection);
|
||||
PointCloud *point_merge_by_distance(
|
||||
const PointCloud &src_points,
|
||||
const float merge_distance,
|
||||
const IndexMask selection,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -19,6 +19,8 @@ struct RealizeInstancesOptions {
|
|||
* instances. Otherwise, instance attributes are ignored.
|
||||
*/
|
||||
bool realize_instance_attributes = true;
|
||||
|
||||
bke::AnonymousAttributePropagationInfo propagation_info;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "FN_field.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_curves.hh"
|
||||
|
||||
namespace blender::geometry {
|
||||
|
|
|
@ -27,6 +27,7 @@ bool try_curves_conversion_in_place(IndexMask selection,
|
|||
*/
|
||||
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
|
||||
IndexMask selection,
|
||||
CurveType dst_type);
|
||||
CurveType dst_type,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -17,8 +17,10 @@ namespace blender::geometry {
|
|||
*
|
||||
* \param selection: A selection of curves to consider when subdividing.
|
||||
*/
|
||||
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
|
||||
IndexMask selection,
|
||||
const VArray<int> &cuts);
|
||||
bke::CurvesGeometry subdivide_curves(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
IndexMask selection,
|
||||
const VArray<int> &cuts,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -19,6 +19,7 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
|
|||
IndexMask selection,
|
||||
const VArray<float> &starts,
|
||||
const VArray<float> &ends,
|
||||
GeometryNodeCurveSampleMode mode);
|
||||
GeometryNodeCurveSampleMode mode,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -372,7 +372,7 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
|
|||
Set<std::string> attributes_to_skip{{"position", "curve_type", "surface_uv_coordinate"}};
|
||||
attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
|
||||
if (id.is_named() && attributes_to_skip.contains(id.name())) {
|
||||
if (attributes_to_skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
|
||||
|
|
|
@ -397,12 +397,14 @@ static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
|
|||
});
|
||||
}
|
||||
|
||||
static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask curve_selection,
|
||||
const VArray<float> &radius_input,
|
||||
const VArray<int> &counts,
|
||||
const bool limit_radius,
|
||||
const bool use_bezier_mode)
|
||||
static bke::CurvesGeometry fillet_curves(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask curve_selection,
|
||||
const VArray<float> &radius_input,
|
||||
const VArray<int> &counts,
|
||||
const bool limit_radius,
|
||||
const bool use_bezier_mode,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const Vector<IndexRange> unselected_ranges = curve_selection.extract_ranges_invert(
|
||||
src_curves.curves_range());
|
||||
|
@ -520,6 +522,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
|
|||
src_attributes,
|
||||
dst_attributes,
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info,
|
||||
{"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) {
|
||||
duplicate_fillet_point_data(
|
||||
src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span);
|
||||
|
@ -528,7 +531,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
|
|||
|
||||
if (!unselected_ranges.is_empty()) {
|
||||
for (auto &attribute : bke::retrieve_attributes_for_transfer(
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
|
||||
bke::curves::copy_point_data(
|
||||
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
|
@ -538,26 +541,32 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
|
|||
return dst_curves;
|
||||
}
|
||||
|
||||
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
const VArray<int> &count,
|
||||
const bool limit_radius)
|
||||
bke::CurvesGeometry fillet_curves_poly(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
const VArray<int> &count,
|
||||
const bool limit_radius,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false);
|
||||
return fillet_curves(
|
||||
src_curves, curve_selection, radius, count, limit_radius, false, propagation_info);
|
||||
}
|
||||
|
||||
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
const bool limit_radius)
|
||||
bke::CurvesGeometry fillet_curves_bezier(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask curve_selection,
|
||||
const VArray<float> &radius,
|
||||
const bool limit_radius,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
return fillet_curves(src_curves,
|
||||
curve_selection,
|
||||
radius,
|
||||
VArray<int>::ForSingle(1, src_curves.points_num()),
|
||||
limit_radius,
|
||||
true);
|
||||
true,
|
||||
propagation_info);
|
||||
}
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_devirtualize_parameters.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_user_counter.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
@ -59,35 +60,36 @@ static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
|
|||
|
||||
static void add_new_edges(Mesh &mesh,
|
||||
const Span<MEdge> new_edges,
|
||||
const Span<int> new_to_old_edges_map)
|
||||
const Span<int> new_to_old_edges_map,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
|
||||
/* Store a copy of the IDs locally since we will remove the existing attributes which
|
||||
* can also free the names, since the API does not provide pointer stability. */
|
||||
Vector<std::string> named_ids;
|
||||
Vector<bke::WeakAnonymousAttributeID> anonymous_ids;
|
||||
Vector<UserCounter<const bke::AnonymousAttributeID>> anonymous_ids;
|
||||
for (const bke::AttributeIDRef &id : attributes.all_ids()) {
|
||||
if (attributes.lookup_meta_data(id)->domain != ATTR_DOMAIN_EDGE) {
|
||||
continue;
|
||||
}
|
||||
if (!id.should_be_kept()) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
continue;
|
||||
}
|
||||
if (id.is_named()) {
|
||||
if (!id.is_anonymous()) {
|
||||
named_ids.append(id.name());
|
||||
}
|
||||
else {
|
||||
anonymous_ids.append(bke::WeakAnonymousAttributeID(&id.anonymous_id()));
|
||||
BKE_anonymous_attribute_id_increment_weak(&id.anonymous_id());
|
||||
anonymous_ids.append(&id.anonymous_id());
|
||||
id.anonymous_id().user_add();
|
||||
}
|
||||
}
|
||||
Vector<bke::AttributeIDRef> local_edge_ids;
|
||||
for (const StringRef name : named_ids) {
|
||||
local_edge_ids.append(name);
|
||||
}
|
||||
for (const bke::WeakAnonymousAttributeID &id : anonymous_ids) {
|
||||
local_edge_ids.append(id.get());
|
||||
for (const UserCounter<const bke::AnonymousAttributeID> &id : anonymous_ids) {
|
||||
local_edge_ids.append(*id);
|
||||
}
|
||||
|
||||
/* Build new arrays for the copied edge attributes. Unlike vertices, new edges aren't all at the
|
||||
|
@ -348,7 +350,9 @@ static void split_edge_per_poly(const int edge_i,
|
|||
edge_to_loop_map[edge_i].resize(1);
|
||||
}
|
||||
|
||||
void split_edges(Mesh &mesh, const IndexMask mask)
|
||||
void split_edges(Mesh &mesh,
|
||||
const IndexMask mask,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
/* Flag vertices that need to be split. */
|
||||
Array<bool> should_split_vert(mesh.totvert, false);
|
||||
|
@ -483,7 +487,7 @@ void split_edges(Mesh &mesh, const IndexMask mask)
|
|||
|
||||
/* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */
|
||||
add_new_vertices(mesh, new_to_old_verts_map);
|
||||
add_new_edges(mesh, new_edges, new_to_old_edges_map);
|
||||
add_new_edges(mesh, new_edges, new_to_old_edges_map, propagation_info);
|
||||
|
||||
BKE_mesh_tag_edges_split(&mesh);
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
|
||||
namespace blender::geometry {
|
||||
|
||||
bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
|
||||
const Span<int> vert_indices,
|
||||
const Span<int> curve_offsets,
|
||||
const IndexRange cyclic_curves)
|
||||
bke::CurvesGeometry create_curve_from_vert_indices(
|
||||
const Mesh &mesh,
|
||||
const Span<int> vert_indices,
|
||||
const Span<int> curve_offsets,
|
||||
const IndexRange cyclic_curves,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size());
|
||||
curves.offsets_for_write().drop_back(1).copy_from(curve_offsets);
|
||||
|
@ -43,7 +45,7 @@ bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!attribute_id.should_be_kept()) {
|
||||
if (attribute_id.is_anonymous() && !propagation_info.propagate(attribute_id.anonymous_id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -209,14 +211,17 @@ static Vector<std::pair<int, int>> get_selected_edges(const Mesh &mesh, const In
|
|||
return selected_edges;
|
||||
}
|
||||
|
||||
bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection)
|
||||
bke::CurvesGeometry mesh_to_curve_convert(
|
||||
const Mesh &mesh,
|
||||
const IndexMask selection,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
Vector<std::pair<int, int>> selected_edges = get_selected_edges(mesh, selection);
|
||||
const Span<MVert> verts = mesh.verts();
|
||||
CurveFromEdgesOutput output = edges_to_curve_point_indices(verts, selected_edges);
|
||||
|
||||
return create_curve_from_vert_indices(
|
||||
mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves);
|
||||
mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves, propagation_info);
|
||||
}
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -15,7 +15,8 @@ namespace blender::geometry {
|
|||
|
||||
PointCloud *point_merge_by_distance(const PointCloud &src_points,
|
||||
const float merge_distance,
|
||||
const IndexMask selection)
|
||||
const IndexMask selection,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const bke::AttributeAccessor src_attributes = src_points.attributes();
|
||||
VArraySpan<float3> positions = src_attributes.lookup_or_default<float3>(
|
||||
|
@ -125,7 +126,7 @@ PointCloud *point_merge_by_distance(const PointCloud &src_points,
|
|||
|
||||
/* Transfer all other attributes. */
|
||||
for (const bke::AttributeIDRef &id : attribute_ids) {
|
||||
if (!id.should_be_kept()) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -636,8 +636,11 @@ static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate(
|
|||
}
|
||||
|
||||
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
|
||||
in_geometry_set.gather_attributes_for_propagation(
|
||||
src_component_types, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes_to_propagate);
|
||||
in_geometry_set.gather_attributes_for_propagation(src_component_types,
|
||||
GEO_COMPONENT_TYPE_POINT_CLOUD,
|
||||
true,
|
||||
options.propagation_info,
|
||||
attributes_to_propagate);
|
||||
attributes_to_propagate.remove("position");
|
||||
r_create_id = attributes_to_propagate.pop_try("id").has_value();
|
||||
r_create_radii = attributes_to_propagate.pop_try("radius").has_value();
|
||||
|
@ -829,8 +832,11 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
|
|||
}
|
||||
|
||||
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
|
||||
in_geometry_set.gather_attributes_for_propagation(
|
||||
src_component_types, GEO_COMPONENT_TYPE_MESH, true, attributes_to_propagate);
|
||||
in_geometry_set.gather_attributes_for_propagation(src_component_types,
|
||||
GEO_COMPONENT_TYPE_MESH,
|
||||
true,
|
||||
options.propagation_info,
|
||||
attributes_to_propagate);
|
||||
attributes_to_propagate.remove("position");
|
||||
attributes_to_propagate.remove("normal");
|
||||
attributes_to_propagate.remove("shade_smooth");
|
||||
|
@ -1149,8 +1155,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate(
|
|||
}
|
||||
|
||||
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
|
||||
in_geometry_set.gather_attributes_for_propagation(
|
||||
src_component_types, GEO_COMPONENT_TYPE_CURVE, true, attributes_to_propagate);
|
||||
in_geometry_set.gather_attributes_for_propagation(src_component_types,
|
||||
GEO_COMPONENT_TYPE_CURVE,
|
||||
true,
|
||||
options.propagation_info,
|
||||
attributes_to_propagate);
|
||||
attributes_to_propagate.remove("position");
|
||||
attributes_to_propagate.remove("radius");
|
||||
attributes_to_propagate.remove("resolution");
|
||||
|
|
|
@ -53,7 +53,7 @@ static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length
|
|||
static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id,
|
||||
const std::array<int, CURVE_TYPES_NUM> &type_counts)
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return true;
|
||||
}
|
||||
if (ELEM(attribute_id.name(),
|
||||
|
@ -81,7 +81,7 @@ static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attri
|
|||
"handle_left",
|
||||
"nurbs_weight",
|
||||
}};
|
||||
return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name()));
|
||||
return !no_interpolation.contains(attribute_id.name());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -286,8 +286,10 @@ static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<
|
|||
});
|
||||
}
|
||||
|
||||
static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask selection)
|
||||
static bke::CurvesGeometry convert_curves_to_bezier(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask selection,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
|
||||
const VArray<int8_t> src_types = src_curves.curve_types();
|
||||
|
@ -315,6 +317,7 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s
|
|||
src_attributes,
|
||||
dst_attributes,
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info,
|
||||
{"position",
|
||||
"handle_type_left",
|
||||
"handle_type_right",
|
||||
|
@ -460,8 +463,10 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s
|
|||
return dst_curves;
|
||||
}
|
||||
|
||||
static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask selection)
|
||||
static bke::CurvesGeometry convert_curves_to_nurbs(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask selection,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const VArray<int8_t> src_types = src_curves.curve_types();
|
||||
const VArray<bool> src_cyclic = src_curves.cyclic();
|
||||
|
@ -487,6 +492,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr
|
|||
src_attributes,
|
||||
dst_attributes,
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info,
|
||||
{"position",
|
||||
"handle_type_left",
|
||||
"handle_type_right",
|
||||
|
@ -639,16 +645,17 @@ static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src
|
|||
|
||||
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask selection,
|
||||
const CurveType dst_type)
|
||||
const CurveType dst_type,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
switch (dst_type) {
|
||||
case CURVE_TYPE_CATMULL_ROM:
|
||||
case CURVE_TYPE_POLY:
|
||||
return convert_curves_trivial(src_curves, selection, dst_type);
|
||||
case CURVE_TYPE_BEZIER:
|
||||
return convert_curves_to_bezier(src_curves, selection);
|
||||
return convert_curves_to_bezier(src_curves, selection, propagation_info);
|
||||
case CURVE_TYPE_NURBS:
|
||||
return convert_curves_to_nurbs(src_curves, selection);
|
||||
return convert_curves_to_nurbs(src_curves, selection, propagation_info);
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
|
|
|
@ -294,9 +294,11 @@ static void subdivide_bezier_positions(const Span<float3> src_positions,
|
|||
cyclic, dst_types_l, dst_types_r, dst_positions, dst_handles_l, dst_handles_r);
|
||||
}
|
||||
|
||||
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask selection,
|
||||
const VArray<int> &cuts)
|
||||
bke::CurvesGeometry subdivide_curves(
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask selection,
|
||||
const VArray<int> &cuts,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
|
||||
src_curves.curves_range());
|
||||
|
@ -338,7 +340,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
|
|||
|
||||
auto subdivide_catmull_rom = [&](IndexMask selection) {
|
||||
for (auto &attribute : bke::retrieve_attributes_for_transfer(
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
|
||||
subdivide_attribute_catmull_rom(src_curves,
|
||||
dst_curves,
|
||||
selection,
|
||||
|
@ -352,7 +354,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
|
|||
|
||||
auto subdivide_poly = [&](IndexMask selection) {
|
||||
for (auto &attribute : bke::retrieve_attributes_for_transfer(
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
|
||||
subdivide_attribute_linear(
|
||||
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
|
@ -396,6 +398,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
|
|||
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
|
||||
dst_attributes,
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info,
|
||||
{"position",
|
||||
"handle_type_left",
|
||||
"handle_type_right",
|
||||
|
@ -421,7 +424,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
|
|||
|
||||
if (!unselected_ranges.is_empty()) {
|
||||
for (auto &attribute : bke::retrieve_attributes_for_transfer(
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
|
||||
bke::curves::copy_point_data(
|
||||
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
|
|
|
@ -929,7 +929,8 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
|
|||
const IndexMask selection,
|
||||
const VArray<float> &starts,
|
||||
const VArray<float> &ends,
|
||||
const GeometryNodeCurveSampleMode mode)
|
||||
const GeometryNodeCurveSampleMode mode,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
BLI_assert(selection.size() > 0);
|
||||
BLI_assert(selection.last() <= src_curves.curves_num());
|
||||
|
@ -991,14 +992,19 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
|
|||
transfer_curve_skip.remove("nurbs_order");
|
||||
transfer_curve_skip.remove("knots_mode");
|
||||
}
|
||||
bke::copy_attribute_domain(
|
||||
src_attributes, dst_attributes, selection, ATTR_DOMAIN_CURVE, transfer_curve_skip);
|
||||
bke::copy_attribute_domain(src_attributes,
|
||||
dst_attributes,
|
||||
selection,
|
||||
ATTR_DOMAIN_CURVE,
|
||||
propagation_info,
|
||||
transfer_curve_skip);
|
||||
|
||||
/* Fetch custom point domain attributes for transfer (copy). */
|
||||
Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer(
|
||||
src_attributes,
|
||||
dst_attributes,
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info,
|
||||
{"position",
|
||||
"handle_left",
|
||||
"handle_right",
|
||||
|
@ -1063,8 +1069,12 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
|
|||
/* Copy unselected */
|
||||
if (!inverse_selection.is_empty()) {
|
||||
transfer_curve_skip.remove("cyclic");
|
||||
bke::copy_attribute_domain(
|
||||
src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE, transfer_curve_skip);
|
||||
bke::copy_attribute_domain(src_attributes,
|
||||
dst_attributes,
|
||||
inverse_selection,
|
||||
ATTR_DOMAIN_CURVE,
|
||||
propagation_info,
|
||||
transfer_curve_skip);
|
||||
/* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */
|
||||
dst_curves.cyclic_for_write().fill_indices(selection, false);
|
||||
|
||||
|
@ -1075,8 +1085,11 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
|
|||
}
|
||||
|
||||
/* Copy point domain. */
|
||||
for (auto &attribute : bke::retrieve_attributes_for_transfer(
|
||||
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, copy_point_skip)) {
|
||||
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
|
||||
dst_attributes,
|
||||
ATTR_DOMAIN_MASK_POINT,
|
||||
propagation_info,
|
||||
copy_point_skip)) {
|
||||
bke::curves::copy_point_data(
|
||||
src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
|
|
|
@ -2450,9 +2450,10 @@ static void lineart_object_load_single_instance(LineartData *ld,
|
|||
}
|
||||
if (ob->type == OB_MESH) {
|
||||
use_mesh = BKE_object_get_evaluated_mesh(ob);
|
||||
if (use_mesh->edit_mesh) {
|
||||
if ((!use_mesh) || use_mesh->edit_mesh) {
|
||||
/* If the object is being edited, then the mesh is not evaluated fully into the final
|
||||
* result, do not load them. */
|
||||
* result, do not load them. This could be caused by incorrect evaluation order due to
|
||||
* the way line art uses depsgraph.See T102612 for explaination of this workaround. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2460,7 +2461,7 @@ static void lineart_object_load_single_instance(LineartData *ld,
|
|||
use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true);
|
||||
}
|
||||
|
||||
/* In case we still can not get any mesh geometry data from the object */
|
||||
/* In case we still can not get any mesh geometry data from the object, same as above. */
|
||||
if (!use_mesh) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -603,9 +603,15 @@ static void lineart_shadow_edge_cut(LineartData *ld,
|
|||
for (seg = cut_start_after; seg != cut_end_before; seg = nes) {
|
||||
nes = seg->next;
|
||||
|
||||
s1_fb_co_1 = seg->fbc2, s1_fb_co_2 = nes->fbc1;
|
||||
s1_gloc_1 = seg->g2, s1_gloc_2 = nes->g1;
|
||||
seg_1 = seg, seg_2 = nes;
|
||||
s1_fb_co_1 = seg->fbc2;
|
||||
s1_fb_co_2 = nes->fbc1;
|
||||
|
||||
s1_gloc_1 = seg->g2;
|
||||
s1_gloc_2 = nes->g1;
|
||||
|
||||
seg_1 = seg;
|
||||
seg_2 = nes;
|
||||
|
||||
if (seg == cut_start_after) {
|
||||
lineart_shadow_segment_slice_get(seg->fbc2,
|
||||
nes->fbc1,
|
||||
|
@ -616,7 +622,9 @@ static void lineart_shadow_edge_cut(LineartData *ld,
|
|||
nes->ratio,
|
||||
m_fbc1,
|
||||
m_g1);
|
||||
s1_fb_co_1 = m_fbc1, s1_gloc_1 = m_g1;
|
||||
s1_fb_co_1 = m_fbc1;
|
||||
s1_gloc_1 = m_g1;
|
||||
|
||||
seg_1 = new_seg_1;
|
||||
if (cut_start_after != new_seg_1) {
|
||||
BLI_insertlinkafter(&e->shadow_segments, cut_start_after, new_seg_1);
|
||||
|
@ -634,7 +642,9 @@ static void lineart_shadow_edge_cut(LineartData *ld,
|
|||
nes->ratio,
|
||||
m_fbc2,
|
||||
m_g2);
|
||||
s1_fb_co_2 = m_fbc2, s1_gloc_2 = m_g2;
|
||||
s1_fb_co_2 = m_fbc2;
|
||||
s1_gloc_2 = m_g2;
|
||||
|
||||
seg_2 = new_seg_2;
|
||||
if (cut_end_before != new_seg_2) {
|
||||
BLI_insertlinkbefore(&e->shadow_segments, cut_end_before, new_seg_2);
|
||||
|
@ -821,13 +831,15 @@ static bool lineart_shadow_cast_onto_triangle(LineartData *ld,
|
|||
interp_v3_v3v3_db(t_gpos1, gpos1, gpos2, gat1);
|
||||
interp_v3_v3v3_db(t_fbc1, fbc1, fbc2, rat1);
|
||||
t_fbc1[3] = interpd(fbc2[3], fbc1[3], gat1);
|
||||
at1 = 0, trimmed1 = true;
|
||||
at1 = 0;
|
||||
trimmed1 = true;
|
||||
}
|
||||
if (at2 > 1) {
|
||||
interp_v3_v3v3_db(t_gpos2, gpos1, gpos2, gat2);
|
||||
interp_v3_v3v3_db(t_fbc2, fbc1, fbc2, rat2);
|
||||
t_fbc2[3] = interpd(fbc2[3], fbc1[3], gat2);
|
||||
at2 = 1, trimmed2 = true;
|
||||
at2 = 1;
|
||||
trimmed2 = true;
|
||||
}
|
||||
}
|
||||
if (trimmed1) {
|
||||
|
|
|
@ -141,7 +141,9 @@ GPU_INLINE void *GPU_vertbuf_raw_step(GPUVertBufRaw *a)
|
|||
{
|
||||
unsigned char *data = a->data;
|
||||
a->data += a->stride;
|
||||
#ifdef DEBUG
|
||||
BLI_assert(data < a->_data_end);
|
||||
#endif
|
||||
return (void *)data;
|
||||
}
|
||||
|
||||
|
|
|
@ -1236,6 +1236,11 @@ mat3 MAT3x3(
|
|||
{
|
||||
return mat3(vec3(a1, a2, a3), vec3(b1, b2, b3), vec3(c1, c2, c3));
|
||||
}
|
||||
mat3 MAT3x3(
|
||||
vec3 a, float b1, float b2, float b3, float c1, float c2, float c3)
|
||||
{
|
||||
return mat3(a, vec3(b1, b2, b3), vec3(c1, c2, c3));
|
||||
}
|
||||
mat3 MAT3x3(float f)
|
||||
{
|
||||
return mat3(f);
|
||||
|
|
|
@ -15,7 +15,15 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct AnonymousAttributeID;
|
||||
/** Workaround to forward-declare C++ type in C header. */
|
||||
#ifdef __cplusplus
|
||||
namespace blender::bke {
|
||||
class AnonymousAttributeID;
|
||||
} // namespace blender::bke
|
||||
using AnonymousAttributeIDHandle = blender::bke::AnonymousAttributeID;
|
||||
#else
|
||||
typedef struct AnonymousAttributeIDHandle AnonymousAttributeIDHandle;
|
||||
#endif
|
||||
|
||||
/** Descriptor and storage for a custom data layer. */
|
||||
typedef struct CustomDataLayer {
|
||||
|
@ -40,12 +48,10 @@ typedef struct CustomDataLayer {
|
|||
/** Layer data. */
|
||||
void *data;
|
||||
/**
|
||||
* Run-time identifier for this layer. If no one has a strong reference to this id anymore,
|
||||
* the layer can be removed. The custom data layer only has a weak reference to the id, because
|
||||
* otherwise there will always be a strong reference and the attribute can't be removed
|
||||
* automatically.
|
||||
* Run-time identifier for this layer. Can be used to retrieve information about where this
|
||||
* attribute was created.
|
||||
*/
|
||||
const struct AnonymousAttributeID *anonymous_id;
|
||||
const AnonymousAttributeIDHandle *anonymous_id;
|
||||
} CustomDataLayer;
|
||||
|
||||
#define MAX_CUSTOMDATA_LAYER_NAME 64
|
||||
|
|
|
@ -65,8 +65,7 @@ typedef struct MovieClip {
|
|||
|
||||
/** Sequence or movie. */
|
||||
int source;
|
||||
/** Last accessed frame number. */
|
||||
int lastframe;
|
||||
int _pad;
|
||||
/** Size of last accessed frame. */
|
||||
int lastsize[2];
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ DEF_ENUM(rna_enum_operator_type_flag_items)
|
|||
DEF_ENUM(rna_enum_operator_return_items)
|
||||
DEF_ENUM(rna_enum_operator_property_tags)
|
||||
|
||||
DEF_ENUM(rna_enum_brush_automasking_flag_items)
|
||||
DEF_ENUM(rna_enum_brush_sculpt_tool_items)
|
||||
DEF_ENUM(rna_enum_brush_uv_sculpt_tool_items)
|
||||
DEF_ENUM(rna_enum_brush_vertex_tool_items)
|
||||
|
|
|
@ -94,7 +94,7 @@ static const EnumPropertyItem rna_enum_brush_texture_slot_map_texture_mode_items
|
|||
/* clang-format off */
|
||||
/* Note: we don't actually turn these into a single enum bit-mask property,
|
||||
* instead we construct individual boolean properties. */
|
||||
const EnumPropertyItem RNA_automasking_flags[] = {
|
||||
const EnumPropertyItem rna_enum_brush_automasking_flag_items[] = {
|
||||
{BRUSH_AUTOMASKING_TOPOLOGY, "use_automasking_topology", 0,"Topology", "Affect only vertices connected to the active vertex under the brush"},
|
||||
{BRUSH_AUTOMASKING_FACE_SETS, "use_automasking_face_sets", 0,"Face Sets", "Affect only vertices that share Face Sets with the active vertex"},
|
||||
{BRUSH_AUTOMASKING_BOUNDARY_EDGES, "use_automasking_boundary_edges", 0,"Mesh Boundary Auto-Masking", "Do not affect non manifold boundary edges"},
|
||||
|
@ -3204,7 +3204,7 @@ static void rna_def_brush(BlenderRNA *brna)
|
|||
"When locked keep using the plane origin of surface where stroke was initiated");
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_update");
|
||||
|
||||
const EnumPropertyItem *entry = RNA_automasking_flags;
|
||||
const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items;
|
||||
do {
|
||||
prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", entry->value);
|
||||
|
|
|
@ -476,7 +476,7 @@ const EnumPropertyItem rna_enum_bake_save_mode_items[] = {
|
|||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
const EnumPropertyItem rna_enum_bake_view_from_items[] = {
|
||||
static const EnumPropertyItem rna_enum_bake_view_from_items[] = {
|
||||
{R_BAKE_VIEW_FROM_ABOVE_SURFACE,
|
||||
"ABOVE_SURFACE",
|
||||
0,
|
||||
|
@ -7378,12 +7378,14 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
|
|||
RNA_def_property_range(prop, 0, INT_MAX);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
RNA_def_property_flag(prop, PROP_ANIMATABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "taa_render_samples", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Render Samples", "Number of samples per pixel for rendering");
|
||||
RNA_def_property_range(prop, 1, INT_MAX);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
RNA_def_property_flag(prop, PROP_ANIMATABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "use_taa_reprojection", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_TAA_REPROJECTION);
|
||||
|
@ -7393,6 +7395,7 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
|
|||
"(can leave some ghosting)");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
RNA_def_property_flag(prop, PROP_ANIMATABLE);
|
||||
|
||||
/* Screen Space Subsurface Scattering */
|
||||
prop = RNA_def_property(srna, "sss_samples", PROP_INT, PROP_NONE);
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
extern const EnumPropertyItem RNA_automasking_flags[];
|
||||
|
||||
const EnumPropertyItem rna_enum_particle_edit_hair_brush_items[] = {
|
||||
{PE_BRUSH_COMB, "COMB", 0, "Comb", "Comb hairs"},
|
||||
{PE_BRUSH_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth hairs"},
|
||||
|
@ -864,7 +862,7 @@ static void rna_def_sculpt(BlenderRNA *brna)
|
|||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update");
|
||||
|
||||
const EnumPropertyItem *entry = RNA_automasking_flags;
|
||||
const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items;
|
||||
do {
|
||||
prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", entry->value);
|
||||
|
|
|
@ -140,7 +140,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
|
|||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = {
|
||||
static const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = {
|
||||
{GPU_BACKEND_OPENGL, "OPENGL", 0, "OpenGL", "Use OpenGL backend"},
|
||||
{GPU_BACKEND_METAL, "METAL", 0, "Metal", "Use Metal backend"},
|
||||
{GPU_BACKEND_VULKAN, "VULKAN", 0, "Vulkan", "Use Vulkan backend"},
|
||||
|
|
|
@ -884,12 +884,12 @@ static void find_side_effect_nodes_for_viewer_path(
|
|||
|
||||
/* Not only mark the viewer node as having side effects, but also all group nodes it is contained
|
||||
* in. */
|
||||
r_side_effect_nodes.add(compute_context_builder.hash(),
|
||||
&find_viewer_lf_node(*found_viewer_node));
|
||||
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
|
||||
&find_viewer_lf_node(*found_viewer_node));
|
||||
compute_context_builder.pop();
|
||||
while (!compute_context_builder.is_empty()) {
|
||||
r_side_effect_nodes.add(compute_context_builder.hash(),
|
||||
&find_group_lf_node(*group_node_stack.pop()));
|
||||
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
|
||||
&find_group_lf_node(*group_node_stack.pop()));
|
||||
compute_context_builder.pop();
|
||||
}
|
||||
}
|
||||
|
@ -1124,12 +1124,11 @@ static GeometrySet compute_geometry(
|
|||
{
|
||||
const blender::nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
|
||||
|
||||
Span<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
|
||||
Vector<const lf::InputSocket *> graph_outputs;
|
||||
for (const bNodeSocket *bsocket : output_node.input_sockets().drop_back(1)) {
|
||||
const lf::InputSocket &socket = mapping.dummy_socket_map.lookup(bsocket)->as_input();
|
||||
graph_outputs.append(&socket);
|
||||
}
|
||||
Vector<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
|
||||
graph_inputs.extend(mapping.group_output_used_sockets);
|
||||
graph_inputs.extend(mapping.attribute_set_by_geometry_output.values().begin(),
|
||||
mapping.attribute_set_by_geometry_output.values().end());
|
||||
Vector<const lf::InputSocket *> graph_outputs = mapping.standard_group_output_sockets;
|
||||
|
||||
Array<GMutablePointer> param_inputs(graph_inputs.size());
|
||||
Array<GMutablePointer> param_outputs(graph_outputs.size());
|
||||
|
@ -1171,21 +1170,36 @@ static GeometrySet compute_geometry(
|
|||
|
||||
Vector<GMutablePointer> inputs_to_destruct;
|
||||
|
||||
int input_index;
|
||||
LISTBASE_FOREACH_INDEX (bNodeSocket *, interface_socket, &btree.inputs, input_index) {
|
||||
if (interface_socket->type == SOCK_GEOMETRY && input_index == 0) {
|
||||
int input_index = -1;
|
||||
for (const int i : btree.interface_inputs().index_range()) {
|
||||
input_index++;
|
||||
const bNodeSocket &interface_socket = *btree.interface_inputs()[i];
|
||||
if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) {
|
||||
param_inputs[input_index] = &input_geometry_set;
|
||||
continue;
|
||||
}
|
||||
|
||||
const CPPType *type = interface_socket->typeinfo->geometry_nodes_cpp_type;
|
||||
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
|
||||
BLI_assert(type != nullptr);
|
||||
void *value = allocator.allocate(type->size(), type->alignment());
|
||||
initialize_group_input(*nmd, *interface_socket, input_index, value);
|
||||
initialize_group_input(*nmd, interface_socket, i, value);
|
||||
param_inputs[input_index] = {type, value};
|
||||
inputs_to_destruct.append({type, value});
|
||||
}
|
||||
|
||||
Array<bool> output_used_inputs(btree.interface_outputs().size(), true);
|
||||
for (const int i : btree.interface_outputs().index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &output_used_inputs[i];
|
||||
}
|
||||
|
||||
Array<blender::bke::AnonymousAttributeSet> attributes_to_propagate(
|
||||
mapping.attribute_set_by_geometry_output.size());
|
||||
for (const int i : attributes_to_propagate.index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &attributes_to_propagate[i];
|
||||
}
|
||||
|
||||
for (const int i : graph_outputs.index_range()) {
|
||||
const lf::InputSocket &socket = *graph_outputs[i];
|
||||
const CPPType &type = socket.type();
|
||||
|
|
|
@ -19,6 +19,8 @@ struct ModifierData;
|
|||
namespace blender::nodes {
|
||||
|
||||
using bke::AnonymousAttributeFieldInput;
|
||||
using bke::AnonymousAttributeID;
|
||||
using bke::AnonymousAttributePropagationInfo;
|
||||
using bke::AttributeAccessor;
|
||||
using bke::AttributeFieldInput;
|
||||
using bke::AttributeIDRef;
|
||||
|
@ -26,13 +28,12 @@ using bke::AttributeKind;
|
|||
using bke::AttributeMetaData;
|
||||
using bke::AttributeReader;
|
||||
using bke::AttributeWriter;
|
||||
using bke::AutoAnonymousAttributeID;
|
||||
using bke::GAttributeReader;
|
||||
using bke::GAttributeWriter;
|
||||
using bke::GSpanAttributeWriter;
|
||||
using bke::MutableAttributeAccessor;
|
||||
using bke::SpanAttributeWriter;
|
||||
using bke::StrongAnonymousAttributeID;
|
||||
using bke::WeakAnonymousAttributeID;
|
||||
using fn::Field;
|
||||
using fn::FieldContext;
|
||||
using fn::FieldEvaluator;
|
||||
|
@ -43,15 +44,38 @@ using fn::ValueOrField;
|
|||
using geo_eval_log::NamedAttributeUsage;
|
||||
using geo_eval_log::NodeWarningType;
|
||||
|
||||
/**
|
||||
* An anonymous attribute created by a node.
|
||||
*/
|
||||
class NodeAnonymousAttributeID : public AnonymousAttributeID {
|
||||
std::string long_name_;
|
||||
|
||||
public:
|
||||
NodeAnonymousAttributeID(const Object &object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &bnode,
|
||||
const StringRef identifier);
|
||||
};
|
||||
|
||||
class GeoNodeExecParams {
|
||||
private:
|
||||
const bNode &node_;
|
||||
lf::Params ¶ms_;
|
||||
const lf::Context &lf_context_;
|
||||
const Map<StringRef, int> &lf_input_for_output_bsocket_usage_;
|
||||
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output_;
|
||||
|
||||
public:
|
||||
GeoNodeExecParams(const bNode &node, lf::Params ¶ms, const lf::Context &lf_context)
|
||||
: node_(node), params_(params), lf_context_(lf_context)
|
||||
GeoNodeExecParams(const bNode &node,
|
||||
lf::Params ¶ms,
|
||||
const lf::Context &lf_context,
|
||||
const Map<StringRef, int> &lf_input_for_output_bsocket_usage,
|
||||
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output)
|
||||
: node_(node),
|
||||
params_(params),
|
||||
lf_context_(lf_context),
|
||||
lf_input_for_output_bsocket_usage_(lf_input_for_output_bsocket_usage),
|
||||
lf_input_for_attribute_propagation_to_output_(lf_input_for_attribute_propagation_to_output)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -245,6 +269,49 @@ class GeoNodeExecParams {
|
|||
|
||||
void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
|
||||
|
||||
/**
|
||||
* Return true when the anonymous attribute referenced by the given output should be created.
|
||||
*/
|
||||
bool anonymous_attribute_output_is_required(const StringRef output_identifier)
|
||||
{
|
||||
const int lf_index = lf_input_for_output_bsocket_usage_.lookup(output_identifier);
|
||||
return params_.get_input<bool>(lf_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new anonymous attribute id for the given output. None is returned if the anonymous
|
||||
* attribute is not needed.
|
||||
*/
|
||||
AutoAnonymousAttributeID get_output_anonymous_attribute_id_if_needed(
|
||||
const StringRef output_identifier)
|
||||
{
|
||||
if (!this->anonymous_attribute_output_is_required(output_identifier)) {
|
||||
return {};
|
||||
}
|
||||
const GeoNodesLFUserData &user_data = *this->user_data();
|
||||
const ComputeContext &compute_context = *user_data.compute_context;
|
||||
return MEM_new<NodeAnonymousAttributeID>(__func__,
|
||||
*user_data.modifier_data->self_object,
|
||||
compute_context,
|
||||
node_,
|
||||
output_identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about which anonymous attributes should be propagated to the given output.
|
||||
*/
|
||||
AnonymousAttributePropagationInfo get_output_propagation_info(
|
||||
const StringRef output_identifier) const
|
||||
{
|
||||
const int lf_index = lf_input_for_attribute_propagation_to_output_.lookup(output_identifier);
|
||||
const bke::AnonymousAttributeSet &set = params_.get_input<bke::AnonymousAttributeSet>(
|
||||
lf_index);
|
||||
AnonymousAttributePropagationInfo info;
|
||||
info.names = set.names;
|
||||
info.propagate_all = false;
|
||||
return info;
|
||||
}
|
||||
|
||||
private:
|
||||
/* Utilities for detecting common errors at when using this class. */
|
||||
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
|
||||
|
|
|
@ -78,6 +78,28 @@ struct GeoNodesLFUserData : public lf::UserData {
|
|||
bool log_socket_values = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* In the general case, this is #DynamicSocket. That means that to determine if a node group will
|
||||
* use a particular input, it has to be partially executed.
|
||||
*
|
||||
* In other cases, it's not necessary to look into the node group to determine if an input is
|
||||
* necessary.
|
||||
*/
|
||||
enum class InputUsageHintType {
|
||||
/** The input socket is never used. */
|
||||
Never,
|
||||
/** The input socket is used when a subset of the outputs is used. */
|
||||
DependsOnOutput,
|
||||
/** Can't determine statically if the input is used, check the corresponding output socket. */
|
||||
DynamicSocket,
|
||||
};
|
||||
|
||||
struct InputUsageHint {
|
||||
InputUsageHintType type = InputUsageHintType::DependsOnOutput;
|
||||
/** Used in depends-on-output mode. */
|
||||
Vector<int> output_dependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains the mapping between the #bNodeTree and the corresponding lazy-function graph.
|
||||
* This is *not* a one-to-one mapping.
|
||||
|
@ -91,7 +113,32 @@ struct GeometryNodeLazyFunctionGraphMapping {
|
|||
* The inputs sockets in the graph. Multiple group input nodes are combined into one in the
|
||||
* lazy-function graph.
|
||||
*/
|
||||
Vector<lf::OutputSocket *> group_input_sockets;
|
||||
Vector<const lf::OutputSocket *> group_input_sockets;
|
||||
/**
|
||||
* Dummy output sockets that correspond to the active group output node. If there is no such
|
||||
* node, defaulted fallback outputs are created.
|
||||
*/
|
||||
Vector<const lf::InputSocket *> standard_group_output_sockets;
|
||||
/**
|
||||
* Dummy boolean sockets that have to be passed in from the outside and indicate whether a
|
||||
* specific output will be used.
|
||||
*/
|
||||
Vector<const lf::OutputSocket *> group_output_used_sockets;
|
||||
/**
|
||||
* Dummy boolean sockets that can be used as group output that indicate whether a specific input
|
||||
* will be used (this may depend on the used outputs as well as other inputs).
|
||||
*/
|
||||
Vector<const lf::InputSocket *> group_input_usage_sockets;
|
||||
/**
|
||||
* This is an optimization to avoid partially evaluating a node group just to figure out which
|
||||
* inputs are needed.
|
||||
*/
|
||||
Vector<InputUsageHint> group_input_usage_hints;
|
||||
/**
|
||||
* If the node group propagates attributes from an input geometry to the output, it has to know
|
||||
* which attributes should be propagated and which can be removed (for optimization purposes).
|
||||
*/
|
||||
Map<int, const lf::OutputSocket *> attribute_set_by_geometry_output;
|
||||
/**
|
||||
* A mapping used for logging intermediate values.
|
||||
*/
|
||||
|
|
|
@ -50,10 +50,10 @@ Mesh *create_grid_mesh(
|
|||
int verts_x, int verts_y, float size_x, float size_y, const AttributeIDRef &uv_map_id);
|
||||
|
||||
struct ConeAttributeOutputs {
|
||||
StrongAnonymousAttributeID top_id;
|
||||
StrongAnonymousAttributeID bottom_id;
|
||||
StrongAnonymousAttributeID side_id;
|
||||
StrongAnonymousAttributeID uv_map_id;
|
||||
AutoAnonymousAttributeID top_id;
|
||||
AutoAnonymousAttributeID bottom_id;
|
||||
AutoAnonymousAttributeID side_id;
|
||||
AutoAnonymousAttributeID uv_map_id;
|
||||
};
|
||||
|
||||
Mesh *create_cylinder_or_cone_mesh(float radius_top,
|
||||
|
@ -81,6 +81,7 @@ void separate_geometry(GeometrySet &geometry_set,
|
|||
eAttrDomain domain,
|
||||
GeometryNodeDeleteGeometryMode mode,
|
||||
const Field<bool> &selection_field,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
bool &r_is_error);
|
||||
|
||||
void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue