Compare commits
324 Commits
virtual-ar
...
geometry-n
Author | SHA1 | Date | |
---|---|---|---|
7030e3a016 | |||
936dfae3de | |||
7e17dbfcf3 | |||
e032ca2e25 | |||
399aed9a81 | |||
a7bda03516 | |||
607cd463b3 | |||
49b3d00c10 | |||
eb0d680851 | |||
38bfa8902b | |||
c266632ff6 | |||
d89d53b453 | |||
b67fe05d4b | |||
bac9562f15 | |||
8f03c9b19a | |||
e12a8aedce | |||
94960250b5 | |||
d7c762d2f7 | |||
![]() |
a65d5dadeb | ||
9b64927a8a | |||
a17ea1a669 | |||
c63142ca8f | |||
e7c1702ccb | |||
a200a8cf59 | |||
149078d580 | |||
36c4b79c30 | |||
f2d70c02f8 | |||
47b1f9a4d3 | |||
8709d0cd5d | |||
![]() |
5341482064 | ||
c76141e425 | |||
db3b610040 | |||
c204e0c385 | |||
46bfc3ec46 | |||
a29b9a73d2 | |||
39f21c2475 | |||
87036f5eeb | |||
16b2b33d01 | |||
9636e79c5a | |||
e1c04da36f | |||
45bac491eb | |||
20142b0823 | |||
![]() |
f1cf706758 | ||
ae58db91a2 | |||
87a70801c6 | |||
05dddb71b0 | |||
837d630466 | |||
23eb05eba5 | |||
3a6f481d5a | |||
c96f9bd340 | |||
f7afd78b80 | |||
9cce18a585 | |||
82328797cf | |||
b6542f71e1 | |||
f8d5d03ebc | |||
b06d9069ad | |||
c2a0ca622b | |||
536a2fe7cc | |||
86ff35c7fc | |||
0f1ee611d4 | |||
17fca62fff | |||
aa95f8019e | |||
12951aecf6 | |||
2e513afe80 | |||
e4cebec647 | |||
be68d00c36 | |||
17afa86336 | |||
![]() |
c463675188 | ||
8aa8537632 | |||
5e509f966f | |||
23185262ab | |||
877711e9e4 | |||
8adeac7c27 | |||
9afa738542 | |||
361579c86a | |||
5441f5fc90 | |||
4cb8438e08 | |||
f8e1526fa6 | |||
ffa8563429 | |||
![]() |
425e19bc1f | ||
![]() |
2f6219c48d | ||
dc1e98d8a0 | |||
cfa20ff03b | |||
8b049e4c2a | |||
5a33965627 | |||
0817763624 | |||
76fcf58cdd | |||
a23e49c696 | |||
3cb09f7a83 | |||
46a037707b | |||
f2948faf97 | |||
47892d6695 | |||
6ebe0b8cf0 | |||
d7aa481231 | |||
cd07fb6477 | |||
bbb52a462e | |||
d0bdb2b48f | |||
35b0d177a2 | |||
441f8d98eb | |||
c21199a0ba | |||
26d4864ee6 | |||
d1ccc5b969 | |||
f240b5e5f7 | |||
9fccbe2d13 | |||
b9a7b40924 | |||
e0852368bc | |||
f6efacfec7 | |||
80536e8bae | |||
c3701e2303 | |||
0ba0d27d36 | |||
8c146d5efc | |||
71db02ed88 | |||
1095d5964d | |||
7192ab614b | |||
91c652e39a | |||
33d9a0c951 | |||
00ec99050e | |||
a4bd0fcabf | |||
a43d644dec | |||
6944521d7e | |||
4554f27adf | |||
da443d82ee | |||
48c5129d1f | |||
330c14c26c | |||
932b23782a | |||
0f66dbea90 | |||
19360c2c1c | |||
ef9551afd1 | |||
aed9b6faee | |||
3da74c1c18 | |||
1dd17726f2 | |||
d5309bf4cf | |||
b9cbf7fc80 | |||
9fa9854e2a | |||
b1fcc8e6ba | |||
7aa6444a65 | |||
e9e2805573 | |||
0b458e8322 | |||
2e3f072d5d | |||
b72d9216f1 | |||
183e3f6bb9 | |||
d17bff849d | |||
26ab90e49b | |||
5fe3d2dc7d | |||
1a6b51e175 | |||
79d2f2c2f9 | |||
f9867c1f11 | |||
a6cd20716e | |||
06c682423a | |||
3003fbbecf | |||
5da3177190 | |||
c5cabb6eb7 | |||
557b3d2762 | |||
34542eede5 | |||
2c73e16d1c | |||
847579b422 | |||
f2626f1420 | |||
3735986e87 | |||
f1b61f5e7d | |||
e2af5030b9 | |||
3d994b26ba | |||
2c1a2c9a99 | |||
fa7ddd0f43 | |||
4cf60a2abf | |||
eca5cf1460 | |||
26d778cd8a | |||
e1a9ba94c5 | |||
7834fcc67d | |||
2125ee4305 | |||
b2c6eb8640 | |||
27cfc1ea11 | |||
2efd3509ee | |||
db0b1cab1f | |||
8c0f7d1772 | |||
5d2ec2bc08 | |||
8c5c55b8c9 | |||
5e82e77112 | |||
b760481f8d | |||
72fec0f7c5 | |||
7ccd19fc12 | |||
ebe04bd3ca | |||
985ccba77c | |||
dd886fcc6d | |||
6b44605cec | |||
795f024558 | |||
0566ebdebe | |||
2d1d6fbb23 | |||
d1fbf1599f | |||
b42454be8b | |||
68cbf0a2be | |||
cedd8b8c56 | |||
c13b3dd168 | |||
d7caae56c4 | |||
2dc5961874 | |||
b8b7b47a00 | |||
458cbcbfea | |||
6f761c7110 | |||
8032bd98d8 | |||
f99f884444 | |||
feedf28549 | |||
4402c2324b | |||
e1acefd45e | |||
46a13482cb | |||
33440760a6 | |||
8245805ce3 | |||
33f218fa3f | |||
2e76c3565d | |||
eb06ccc324 | |||
c75b2019e1 | |||
![]() |
799f532f46 | ||
112fb77157 | |||
1d96493c2f | |||
969cc22a89 | |||
eae39a4973 | |||
6dffdb02fa | |||
3b4b231be5 | |||
0b903755a9 | |||
9175911ffa | |||
0af28f007f | |||
c3b5378978 | |||
950d8360f8 | |||
645fa86b7d | |||
e43428eba3 | |||
1825e67dea | |||
c8dd684b65 | |||
6879868814 | |||
063c9938f1 | |||
52cb657e91 | |||
a80edb8865 | |||
9a69653b42 | |||
06888a8970 | |||
76eae59648 | |||
01448ee7ce | |||
8d30a7a1cf | |||
4bce9c5283 | |||
1544bcfc37 | |||
4e1507bd3b | |||
1fc446a908 | |||
aca9a1bac3 | |||
ebcf49fe1a | |||
2d5a715f44 | |||
3516ee5a14 | |||
ddbfae7a0d | |||
1a010450bc | |||
eff2b89446 | |||
686452fb1b | |||
e4990a5658 | |||
fa2c00ae91 | |||
d0d85742fc | |||
5cf6f570c6 | |||
4dca44086f | |||
d9224f64a1 | |||
3608891282 | |||
b500099e5c | |||
c9c295ef25 | |||
d64dea8a7e | |||
476006ae64 | |||
51e72e4703 | |||
ba12548f24 | |||
a6f2da1caf | |||
91b44ebb52 | |||
0d203862ca | |||
371720cb6d | |||
be1a41878c | |||
53b2a1a0e3 | |||
d9cb1fa7dc | |||
e7516174c7 | |||
98c908d6f6 | |||
fcb7d2fcba | |||
d0b2ddefa3 | |||
bb04d1c2c8 | |||
839c796321 | |||
db731b0beb | |||
6cf3878765 | |||
1975dc96d1 | |||
fa83dbda06 | |||
d0b0cb5982 | |||
da3004a0f9 | |||
ec090a215b | |||
797331db86 | |||
652ee81662 | |||
b5bd676a51 | |||
b490e0838c | |||
7c77d769dd | |||
48316b7e08 | |||
ff34a3a612 | |||
583bc7a05d | |||
2a96dc80b0 | |||
2cdec6cbf4 | |||
f8604f18e8 | |||
4f0e6c3aa3 | |||
f3775ca2cf | |||
5e013d47a1 | |||
2f2bedafa1 | |||
b90aeb0a44 | |||
29e24974a6 | |||
c752884d0d | |||
797ff2f064 | |||
24d32a796a | |||
d1519df6a8 | |||
457e1f9d1f | |||
85165058ef | |||
54188678b4 | |||
073bf63a9d | |||
f91df2e3e3 | |||
c95b2cefab | |||
53f92279cb | |||
b3fc6ac07a | |||
968977d684 | |||
f49a499129 | |||
140828037b | |||
188e4d302c | |||
cc06b059c2 | |||
0c64850e43 | |||
d64e149a4f | |||
09846cdce8 | |||
a4ad4bc8d3 | |||
2622882d83 | |||
b4beba7f1c | |||
58cdb78ea0 | |||
25e95ccb1a | |||
9e91aea40a | |||
5f03931fc4 | |||
e1cef1b85d |
@@ -1526,6 +1526,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_UNDEF -Wundef)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_FORMAT_SIGN -Wformat-signedness)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_RESTRICT -Wrestrict)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SUGGEST_OVERRIDE -Wno-suggest-override)
|
||||
|
||||
# gcc 4.2 gives annoying warnings on every file with this
|
||||
if(NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "4.3")
|
||||
@@ -1589,6 +1590,8 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_OVERLOADED_VIRTUAL -Wno-overloaded-virtual) # we get a lot of these, if its a problem a dev needs to look into it.
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SIGN_COMPARE -Wno-sign-compare)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_INVALID_OFFSETOF -Wno-invalid-offsetof)
|
||||
# Apple Clang (tested on version 12) doesn't support this flag while LLVM Clang 11 does.
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SUGGEST_OVERRIDE -Wno-suggest-override)
|
||||
|
||||
# gives too many unfixable warnings
|
||||
# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_MACROS -Wunused-macros)
|
||||
|
@@ -113,7 +113,7 @@ include(cmake/expat.cmake)
|
||||
include(cmake/yamlcpp.cmake)
|
||||
include(cmake/opencolorio.cmake)
|
||||
|
||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
include(cmake/sse2neon.cmake)
|
||||
endif()
|
||||
|
||||
|
@@ -18,6 +18,12 @@
|
||||
|
||||
set(BOOST_ADDRESS_MODEL 64)
|
||||
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
set(BOOST_ARCHITECTURE arm)
|
||||
else()
|
||||
set(BOOST_ARCHITECTURE x86)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(BOOST_TOOLSET toolset=msvc-14.1)
|
||||
set(BOOST_COMPILER_STRING -vc141)
|
||||
@@ -29,7 +35,6 @@ if(WIN32)
|
||||
if(BUILD_MODE STREQUAL Release)
|
||||
set(BOOST_HARVEST_CMD ${BOOST_HARVEST_CMD} && ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/boost/include/boost-${BOOST_VERSION_NODOTS_SHORT}/ ${HARVEST_TARGET}/boost/include/)
|
||||
endif()
|
||||
|
||||
elseif(APPLE)
|
||||
set(BOOST_CONFIGURE_COMMAND ./bootstrap.sh)
|
||||
set(BOOST_BUILD_COMMAND ./b2)
|
||||
@@ -93,7 +98,7 @@ ExternalProject_Add(external_boost
|
||||
UPDATE_COMMAND ""
|
||||
PATCH_COMMAND ${BOOST_PATCH_COMMAND}
|
||||
CONFIGURE_COMMAND ${BOOST_CONFIGURE_COMMAND}
|
||||
BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=x86 address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
|
||||
BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=${BOOST_ARCHITECTURE} address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND "${BOOST_HARVEST_CMD}"
|
||||
)
|
||||
|
@@ -47,7 +47,7 @@ else()
|
||||
set(EMBREE_BUILD_DIR)
|
||||
endif()
|
||||
|
||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
ExternalProject_Add(external_embree
|
||||
GIT_REPOSITORY ${EMBREE_ARM_GIT}
|
||||
GIT_TAG "blender-arm"
|
||||
|
@@ -25,19 +25,12 @@ else()
|
||||
set(GMP_OPTIONS --enable-static --disable-shared )
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
|
||||
set(GMP_OPTIONS
|
||||
${GMP_OPTIONS}
|
||||
--disable-assembly
|
||||
)
|
||||
else()
|
||||
set(GMP_OPTIONS
|
||||
${GMP_OPTIONS}
|
||||
--with-pic
|
||||
)
|
||||
endif()
|
||||
elseif(UNIX)
|
||||
if(APPLE AND NOT BLENDER_PLATFORM_ARM)
|
||||
set(GMP_OPTIONS
|
||||
${GMP_OPTIONS}
|
||||
--with-pic
|
||||
)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
set(GMP_OPTIONS
|
||||
${GMP_OPTIONS}
|
||||
--with-pic
|
||||
@@ -45,6 +38,13 @@ elseif(UNIX)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
set(GMP_OPTIONS
|
||||
${GMP_OPTIONS}
|
||||
--disable-assembly
|
||||
)
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(external_gmp
|
||||
URL file://${PACKAGE_DIR}/${GMP_FILE}
|
||||
DOWNLOAD_DIR ${DOWNLOAD_DIR}
|
||||
|
@@ -109,9 +109,9 @@ harvest(llvm/lib llvm/lib "libclang*.a")
|
||||
if(APPLE)
|
||||
harvest(openmp/lib openmp/lib "*")
|
||||
harvest(openmp/include openmp/include "*.h")
|
||||
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
|
||||
harvest(sse2neon sse2neon "*.h")
|
||||
endif()
|
||||
endif()
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
harvest(sse2neon sse2neon "*.h")
|
||||
endif()
|
||||
harvest(ogg/lib ffmpeg/lib "*.a")
|
||||
harvest(openal/include openal/include "*.h")
|
||||
|
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
set(LLVM_TARGETS AArch64$<SEMICOLON>ARM)
|
||||
else()
|
||||
set(LLVM_TARGETS X86)
|
||||
|
@@ -36,7 +36,7 @@ set(OPENCOLORIO_EXTRA_ARGS
|
||||
-Dyaml-cpp_ROOT=${LIBDIR}/yamlcpp
|
||||
)
|
||||
|
||||
if(APPLE AND NOT("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64"))
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
set(OPENCOLORIO_EXTRA_ARGS
|
||||
${OPENCOLORIO_EXTRA_ARGS}
|
||||
-DOCIO_USE_SSE=OFF
|
||||
|
@@ -137,6 +137,10 @@ else()
|
||||
endif()
|
||||
set(OSX_SYSROOT ${XCODE_DEV_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk)
|
||||
|
||||
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
|
||||
set(BLENDER_PLATFORM_ARM ON)
|
||||
endif()
|
||||
|
||||
set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
|
||||
set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++ -arch ${CMAKE_OSX_ARCHITECTURES}")
|
||||
set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
|
||||
@@ -151,6 +155,10 @@ else()
|
||||
-DCMAKE_OSX_SYSROOT:PATH=${OSX_SYSROOT}
|
||||
)
|
||||
else()
|
||||
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
|
||||
set(BLENDER_PLATFORM_ARM ON)
|
||||
endif()
|
||||
|
||||
set(PLATFORM_CFLAGS "-fPIC")
|
||||
set(PLATFORM_CXXFLAGS "-std=c++11 -fPIC")
|
||||
set(PLATFORM_LDFLAGS)
|
||||
|
@@ -22,8 +22,8 @@ set(PNG_EXTRA_ARGS
|
||||
-DPNG_STATIC=ON
|
||||
)
|
||||
|
||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||
set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=on -DCMAKE_SYSTEM_PROCESSOR="aarch64")
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR="aarch64")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(external_png
|
||||
|
@@ -16,15 +16,13 @@
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||
ExternalProject_Add(external_sse2neon
|
||||
GIT_REPOSITORY ${SSE2NEON_GIT}
|
||||
GIT_TAG ${SSE2NEON_GIT_HASH}
|
||||
DOWNLOAD_DIR ${DOWNLOAD_DIR}
|
||||
PREFIX ${BUILD_DIR}/sse2neon
|
||||
CONFIGURE_COMMAND echo sse2neon - Nothing to configure
|
||||
BUILD_COMMAND echo sse2neon - nothing to build
|
||||
INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
|
||||
INSTALL_DIR ${LIBDIR}/sse2neon
|
||||
)
|
||||
endif()
|
||||
ExternalProject_Add(external_sse2neon
|
||||
GIT_REPOSITORY ${SSE2NEON_GIT}
|
||||
GIT_TAG ${SSE2NEON_GIT_HASH}
|
||||
DOWNLOAD_DIR ${DOWNLOAD_DIR}
|
||||
PREFIX ${BUILD_DIR}/sse2neon
|
||||
CONFIGURE_COMMAND echo sse2neon - Nothing to configure
|
||||
BUILD_COMMAND echo sse2neon - nothing to build
|
||||
INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
|
||||
INSTALL_DIR ${LIBDIR}/sse2neon
|
||||
)
|
||||
|
@@ -22,7 +22,9 @@ set(SSL_PATCH_CMD echo .)
|
||||
if(APPLE)
|
||||
set(SSL_OS_COMPILER "blender-darwin-${CMAKE_OSX_ARCHITECTURES}")
|
||||
else()
|
||||
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
set(SSL_OS_COMPILER "blender-linux-aarch64")
|
||||
elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(SSL_EXTRA_ARGS enable-ec_nistp_64_gcc_128)
|
||||
set(SSL_OS_COMPILER "blender-linux-x86_64")
|
||||
else()
|
||||
|
@@ -8,6 +8,11 @@ my %targets = (
|
||||
inherit_from => [ "linux-x86_64" ],
|
||||
cflags => add("-fPIC"),
|
||||
},
|
||||
"blender-linux-aarch64" => {
|
||||
inherit_from => [ "linux-aarch64" ],
|
||||
cxxflags => add("-fPIC"),
|
||||
cflags => add("-fPIC"),
|
||||
},
|
||||
"blender-darwin-x86_64" => {
|
||||
inherit_from => [ "darwin64-x86_64-cc" ],
|
||||
cflags => add("-fPIC"),
|
||||
|
@@ -21,6 +21,7 @@ if(WIN32)
|
||||
-DTBB_BUILD_TBBMALLOC=On
|
||||
-DTBB_BUILD_TBBMALLOC_PROXY=On
|
||||
-DTBB_BUILD_STATIC=Off
|
||||
-DTBB_BUILD_TESTS=Off
|
||||
)
|
||||
set(TBB_LIBRARY tbb)
|
||||
set(TBB_STATIC_LIBRARY Off)
|
||||
@@ -30,6 +31,7 @@ else()
|
||||
-DTBB_BUILD_TBBMALLOC=On
|
||||
-DTBB_BUILD_TBBMALLOC_PROXY=Off
|
||||
-DTBB_BUILD_STATIC=On
|
||||
-DTBB_BUILD_TESTS=Off
|
||||
)
|
||||
set(TBB_LIBRARY tbb_static)
|
||||
set(TBB_STATIC_LIBRARY On)
|
||||
@@ -42,7 +44,7 @@ ExternalProject_Add(external_tbb
|
||||
URL_HASH ${TBB_HASH_TYPE}=${TBB_HASH}
|
||||
PREFIX ${BUILD_DIR}/tbb
|
||||
PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/cmakelists_tbb.txt ${BUILD_DIR}/tbb/src/external_tbb/CMakeLists.txt &&
|
||||
${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/src/tbb/version_string.ver &&
|
||||
${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/build/version_string.ver.in &&
|
||||
${PATCH_CMD} -p 1 -d ${BUILD_DIR}/tbb/src/external_tbb < ${PATCH_DIR}/tbb.diff
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/tbb ${DEFAULT_CMAKE_FLAGS} ${TBB_EXTRA_ARGS}
|
||||
INSTALL_DIR ${LIBDIR}/tbb
|
||||
|
@@ -152,7 +152,7 @@ set(OPENCOLORIO_HASH 1a2e3478b6cd9a1549f24e1b2205e3f0)
|
||||
set(OPENCOLORIO_HASH_TYPE MD5)
|
||||
set(OPENCOLORIO_FILE OpenColorIO-${OPENCOLORIO_VERSION}.tar.gz)
|
||||
|
||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
# Newer version required by ISPC with arm support.
|
||||
set(LLVM_VERSION 11.0.1)
|
||||
set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz)
|
||||
@@ -398,11 +398,20 @@ set(LZMA_HASH 5117f930900b341493827d63aa910ff5e011e0b994197c3b71c08a20228a42df)
|
||||
set(LZMA_HASH_TYPE SHA256)
|
||||
set(LZMA_FILE xz-${LZMA_VERSION}.tar.bz2)
|
||||
|
||||
set(SSL_VERSION 1.1.1g)
|
||||
set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
|
||||
set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
|
||||
set(SSL_HASH_TYPE SHA256)
|
||||
set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
# Need at least 1.1.1i for aarch64 support (https://github.com/openssl/openssl/pull/13218)
|
||||
set(SSL_VERSION 1.1.1i)
|
||||
set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
|
||||
set(SSL_HASH e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242)
|
||||
set(SSL_HASH_TYPE SHA256)
|
||||
set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
|
||||
else()
|
||||
set(SSL_VERSION 1.1.1g)
|
||||
set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
|
||||
set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
|
||||
set(SSL_HASH_TYPE SHA256)
|
||||
set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
|
||||
endif()
|
||||
|
||||
set(SQLITE_VERSION 3.31.1)
|
||||
set(SQLITE_URI https://www.sqlite.org/2018/sqlite-src-3240000.zip)
|
||||
@@ -453,7 +462,7 @@ set(XR_OPENXR_SDK_HASH 0df6b2fd6045423451a77ff6bc3e1a75)
|
||||
set(XR_OPENXR_SDK_HASH_TYPE MD5)
|
||||
set(XR_OPENXR_SDK_FILE OpenXR-SDK-${XR_OPENXR_SDK_VERSION}.tar.gz)
|
||||
|
||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
# Unreleased version with macOS arm support.
|
||||
set(ISPC_URI https://github.com/ispc/ispc/archive/f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip)
|
||||
set(ISPC_HASH d382fea18d01dbd0cd05d9e1ede36d7d)
|
||||
|
@@ -20,24 +20,16 @@ if(WIN32)
|
||||
set(X264_EXTRA_ARGS --enable-win32thread --cross-prefix=${MINGW_HOST}- --host=${MINGW_HOST})
|
||||
endif()
|
||||
|
||||
|
||||
if(APPLE)
|
||||
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
|
||||
set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
|
||||
set(X264_CONFIGURE_ENV echo .)
|
||||
else()
|
||||
set(X264_CONFIGURE_ENV
|
||||
export AS=${LIBDIR}/nasm/bin/nasm
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
set(X264_CONFIGURE_ENV echo .)
|
||||
if(BLENDER_PLATFORM_ARM)
|
||||
set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if((APPLE AND NOT BLENDER_PLATFORM_ARM) OR (UNIX AND NOT APPLE))
|
||||
set(X264_CONFIGURE_ENV
|
||||
export AS=${LIBDIR}/nasm/bin/nasm
|
||||
)
|
||||
else()
|
||||
set(X264_CONFIGURE_ENV echo .)
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(external_x264
|
||||
|
@@ -1797,6 +1797,10 @@ compile_OCIO() {
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=OFF"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_GPU_TESTS=OFF"
|
||||
|
||||
if [ $(uname -m) == "aarch64" ]; then
|
||||
cmake_d="$cmake_d -D OCIO_USE_SSE=OFF"
|
||||
fi
|
||||
|
||||
if file /bin/cp | grep -q '32-bit'; then
|
||||
cflags="-fPIC -m32 -march=i686"
|
||||
else
|
||||
@@ -2059,7 +2063,10 @@ compile_OIIO() {
|
||||
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
|
||||
cmake_d="$cmake_d -D LINKSTATIC=OFF"
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
|
||||
if [ $(uname -m) != "aarch64" ]; then
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION"
|
||||
|
||||
@@ -2079,7 +2086,7 @@ compile_OIIO() {
|
||||
cmake_d="$cmake_d -D USE_OPENCV=OFF"
|
||||
cmake_d="$cmake_d -D BUILD_TESTING=OFF"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=ON"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF"
|
||||
cmake_d="$cmake_d -D TXT2MAN="
|
||||
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
|
||||
@@ -2209,10 +2216,15 @@ compile_LLVM() {
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
LLVM_TARGETS="X86"
|
||||
if [ $(uname -m) == "aarch64" ]; then
|
||||
LLVM_TARGETS="AArch64"
|
||||
fi
|
||||
|
||||
cmake_d="-D CMAKE_BUILD_TYPE=Release"
|
||||
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
cmake_d="$cmake_d -D LLVM_ENABLE_FFI=ON"
|
||||
cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=X86"
|
||||
cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=$LLVM_TARGETS"
|
||||
cmake_d="$cmake_d -D LLVM_ENABLE_TERMINFO=OFF"
|
||||
|
||||
if [ -d $_FFI_INCLUDE_DIR ]; then
|
||||
@@ -2329,13 +2341,16 @@ compile_OSL() {
|
||||
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
|
||||
cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF"
|
||||
cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF"
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
cmake_d="$cmake_d -D USE_LLVM_BITCODE=OFF"
|
||||
cmake_d="$cmake_d -D USE_PARTIO=OFF"
|
||||
cmake_d="$cmake_d -D OSL_BUILD_MATERIALX=OFF"
|
||||
cmake_d="$cmake_d -D USE_QT=OFF"
|
||||
cmake_d="$cmake_d -D USE_PYTHON=OFF"
|
||||
|
||||
if [ $(uname -m) != "aarch64" ]; then
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -D CMAKE_CXX_STANDARD=14"
|
||||
|
||||
#~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
|
||||
|
@@ -1,5 +1,32 @@
|
||||
cmake_minimum_required (VERSION 2.8)
|
||||
project(tbb CXX)
|
||||
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
|
||||
|
||||
if (POLICY CMP0048)
|
||||
# cmake warns if loaded from a min-3.0-required parent dir, so silence the warning:
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
endif()
|
||||
|
||||
project (tbb CXX)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCXXSourceRuns)
|
||||
|
||||
if(POLICY CMP0058)
|
||||
cmake_policy(SET CMP0058 NEW)
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0068)
|
||||
cmake_policy(SET CMP0068 NEW)
|
||||
endif()
|
||||
|
||||
if (POLICY CMP0078)
|
||||
# swig standard target names
|
||||
cmake_policy(SET CMP0078 NEW)
|
||||
endif ()
|
||||
|
||||
if (POLICY CMP0086)
|
||||
# UseSWIG honors SWIG_MODULE_NAME via -module flag
|
||||
cmake_policy(SET CMP0086 NEW)
|
||||
endif ()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to 'Release' as none was specified.")
|
||||
@@ -8,12 +35,36 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
include_directories(include src src/rml/include )
|
||||
if(NOT TBB_INSTALL_RUNTIME_DIR)
|
||||
set(TBB_INSTALL_RUNTIME_DIR bin)
|
||||
endif()
|
||||
if(NOT TBB_INSTALL_LIBRARY_DIR)
|
||||
set(TBB_INSTALL_LIBRARY_DIR lib)
|
||||
endif()
|
||||
if(NOT TBB_INSTALL_ARCHIVE_DIR)
|
||||
set(TBB_INSTALL_ARCHIVE_DIR lib)
|
||||
endif()
|
||||
if(NOT TBB_INSTALL_INCLUDE_DIR)
|
||||
set(TBB_INSTALL_INCLUDE_DIR include)
|
||||
endif()
|
||||
if(NOT TBB_CMAKE_PACKAGE_INSTALL_DIR)
|
||||
set(TBB_CMAKE_PACKAGE_INSTALL_DIR lib/cmake/tbb)
|
||||
endif()
|
||||
|
||||
include_directories(include src src/rml/include ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
option(TBB_BUILD_SHARED "Build TBB shared library" ON)
|
||||
option(TBB_BUILD_STATIC "Build TBB static library" ON)
|
||||
option(TBB_BUILD_TBBMALLOC "Build TBB malloc library" ON)
|
||||
option(TBB_BUILD_TBBMALLOC_PROXY "Build TBB malloc proxy library" ON)
|
||||
option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" ON)
|
||||
option(TBB_NO_DATE "Do not save the configure date in the version string" OFF)
|
||||
option(TBB_BUILD_PYTHON "Build TBB Python bindings" OFF)
|
||||
option(TBB_SET_SOVERSION "Set the SOVERSION (shared library build version suffix)?" OFF)
|
||||
|
||||
# When this repository is part of a larger build system of a parent project
|
||||
# we may not want TBB to set up default installation targets
|
||||
option(TBB_INSTALL_TARGETS "Include build targets for 'make install'" ON)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_MACOSX_RPATH ON)
|
||||
@@ -39,66 +90,143 @@ set(tbbmalloc_proxy_src
|
||||
src/tbbmalloc/proxy.cpp
|
||||
src/tbbmalloc/tbb_function_replacement.cpp)
|
||||
|
||||
if (NOT APPLE)
|
||||
add_definitions(-DDO_ITT_NOTIFY)
|
||||
else()
|
||||
# Disable annoying "has no symbols" warnings
|
||||
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
||||
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
||||
add_library (tbb_interface INTERFACE)
|
||||
add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(i386|x86_64)")
|
||||
if (NOT APPLE AND NOT MINGW)
|
||||
target_compile_definitions(tbb_interface INTERFACE DO_ITT_NOTIFY)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# Disable annoying "has no symbols" warnings
|
||||
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
||||
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
||||
endif()
|
||||
|
||||
macro(CHECK_CXX_COMPILER_AND_LINKER_FLAGS _RESULT _CXX_FLAGS _LINKER_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${_CXX_FLAGS})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${_LINKER_FLAGS})
|
||||
set(CMAKE_REQUIRED_QUIET TRUE)
|
||||
check_cxx_source_runs("#include <iostream>\nint main(int argc, char **argv) { std::cout << \"test\"; return 0; }" ${_RESULT})
|
||||
set(CMAKE_REQUIRED_FLAGS "")
|
||||
set(CMAKE_REQUIRED_LIBRARIES "")
|
||||
endmacro()
|
||||
|
||||
# Prefer libc++ in conjunction with Clang
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
if (CMAKE_CXX_FLAGS MATCHES "-stdlib=libc\\+\\+")
|
||||
message(STATUS "TBB: using libc++.")
|
||||
else()
|
||||
CHECK_CXX_COMPILER_AND_LINKER_FLAGS(HAS_LIBCPP "-stdlib=libc++" "-stdlib=libc++")
|
||||
if (HAS_LIBCPP)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -D_LIBCPP_VERSION")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")
|
||||
message(STATUS "TBB: using libc++.")
|
||||
else()
|
||||
message(STATUS "TBB: NOT using libc++.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
|
||||
if (UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DUSE_PTHREAD")
|
||||
if(NOT CMAKE_CXX_FLAGS MATCHES "-mno-rtm")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mrtm")
|
||||
endif()
|
||||
if (APPLE)
|
||||
set(ARCH_PREFIX "mac")
|
||||
else()
|
||||
set(ARCH_PREFIX "lin")
|
||||
endif()
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(ARCH_PREFIX "${ARCH_PREFIX}64")
|
||||
else()
|
||||
set(ARCH_PREFIX "${ARCH_PREFIX}32")
|
||||
endif()
|
||||
set(ENABLE_RTTI "-frtti -fexceptions ")
|
||||
set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
|
||||
elseif(WIN32)
|
||||
cmake_minimum_required (VERSION 3.1)
|
||||
enable_language(ASM_MASM)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS- /Zc:wchar_t /Zc:forScope /DUSE_WINTHREAD")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_CRT_SECURE_NO_DEPRECATE /D_WIN32_WINNT=0x0600 /volatile:iso")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4800 /wd4146 /wd4244 /wd4018")
|
||||
target_compile_definitions(tbb_interface INTERFACE USE_PTHREAD)
|
||||
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
|
||||
src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
|
||||
list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
|
||||
set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1")
|
||||
set(ARCH_PREFIX "win64")
|
||||
else()
|
||||
list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
|
||||
src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
|
||||
list(APPEND tbbmalloc_src src/tbb/ia32-masm/atomic_support.asm)
|
||||
set(ARCH_PREFIX "win32")
|
||||
endif()
|
||||
set(ENABLE_RTTI "/EHsc /GR ")
|
||||
set(DISABLE_RTTI "/EHs- /GR- ")
|
||||
check_cxx_compiler_flag ("-mrtm -Werror" SUPPORTS_MRTM)
|
||||
if (SUPPORTS_MRTM)
|
||||
target_compile_options(tbb_interface INTERFACE "-mrtm")
|
||||
endif ()
|
||||
|
||||
elseif(WIN32)
|
||||
target_compile_definitions(tbb_interface INTERFACE USE_WINTHREAD _WIN32_WINNT=0x0600)
|
||||
if (MSVC)
|
||||
enable_language(ASM_MASM)
|
||||
target_compile_options(tbb_interface INTERFACE /GS- /Zc:wchar_t /Zc:forScope)
|
||||
check_cxx_compiler_flag ("/volatile:iso" SUPPORTS_VOLATILE_FLAG)
|
||||
if (SUPPORTS_VOLATILE_FLAG)
|
||||
target_compile_options(tbb_interface INTERFACE /volatile:iso)
|
||||
endif ()
|
||||
target_compile_options(tbb_interface INTERFACE $<$<COMPILE_LANGUAGE:CXX>:/wd4267 /wd4800 /wd4146 /wd4244 /wd4577 /wd4018>)
|
||||
if (NOT CMAKE_SIZEOF_VOID_P)
|
||||
message(FATAL_ERROR "'CMAKE_SIZEOF_VOID_P' is undefined. Please delete your build directory and rerun CMake again!")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
|
||||
src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
|
||||
list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
|
||||
set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1 ${CMAKE_ASM_MASM_FLAGS}")
|
||||
else()
|
||||
list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
|
||||
src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
|
||||
# Enable SAFESEH feature for assembly (x86 builds only).
|
||||
set(CMAKE_ASM_MASM_FLAGS "/safeseh ${CMAKE_ASM_MASM_FLAGS}")
|
||||
endif()
|
||||
elseif (MINGW)
|
||||
target_compile_options(tbb_interface INTERFACE "-mthreads")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag("-flifetime-dse=1" SUPPORTS_FLIFETIME)
|
||||
if (SUPPORTS_FLIFETIME)
|
||||
add_definitions(-flifetime-dse=1)
|
||||
if (MSVC)
|
||||
set(ENABLE_RTTI "/EHsc /GR ")
|
||||
set(DISABLE_RTTI "/EHs- /GR- ")
|
||||
elseif (UNIX)
|
||||
set(ENABLE_RTTI "-frtti -fexceptions ")
|
||||
set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
|
||||
endif ()
|
||||
|
||||
##--------
|
||||
# - Added TBB_USE_GLIBCXX_VERSION macro to specify the version of GNU
|
||||
# libstdc++ when it cannot be properly recognized, e.g. when used
|
||||
# with Clang on Linux* OS. Inspired by a contribution from David A.
|
||||
if (NOT TBB_USE_GLIBCXX_VERSION AND UNIX AND NOT APPLE)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# using Clang
|
||||
string(REPLACE "." "0" TBB_USE_GLIBCXX_VERSION ${CMAKE_CXX_COMPILER_VERSION})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (TBB_USE_GLIBCXX_VERSION)
|
||||
target_compile_definitions(tbb_interface INTERFACE TBB_USE_GLIBCXX_VERSION=${TBB_USE_GLIBCXX_VERSION})
|
||||
endif()
|
||||
|
||||
##-------
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
check_cxx_compiler_flag ("-flifetime-dse=1" SUPPORTS_FLIFETIME)
|
||||
if (SUPPORTS_FLIFETIME)
|
||||
target_compile_options(tbb_interface INTERFACE -flifetime-dse=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Linker export definitions
|
||||
if (WIN32)
|
||||
if (APPLE)
|
||||
set (ARCH_PREFIX "mac")
|
||||
elseif(WIN32)
|
||||
set (ARCH_PREFIX "win")
|
||||
else()
|
||||
set (ARCH_PREFIX "lin")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(ARCH_PREFIX "${ARCH_PREFIX}64")
|
||||
else()
|
||||
set(ARCH_PREFIX "${ARCH_PREFIX}32")
|
||||
endif()
|
||||
|
||||
if (MINGW)
|
||||
set (ARCH_PREFIX "${ARCH_PREFIX}-gcc")
|
||||
# there's no win32-gcc-tbb-export.def, use lin32-tbb-export.def
|
||||
execute_process (COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/lin32-tbb-export.def ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/win32-gcc-tbb-export.def)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
add_custom_command(OUTPUT tbb.def
|
||||
COMMAND ${CMAKE_CXX_COMPILER} /TC /EP ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include > tbb.def
|
||||
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
|
||||
@@ -110,18 +238,15 @@ if (WIN32)
|
||||
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
|
||||
COMMENT "Preprocessing tbbmalloc.def"
|
||||
)
|
||||
list(APPEND tbb_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/tbb_resource.rc)
|
||||
list(APPEND tbbmalloc_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
|
||||
list(APPEND tbbmalloc_proxy_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
|
||||
else()
|
||||
add_custom_command(OUTPUT tbb.def
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
|
||||
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
|
||||
COMMENT "Preprocessing tbb.def"
|
||||
)
|
||||
|
||||
add_custom_command(OUTPUT tbbmalloc.def
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
|
||||
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
|
||||
COMMENT "Preprocessing tbbmalloc.def"
|
||||
)
|
||||
@@ -132,34 +257,80 @@ add_custom_target(tbb_def_files DEPENDS tbb.def tbbmalloc.def)
|
||||
# TBB library
|
||||
if (TBB_BUILD_STATIC)
|
||||
add_library(tbb_static STATIC ${tbb_src})
|
||||
set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
|
||||
set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
|
||||
target_link_libraries(tbb_static PRIVATE tbb_interface)
|
||||
target_include_directories(tbb_static INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
|
||||
set_property(TARGET tbb_static APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
|
||||
install(TARGETS tbb_static ARCHIVE DESTINATION lib)
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(TARGETS tbb_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
|
||||
endif()
|
||||
|
||||
target_compile_definitions(tbb_static
|
||||
PRIVATE
|
||||
-D__TBB_BUILD=1
|
||||
-D__TBB_DYNAMIC_LOAD_ENABLED=0
|
||||
-D__TBB_SOURCE_DIRECTLY_INCLUDED=1)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_definitions(tbb_static
|
||||
PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
|
||||
PRIVATE -D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(tbb_static PUBLIC pthread dl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (TBB_BUILD_SHARED)
|
||||
add_library(tbb SHARED ${tbb_src})
|
||||
set_property(TARGET tbb APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
|
||||
target_link_libraries(tbb PRIVATE tbb_interface)
|
||||
target_include_directories(tbb INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
|
||||
set_property(TARGET tbb APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
|
||||
add_dependencies(tbb tbb_def_files)
|
||||
if (APPLE)
|
||||
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
|
||||
elseif(UNIX)
|
||||
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
|
||||
elseif(WIN32)
|
||||
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
|
||||
|
||||
if (TBB_SET_SOVERSION)
|
||||
set_property(TARGET tbb PROPERTY SOVERSION 2)
|
||||
endif ()
|
||||
|
||||
target_compile_definitions(tbb
|
||||
PRIVATE -D__TBB_BUILD=1)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_definitions(tbb
|
||||
PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
|
||||
PRIVATE -D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
install(TARGETS tbb DESTINATION lib)
|
||||
if(WIN32)
|
||||
set_target_properties(tbb PROPERTIES OUTPUT_NAME "tbb$<$<CONFIG:Debug>:_debug>")
|
||||
|
||||
add_dependencies(tbb tbb_def_files)
|
||||
|
||||
if (APPLE)
|
||||
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
|
||||
elseif (MSVC)
|
||||
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
|
||||
else ()
|
||||
set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
|
||||
endif()
|
||||
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(TARGETS tbb EXPORT TBB
|
||||
LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
|
||||
ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
|
||||
RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
|
||||
if (MSVC)
|
||||
install(FILES $<TARGET_PDB_FILE:tbb> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(tbb PUBLIC pthread dl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# Quench a warning on GCC
|
||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/governor.cpp COMPILE_FLAGS "-Wno-missing-field-initializers ")
|
||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# Quench a warning on Clang
|
||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/itt_notify.cpp COMPILE_FLAGS "-Wno-varargs ")
|
||||
elseif(MSVC)
|
||||
# Quench a warning on MSVC
|
||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/scheduler.cpp COMPILE_FLAGS "/wd4458 ")
|
||||
@@ -169,24 +340,50 @@ if(TBB_BUILD_TBBMALLOC)
|
||||
# TBB malloc library
|
||||
if (TBB_BUILD_STATIC)
|
||||
add_library(tbbmalloc_static STATIC ${tbbmalloc_static_src})
|
||||
target_link_libraries(tbbmalloc_static PRIVATE tbb_interface)
|
||||
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
|
||||
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
|
||||
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
|
||||
set_property(TARGET tbbmalloc_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
|
||||
install(TARGETS tbbmalloc_static ARCHIVE DESTINATION lib)
|
||||
if (MSVC)
|
||||
target_compile_definitions(tbbmalloc_static PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
|
||||
endif()
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(TARGETS tbbmalloc_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (TBB_BUILD_SHARED)
|
||||
add_library(tbbmalloc SHARED ${tbbmalloc_src})
|
||||
target_link_libraries(tbbmalloc PRIVATE tbb_interface)
|
||||
set_property(TARGET tbbmalloc APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
|
||||
set_property(TARGET tbbmalloc APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
|
||||
if (TBB_SET_SOVERSION)
|
||||
set_property(TARGET tbbmalloc PROPERTY SOVERSION 2)
|
||||
endif ()
|
||||
add_dependencies(tbbmalloc tbb_def_files)
|
||||
if (APPLE)
|
||||
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
|
||||
elseif(UNIX)
|
||||
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
|
||||
elseif(WIN32)
|
||||
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
|
||||
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
|
||||
elseif (MSVC)
|
||||
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
|
||||
else ()
|
||||
set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
|
||||
endif()
|
||||
if (MSVC)
|
||||
target_compile_definitions(tbbmalloc PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
|
||||
endif()
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(TARGETS tbbmalloc EXPORT TBB
|
||||
LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
|
||||
ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
|
||||
RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
|
||||
if (MSVC)
|
||||
install(FILES $<TARGET_PDB_FILE:tbbmalloc> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(tbbmalloc PUBLIC pthread dl)
|
||||
endif()
|
||||
install(TARGETS tbbmalloc DESTINATION lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -194,19 +391,298 @@ if(TBB_BUILD_TBBMALLOC_PROXY)
|
||||
# TBB malloc proxy library
|
||||
if (TBB_BUILD_STATIC)
|
||||
add_library(tbbmalloc_proxy_static STATIC ${tbbmalloc_proxy_src})
|
||||
target_link_libraries(tbbmalloc_proxy_static PRIVATE tbb_interface)
|
||||
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
|
||||
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
|
||||
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
|
||||
set_property(TARGET tbbmalloc_proxy_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
|
||||
link_libraries(tbbmalloc_proxy_static tbbmalloc)
|
||||
install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION lib)
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (TBB_BUILD_SHARED)
|
||||
add_library(tbbmalloc_proxy SHARED ${tbbmalloc_proxy_src})
|
||||
target_link_libraries(tbbmalloc_proxy PRIVATE tbb_interface)
|
||||
set_property(TARGET tbbmalloc_proxy APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
|
||||
set_property(TARGET tbbmalloc_proxy APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
|
||||
target_link_libraries(tbbmalloc_proxy tbbmalloc)
|
||||
install(TARGETS tbbmalloc_proxy DESTINATION lib)
|
||||
if (TBB_SET_SOVERSION)
|
||||
set_property(TARGET tbbmalloc_proxy PROPERTY SOVERSION 2)
|
||||
endif ()
|
||||
target_link_libraries(tbbmalloc_proxy PUBLIC tbbmalloc)
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(TARGETS tbbmalloc_proxy EXPORT TBB
|
||||
LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
|
||||
ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
|
||||
RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
|
||||
if (MSVC)
|
||||
install(FILES $<TARGET_PDB_FILE:tbbmalloc_proxy> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(tbbmalloc_proxy PUBLIC pthread dl)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(DIRECTORY include/tbb DESTINATION include)
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(DIRECTORY include/tbb DESTINATION ${TBB_INSTALL_INCLUDE_DIR})
|
||||
if (TBB_BUILD_SHARED)
|
||||
install(EXPORT TBB DESTINATION ${TBB_CMAKE_PACKAGE_INSTALL_DIR} NAMESPACE TBB:: FILE TBBConfig.cmake)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# version file
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
set (_VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/include/tbb/tbb_stddef.h)
|
||||
file (STRINGS ${_VERSION_FILE} _VERSION_MAJOR_STRING REGEX ".*define[ ]+TBB_VERSION_MAJOR[ ]+[0-9]+.*")
|
||||
file (STRINGS ${_VERSION_FILE} _VERSION_MINOR_STRING REGEX ".*define[ ]+TBB_VERSION_MINOR[ ]+[0-9]+.*")
|
||||
string (REGEX REPLACE ".*TBB_VERSION_MAJOR[ ]+([0-9]+)" "\\1" TBB_MAJOR_VERSION ${_VERSION_MAJOR_STRING})
|
||||
string (REGEX REPLACE ".*TBB_VERSION_MINOR[ ]+([0-9]+)" "\\1" TBB_MINOR_VERSION ${_VERSION_MINOR_STRING})
|
||||
set (TBB_VERSION_STRING "${TBB_MAJOR_VERSION}.${TBB_MINOR_VERSION}")
|
||||
include (CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file (TBBConfigVersion.cmake VERSION "${TBB_VERSION_STRING}" COMPATIBILITY AnyNewerVersion)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/TBBConfigVersion.cmake DESTINATION "${TBB_CMAKE_PACKAGE_INSTALL_DIR}")
|
||||
endif()
|
||||
|
||||
# version_string.ver
|
||||
if (UNIX AND NOT TBB_NO_DATE)
|
||||
execute_process (COMMAND date "+%a, %d %b %Y %H:%M:%S %z"
|
||||
OUTPUT_VARIABLE _configure_date
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
elseif (WIN32 AND NOT TBB_NO_DATE)
|
||||
execute_process (COMMAND cmd " /C date /T"
|
||||
OUTPUT_VARIABLE _configure_date
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
else ()
|
||||
set (_configure_date "Unknown")
|
||||
endif()
|
||||
set (TBB_CONFIG_DATE "${_configure_date}" CACHE STRING "First time that TBB was configured")
|
||||
set (_configure_date "${TBB_CONFIG_DATE}")
|
||||
include_directories (${CMAKE_BINARY_DIR})
|
||||
configure_file (build/version_string.ver.in version_string.ver @ONLY)
|
||||
|
||||
if (TBB_BUILD_TESTS)
|
||||
enable_language (C)
|
||||
enable_testing ()
|
||||
|
||||
find_library (LIBRT_LIBRARIES rt)
|
||||
find_library (LIDL_LIBRARIES dl)
|
||||
find_package (Threads)
|
||||
if (NOT APPLE)
|
||||
find_package (OpenMP)
|
||||
endif()
|
||||
|
||||
macro (tbb_add_test testname)
|
||||
set (full_testname tbb_test_${testname})
|
||||
add_executable (${full_testname} src/test/test_${testname}.cpp)
|
||||
target_link_libraries(${full_testname} PRIVATE tbb_interface)
|
||||
if (TBB_BUILD_SHARED)
|
||||
target_link_libraries (${full_testname} PRIVATE tbb tbbmalloc)
|
||||
target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb)
|
||||
else ()
|
||||
target_link_libraries (${full_testname} PRIVATE tbb_static tbbmalloc_static)
|
||||
target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb_static)
|
||||
endif ()
|
||||
if (LIBRT_LIBRARIES)
|
||||
target_link_libraries (${full_testname} PRIVATE ${LIBRT_LIBRARIES})
|
||||
endif ()
|
||||
if (LIDL_LIBRARIES)
|
||||
target_link_libraries (${full_testname} PRIVATE ${LIDL_LIBRARIES})
|
||||
endif ()
|
||||
if (Threads_FOUND)
|
||||
target_link_libraries (${full_testname} PRIVATE ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif ()
|
||||
if (OPENMP_FOUND AND "${testname}" MATCHES "openmp")
|
||||
set_target_properties (${full_testname} PROPERTIES COMPILE_FLAGS "${OpenMP_CXX_FLAGS}")
|
||||
set_target_properties (${full_testname} PROPERTIES LINK_FLAGS "${OpenMP_CXX_FLAGS}")
|
||||
endif()
|
||||
if (MINGW)
|
||||
target_link_libraries (${full_testname} PRIVATE psapi)
|
||||
endif ()
|
||||
add_test (NAME ${full_testname} COMMAND ${full_testname})
|
||||
endmacro ()
|
||||
|
||||
tbb_add_test (aggregator)
|
||||
tbb_add_test (aligned_space)
|
||||
tbb_add_test (assembly)
|
||||
if (NOT WIN32)
|
||||
tbb_add_test (async_msg) # msvc64/debug timeouts
|
||||
endif()
|
||||
tbb_add_test (async_node)
|
||||
# tbb_add_test (atomic) # msvc64/debug timeouts: Compile-time initialization fails for static tbb::atomic variables
|
||||
tbb_add_test (blocked_range2d)
|
||||
tbb_add_test (blocked_range3d)
|
||||
tbb_add_test (blocked_range)
|
||||
tbb_add_test (broadcast_node)
|
||||
tbb_add_test (buffer_node)
|
||||
tbb_add_test (cache_aligned_allocator)
|
||||
if (NOT WIN32)
|
||||
tbb_add_test (cache_aligned_allocator_STL)
|
||||
endif()
|
||||
tbb_add_test (cilk_dynamic_load)
|
||||
tbb_add_test (cilk_interop)
|
||||
tbb_add_test (combinable)
|
||||
tbb_add_test (composite_node)
|
||||
tbb_add_test (concurrent_hash_map)
|
||||
tbb_add_test (concurrent_lru_cache)
|
||||
# tbb_add_test (concurrent_monitor) # too long
|
||||
# tbb_add_test (concurrent_priority_queue)
|
||||
if (NOT WIN32)
|
||||
tbb_add_test (concurrent_queue) # msvc64/debug timeouts
|
||||
endif()
|
||||
# tbb_add_test (concurrent_queue_whitebox)
|
||||
tbb_add_test (concurrent_unordered_map)
|
||||
# tbb_add_test (concurrent_unordered_set)
|
||||
tbb_add_test (concurrent_vector)
|
||||
tbb_add_test (continue_node)
|
||||
tbb_add_test (critical_section)
|
||||
tbb_add_test (dynamic_link)
|
||||
# tbb_add_test (eh_algorithms)
|
||||
tbb_add_test (eh_flow_graph)
|
||||
# tbb_add_test (eh_tasks)
|
||||
tbb_add_test (enumerable_thread_specific)
|
||||
tbb_add_test (examples_common_utility)
|
||||
# tbb_add_test (fast_random)
|
||||
tbb_add_test (flow_graph)
|
||||
tbb_add_test (flow_graph_whitebox)
|
||||
# tbb_add_test (fp) # mingw: harness_fp.h:66, assertion !checkConsistency || (ctl.mxcsr & SSE_RND_MODE_MASK) >> 3 == (ctl.x87cw & FE_RND_MODE_MASK): failed
|
||||
# tbb_add_test (function_node) # mingw:random timeout
|
||||
# tbb_add_test (global_control)
|
||||
# tbb_add_test (global_control_whitebox)
|
||||
tbb_add_test (halt)
|
||||
tbb_add_test (handle_perror)
|
||||
# tbb_add_test (hw_concurrency)
|
||||
tbb_add_test (indexer_node)
|
||||
tbb_add_test (inits_loop)
|
||||
tbb_add_test (intrusive_list)
|
||||
tbb_add_test (ittnotify)
|
||||
# tbb_add_test (join_node) #msvc/64: fatal error C1128: number of sections exceeded object file format limit: compile with /bigob
|
||||
tbb_add_test (lambda)
|
||||
tbb_add_test (limiter_node)
|
||||
# tbb_add_test (malloc_atexit)
|
||||
# tbb_add_test (malloc_compliance) #mingw: Limits should be decreased for the test to work
|
||||
tbb_add_test (malloc_init_shutdown)
|
||||
# tbb_add_test (malloc_lib_unload)
|
||||
# tbb_add_test (malloc_overload)
|
||||
tbb_add_test (malloc_pools)
|
||||
tbb_add_test (malloc_regression)
|
||||
# tbb_add_test (malloc_used_by_lib)
|
||||
# tbb_add_test (malloc_whitebox)
|
||||
tbb_add_test (model_plugin)
|
||||
# tbb_add_test (multifunction_node) # too long
|
||||
tbb_add_test (mutex)
|
||||
tbb_add_test (mutex_native_threads)
|
||||
# tbb_add_test (opencl_node)
|
||||
if (OPENMP_FOUND)
|
||||
tbb_add_test (openmp)
|
||||
endif ()
|
||||
tbb_add_test (overwrite_node)
|
||||
# tbb_add_test (parallel_do)
|
||||
# This seems to fail on CI platforms (AppVeyor/Travis), perhaps because the VM exposes just 1 core?
|
||||
tbb_add_test (parallel_for)
|
||||
tbb_add_test (parallel_for_each)
|
||||
tbb_add_test (parallel_for_vectorization)
|
||||
tbb_add_test (parallel_invoke)
|
||||
tbb_add_test (parallel_pipeline)
|
||||
tbb_add_test (parallel_reduce)
|
||||
tbb_add_test (parallel_scan)
|
||||
tbb_add_test (parallel_sort)
|
||||
tbb_add_test (parallel_while)
|
||||
# tbb_add_test (partitioner_whitebox) # too long
|
||||
tbb_add_test (pipeline)
|
||||
# tbb_add_test (pipeline_with_tbf) # takes forever on appveyor
|
||||
tbb_add_test (priority_queue_node)
|
||||
tbb_add_test (queue_node)
|
||||
tbb_add_test (reader_writer_lock)
|
||||
# tbb_add_test (runtime_loader) # LINK : fatal error LNK1104: cannot open file 'tbbproxy.lib' [C:\projects\tbb\test_runtime_loader.vcxproj]
|
||||
tbb_add_test (rwm_upgrade_downgrade)
|
||||
# tbb_add_test (ScalableAllocator)
|
||||
if (NOT WIN32)
|
||||
tbb_add_test (ScalableAllocator_STL)
|
||||
endif()
|
||||
tbb_add_test (semaphore)
|
||||
# tbb_add_test (sequencer_node) # msvc: timeout
|
||||
tbb_add_test (source_node)
|
||||
tbb_add_test (split_node)
|
||||
tbb_add_test (static_assert)
|
||||
tbb_add_test (std_thread)
|
||||
tbb_add_test (tagged_msg)
|
||||
# tbb_add_test (task_arena) # LINK : fatal error LNK1104: cannot open file '__TBB_LIB_NAME.lib' [C:\projects\tbb\test_task_arena.vcxproj]
|
||||
# tbb_add_test (task_assertions)
|
||||
tbb_add_test (task_auto_init)
|
||||
tbb_add_test (task)
|
||||
# tbb_add_test (task_enqueue) # too long
|
||||
tbb_add_test (task_group)
|
||||
# tbb_add_test (task_leaks)
|
||||
# tbb_add_test (task_priority)
|
||||
# tbb_add_test (task_scheduler_init) # msvc: test_task_scheduler_init.cpp:68, assertion !test_mandatory_parallelism || Harness::CanReachConcurrencyLevel(threads): failed
|
||||
tbb_add_test (task_scheduler_observer)
|
||||
tbb_add_test (task_steal_limit)
|
||||
tbb_add_test (tbb_condition_variable)
|
||||
tbb_add_test (tbb_fork)
|
||||
# tbb_add_test (tbb_header)
|
||||
tbb_add_test (tbb_thread)
|
||||
# tbb_add_test (tbb_version)
|
||||
tbb_add_test (tick_count)
|
||||
tbb_add_test (tuple)
|
||||
tbb_add_test (write_once_node)
|
||||
tbb_add_test (yield)
|
||||
endif ()
|
||||
|
||||
if (TBB_BUILD_PYTHON)
|
||||
find_package(PythonInterp)
|
||||
find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT)
|
||||
find_package(SWIG 3)
|
||||
if (PythonLibs_FOUND AND SWIG_FOUND AND TBB_BUILD_SHARED)
|
||||
include (${SWIG_USE_FILE})
|
||||
set_source_files_properties (python/tbb/api.i PROPERTIES CPLUSPLUS ON)
|
||||
set (CMAKE_SWIG_FLAGS "-threads")
|
||||
|
||||
# swig_add_module is deprecated
|
||||
if (CMAKE_VERSION VERSION_LESS 3.8)
|
||||
swig_add_module (api python python/tbb/api.i)
|
||||
else ()
|
||||
swig_add_library (api LANGUAGE python SOURCES python/tbb/api.i)
|
||||
endif ()
|
||||
|
||||
# UseSWIG generates now standard target names
|
||||
if (CMAKE_VERSION VERSION_LESS 3.13)
|
||||
set (module_target ${SWIG_MODULE_api_REAL_NAME})
|
||||
else ()
|
||||
set (module_target api)
|
||||
endif ()
|
||||
|
||||
target_include_directories(${module_target} PRIVATE ${PYTHON_INCLUDE_DIRS})
|
||||
target_link_libraries(${module_target} PRIVATE tbb)
|
||||
if(WIN32)
|
||||
target_link_libraries(${module_target} ${PYTHON_LIBRARIES})
|
||||
elseif(APPLE)
|
||||
set_target_properties(${module_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set (PYTHON_SITE_PACKAGES Lib/site-packages)
|
||||
else ()
|
||||
set (PYTHON_SITE_PACKAGES lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages)
|
||||
endif ()
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(FILES python/TBB.py
|
||||
DESTINATION ${PYTHON_SITE_PACKAGES})
|
||||
install(FILES python/tbb/__init__.py python/tbb/pool.py python/tbb/test.py python/tbb/__main__.py ${CMAKE_CURRENT_BINARY_DIR}/api.py
|
||||
DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
|
||||
install(TARGETS ${module_target}
|
||||
DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_library(irml SHARED python/rml/ipc_server.cpp python/rml/ipc_utils.cpp src/tbb/cache_aligned_allocator.cpp src/tbb/dynamic_link.cpp src/tbb/tbb_misc_ex.cpp src/tbb/tbb_misc.cpp)
|
||||
target_compile_definitions(irml PRIVATE DO_ITT_NOTIFY=0 USE_PTHREAD=1)
|
||||
target_link_libraries(irml PRIVATE tbb)
|
||||
set_target_properties(irml PROPERTIES VERSION 1)
|
||||
if (TBB_INSTALL_TARGETS)
|
||||
install(TARGETS irml DESTINATION ${TBB_INSTALL_LIBRARY_DIR})
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
@@ -63,3 +63,19 @@ diff -Naur org/CMakeLists.txt external_osl/CMakeLists.txt
|
||||
|
||||
set (OSL_NO_DEFAULT_TEXTURESYSTEM OFF CACHE BOOL "Do not use create a raw OIIO::TextureSystem")
|
||||
if (OSL_NO_DEFAULT_TEXTURESYSTEM)
|
||||
diff --git a/src/liboslexec/llvm_util.cpp b/src/liboslexec/llvm_util.cpp
|
||||
index 445f6400..3d468de2 100644
|
||||
--- a/src/liboslexec/llvm_util.cpp
|
||||
+++ b/src/liboslexec/llvm_util.cpp
|
||||
@@ -3430,8 +3430,9 @@ LLVM_Util::call_function (llvm::Value *func, cspan<llvm::Value *> args)
|
||||
#endif
|
||||
//llvm_gen_debug_printf (std::string("start ") + std::string(name));
|
||||
#if OSL_LLVM_VERSION >= 110
|
||||
- OSL_DASSERT(llvm::isa<llvm::Function>(func));
|
||||
- llvm::Value *r = builder().CreateCall(llvm::cast<llvm::Function>(func), llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
|
||||
+ llvm::Value* r = builder().CreateCall(
|
||||
+ llvm::cast<llvm::FunctionType>(func->getType()->getPointerElementType()), func,
|
||||
+ llvm::ArrayRef<llvm::Value*>(args.data(), args.size()));
|
||||
#else
|
||||
llvm::Value *r = builder().CreateCall (func, llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
|
||||
#endif
|
||||
|
@@ -10,4 +10,15 @@ index 7a8d06a0..886699d8 100644
|
||||
+ #if (__cplusplus >= 201402L && (!defined(_MSC_VER) || _MSC_VER >= 1920))
|
||||
#define __TBB_DEPRECATED [[deprecated]]
|
||||
#define __TBB_DEPRECATED_MSG(msg) [[deprecated(msg)]]
|
||||
#elif _MSC_VER
|
||||
#elif _MSC_VER
|
||||
--- a/src/tbb/tools_api/ittnotify_config.h
|
||||
+++ b/src/tbb/tools_api/ittnotify_config.h
|
||||
@@ -162,7 +162,7 @@
|
||||
# define ITT_ARCH ITT_ARCH_IA32E
|
||||
# elif defined _M_IA64 || defined __ia64__
|
||||
# define ITT_ARCH ITT_ARCH_IA64
|
||||
-# elif defined _M_ARM || defined __arm__
|
||||
+# elif defined _M_ARM || defined __arm__ || defined __aarch64__
|
||||
# define ITT_ARCH ITT_ARCH_ARM
|
||||
# elif defined __powerpc64__
|
||||
# define ITT_ARCH ITT_ARCH_PPC64
|
||||
|
@@ -4,7 +4,7 @@
|
||||
# Some are omitted here because they have special meanings below.
|
||||
1750a | 580 \
|
||||
| a29k \
|
||||
+ | aarch64 \
|
||||
+ | aarch64 \
|
||||
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
|
||||
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
|
||||
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
|
||||
@@ -12,7 +12,7 @@
|
||||
# Recognize the basic CPU types with company name.
|
||||
580-* \
|
||||
| a29k-* \
|
||||
+ | aarch64-* \
|
||||
+ | aarch64-* \
|
||||
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
|
||||
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
|
||||
|
@@ -53,3 +53,147 @@ diff -ru USD-20.11/pxr/base/tf/pxrLZ4/lz4.cpp external_usd/pxr/base/tf/pxrLZ4/lz
|
||||
|
||||
/*-******************************
|
||||
* Compression functions
|
||||
|
||||
From 442d087962f762deeb8b6e49a0955753fcf9aeb9 Mon Sep 17 00:00:00 2001
|
||||
From: Tsahi Zidenberg <tsahee@amazon.com>
|
||||
Date: Sun, 15 Nov 2020 15:18:24 +0000
|
||||
Subject: [PATCH 1/2] stackTrace: support aarch64/linux
|
||||
|
||||
stacktrace calls syscall directly via assembler. Create compatible
|
||||
aarch64 code.
|
||||
---
|
||||
pxr/base/arch/stackTrace.cpp | 30 ++++++++++++++++++++++++------
|
||||
1 file changed, 24 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/pxr/base/arch/stackTrace.cpp b/pxr/base/arch/stackTrace.cpp
|
||||
index dcc1dfd46..c11aabeb1 100644
|
||||
--- a/pxr/base/arch/stackTrace.cpp
|
||||
+++ b/pxr/base/arch/stackTrace.cpp
|
||||
@@ -583,7 +583,6 @@ nonLockingLinux__execve (const char *file,
|
||||
char *const argv[],
|
||||
char *const envp[])
|
||||
{
|
||||
-#if defined(ARCH_BITS_64)
|
||||
/*
|
||||
* We make a direct system call here, because we can't find an
|
||||
* execve which corresponds with the non-locking fork we call
|
||||
@@ -594,7 +593,27 @@ nonLockingLinux__execve (const char *file,
|
||||
* hangs in a threaded app. (We use the non-locking fork to get
|
||||
* around problems with forking when we have had memory
|
||||
* corruption.) whew.
|
||||
- *
|
||||
+ */
|
||||
+
|
||||
+ unsigned long result;
|
||||
+
|
||||
+#if defined (__aarch64__)
|
||||
+ {
|
||||
+ register long __file_result asm ("x0") = (long)file;
|
||||
+ register char* const* __argv asm ("x1") = argv;
|
||||
+ register char* const* __envp asm ("x2") = envp;
|
||||
+ register long __num_execve asm ("x8") = 221;
|
||||
+ __asm__ __volatile__ (
|
||||
+ "svc 0"
|
||||
+ : "=r" (__file_result)
|
||||
+ : "r"(__num_execve), "r" (__file_result), "r" (__argv), "r" (__envp)
|
||||
+ : "memory"
|
||||
+ );
|
||||
+ result = __file_result;
|
||||
+ }
|
||||
+#elif defined(ARCH_CPU_INTEL) && defined(ARCH_BITS_64)
|
||||
+
|
||||
+ /*
|
||||
* %rdi, %rsi, %rdx, %rcx, %r8, %r9 are args 0-5
|
||||
* syscall clobbers %rcx and %r11
|
||||
*
|
||||
@@ -603,7 +622,6 @@ nonLockingLinux__execve (const char *file,
|
||||
* constraints to gcc.
|
||||
*/
|
||||
|
||||
- unsigned long result;
|
||||
__asm__ __volatile__ (
|
||||
"mov %0, %%rdi \n\t"
|
||||
"mov %%rcx, %%rsi \n\t"
|
||||
@@ -614,6 +632,9 @@ nonLockingLinux__execve (const char *file,
|
||||
: "0" (file), "c" (argv), "d" (envp)
|
||||
: "memory", "cc", "r11"
|
||||
);
|
||||
+#else
|
||||
+#error Unknown architecture
|
||||
+#endif
|
||||
|
||||
if (result >= 0xfffffffffffff000) {
|
||||
errno = -result;
|
||||
@@ -621,9 +642,6 @@ nonLockingLinux__execve (const char *file,
|
||||
}
|
||||
|
||||
return result;
|
||||
-#else
|
||||
-#error Unknown architecture
|
||||
-#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
From a1dffe02519bb3c6ccbbe8c6c58304da5db98995 Mon Sep 17 00:00:00 2001
|
||||
From: Tsahi Zidenberg <tsahee@amazon.com>
|
||||
Date: Sun, 15 Nov 2020 15:22:52 +0000
|
||||
Subject: [PATCH 2/2] timing: support aarch64/linux
|
||||
|
||||
The aarch64 arch-timer is directly accessible to userspace via two
|
||||
registers:
|
||||
CNTVCT_EL0 - holds the current counter value
|
||||
CNTFRQ_EL0 - holds the counter frequency (in Hz)
|
||||
---
|
||||
pxr/base/arch/timing.cpp | 6 ++++++
|
||||
pxr/base/arch/timing.h | 6 +++++-
|
||||
2 files changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/pxr/base/arch/timing.cpp b/pxr/base/arch/timing.cpp
|
||||
index 27ad58fed..9022950c1 100644
|
||||
--- a/pxr/base/arch/timing.cpp
|
||||
+++ b/pxr/base/arch/timing.cpp
|
||||
@@ -59,6 +59,11 @@ ARCH_HIDDEN
|
||||
void
|
||||
Arch_InitTickTimer()
|
||||
{
|
||||
+#ifdef __aarch64__
|
||||
+ uint64_t counter_hz;
|
||||
+ __asm __volatile("mrs %0, CNTFRQ_EL0" : "=&r" (counter_hz));
|
||||
+ Arch_NanosecondsPerTick = double(1e9) / double(counter_hz);
|
||||
+#else
|
||||
// NOTE: Normally ifstream would be cleaner, but it causes crashes when
|
||||
// used in conjunction with DSOs and the Intel Compiler.
|
||||
FILE *in;
|
||||
@@ -135,6 +140,7 @@ Arch_InitTickTimer()
|
||||
}
|
||||
|
||||
Arch_NanosecondsPerTick = double(1e9) / double(cpuHz);
|
||||
+#endif
|
||||
}
|
||||
#elif defined(ARCH_OS_WINDOWS)
|
||||
|
||||
diff --git a/pxr/base/arch/timing.h b/pxr/base/arch/timing.h
|
||||
index 67ec0d15f..6dc3e85a0 100644
|
||||
--- a/pxr/base/arch/timing.h
|
||||
+++ b/pxr/base/arch/timing.h
|
||||
@@ -36,7 +36,7 @@
|
||||
/// \addtogroup group_arch_SystemFunctions
|
||||
///@{
|
||||
|
||||
-#if defined(ARCH_OS_LINUX)
|
||||
+#if defined(ARCH_OS_LINUX) && defined(ARCH_CPU_INTEL)
|
||||
#include <x86intrin.h>
|
||||
#elif defined(ARCH_OS_DARWIN)
|
||||
#include <mach/mach_time.h>
|
||||
@@ -69,6 +69,10 @@ ArchGetTickTime()
|
||||
#elif defined(ARCH_CPU_INTEL)
|
||||
// On Intel we'll use the rdtsc instruction.
|
||||
return __rdtsc();
|
||||
+#elif defined (__aarch64__)
|
||||
+ uint64_t result;
|
||||
+ __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (result));
|
||||
+ return result;
|
||||
#else
|
||||
#error Unknown architecture.
|
||||
#endif
|
||||
|
@@ -34,7 +34,7 @@ FIND_PATH(EMBREE_INCLUDE_DIR
|
||||
include
|
||||
)
|
||||
|
||||
IF(NOT (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")))
|
||||
IF(NOT (("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") OR (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))))
|
||||
SET(_embree_SIMD_COMPONENTS
|
||||
embree_sse42
|
||||
embree_avx
|
||||
|
@@ -50,7 +50,8 @@ you should be able to find the poll function with no knowledge of C.
|
||||
|
||||
Blender does have the functionality for poll functions to describe why they fail,
|
||||
but its currently not used much, if you're interested to help improve the API
|
||||
feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g:
|
||||
feel free to add calls to :class:`bpy.types.Operator.poll_message_set` (``CTX_wm_operator_poll_msg_set`` in C)
|
||||
where its not obvious why poll fails, e.g:
|
||||
|
||||
>>> bpy.ops.gpencil.draw()
|
||||
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into
|
||||
|
2
extern/smaa_areatex/smaa_areatex.cpp
vendored
2
extern/smaa_areatex/smaa_areatex.cpp
vendored
@@ -1088,8 +1088,6 @@ static int generate_file(AreaOrtho *ortho, AreaDiag *diag, const char *path, boo
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Generating %s\n", path);
|
||||
|
||||
if (tga)
|
||||
write_tga(ortho, diag, fp, subsampling);
|
||||
else if (raw)
|
||||
|
@@ -955,14 +955,21 @@ class OptiXDevice : public CUDADevice {
|
||||
// Create OptiX denoiser handle on demand when it is first used
|
||||
OptixDenoiserOptions denoiser_options = {};
|
||||
assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3);
|
||||
# if OPTIX_ABI_VERSION >= 47
|
||||
denoiser_options.guideAlbedo = task.denoising.input_passes >= 2;
|
||||
denoiser_options.guideNormal = task.denoising.input_passes >= 3;
|
||||
check_result_optix_ret(optixDenoiserCreate(
|
||||
context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser));
|
||||
# else
|
||||
denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>(
|
||||
OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1));
|
||||
# if OPTIX_ABI_VERSION < 28
|
||||
# if OPTIX_ABI_VERSION < 28
|
||||
denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3;
|
||||
# endif
|
||||
# endif
|
||||
check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser));
|
||||
check_result_optix_ret(
|
||||
optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0));
|
||||
# endif
|
||||
|
||||
// OptiX denoiser handle was created with the requested number of input passes
|
||||
denoiser_input_passes = task.denoising.input_passes;
|
||||
@@ -1032,10 +1039,34 @@ class OptiXDevice : public CUDADevice {
|
||||
# endif
|
||||
output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3;
|
||||
|
||||
# if OPTIX_ABI_VERSION >= 47
|
||||
OptixDenoiserLayer image_layers = {};
|
||||
image_layers.input = input_layers[0];
|
||||
image_layers.output = output_layers[0];
|
||||
|
||||
OptixDenoiserGuideLayer guide_layers = {};
|
||||
guide_layers.albedo = input_layers[1];
|
||||
guide_layers.normal = input_layers[2];
|
||||
# endif
|
||||
|
||||
// Finally run denonising
|
||||
OptixDenoiserParams params = {}; // All parameters are disabled/zero
|
||||
# if OPTIX_ABI_VERSION >= 47
|
||||
check_result_optix_ret(optixDenoiserInvoke(denoiser,
|
||||
0,
|
||||
NULL,
|
||||
¶ms,
|
||||
denoiser_state.device_pointer,
|
||||
scratch_offset,
|
||||
&guide_layers,
|
||||
&image_layers,
|
||||
1,
|
||||
overlap_offset.x,
|
||||
overlap_offset.y,
|
||||
denoiser_state.device_pointer + scratch_offset,
|
||||
scratch_size));
|
||||
# else
|
||||
check_result_optix_ret(optixDenoiserInvoke(denoiser,
|
||||
NULL,
|
||||
¶ms,
|
||||
denoiser_state.device_pointer,
|
||||
scratch_offset,
|
||||
@@ -1046,6 +1077,7 @@ class OptiXDevice : public CUDADevice {
|
||||
output_layers,
|
||||
denoiser_state.device_pointer + scratch_offset,
|
||||
scratch_size));
|
||||
# endif
|
||||
|
||||
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
|
||||
void *output_args[] = {&input_ptr,
|
||||
|
@@ -93,6 +93,7 @@ set(SRC_BVH_HEADERS
|
||||
bvh/bvh_local.h
|
||||
bvh/bvh_traversal.h
|
||||
bvh/bvh_types.h
|
||||
bvh/bvh_util.h
|
||||
bvh/bvh_volume.h
|
||||
bvh/bvh_volume_all.h
|
||||
bvh/bvh_embree.h
|
||||
|
@@ -29,9 +29,10 @@
|
||||
# include "kernel/bvh/bvh_embree.h"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#include "kernel/bvh/bvh_types.h"
|
||||
#include "kernel/bvh/bvh_util.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifndef __KERNEL_OPTIX__
|
||||
|
||||
@@ -533,97 +534,4 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg,
|
||||
}
|
||||
#endif /* __VOLUME_RECORD_ALL__ */
|
||||
|
||||
/* Ray offset to avoid self intersection.
|
||||
*
|
||||
* This function should be used to compute a modified ray start position for
|
||||
* rays leaving from a surface. */
|
||||
|
||||
ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
|
||||
{
|
||||
#ifdef __INTERSECTION_REFINE__
|
||||
const float epsilon_f = 1e-5f;
|
||||
/* ideally this should match epsilon_f, but instancing and motion blur
|
||||
* precision makes it problematic */
|
||||
const float epsilon_test = 1.0f;
|
||||
const int epsilon_i = 32;
|
||||
|
||||
float3 res;
|
||||
|
||||
/* x component */
|
||||
if (fabsf(P.x) < epsilon_test) {
|
||||
res.x = P.x + Ng.x * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint ix = __float_as_uint(P.x);
|
||||
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.x = __uint_as_float(ix);
|
||||
}
|
||||
|
||||
/* y component */
|
||||
if (fabsf(P.y) < epsilon_test) {
|
||||
res.y = P.y + Ng.y * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint iy = __float_as_uint(P.y);
|
||||
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.y = __uint_as_float(iy);
|
||||
}
|
||||
|
||||
/* z component */
|
||||
if (fabsf(P.z) < epsilon_test) {
|
||||
res.z = P.z + Ng.z * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint iz = __float_as_uint(P.z);
|
||||
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.z = __uint_as_float(iz);
|
||||
}
|
||||
|
||||
return res;
|
||||
#else
|
||||
const float epsilon_f = 1e-4f;
|
||||
return P + epsilon_f * Ng;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
|
||||
/* ToDo: Move to another file? */
|
||||
ccl_device int intersections_compare(const void *a, const void *b)
|
||||
{
|
||||
const Intersection *isect_a = (const Intersection *)a;
|
||||
const Intersection *isect_b = (const Intersection *)b;
|
||||
|
||||
if (isect_a->t < isect_b->t)
|
||||
return -1;
|
||||
else if (isect_a->t > isect_b->t)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__SHADOW_RECORD_ALL__)
|
||||
ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
|
||||
{
|
||||
# ifdef __KERNEL_GPU__
|
||||
/* Use bubble sort which has more friendly memory pattern on GPU. */
|
||||
bool swapped;
|
||||
do {
|
||||
swapped = false;
|
||||
for (int j = 0; j < num_hits - 1; ++j) {
|
||||
if (hits[j].t > hits[j + 1].t) {
|
||||
struct Intersection tmp = hits[j];
|
||||
hits[j] = hits[j + 1];
|
||||
hits[j + 1] = tmp;
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
--num_hits;
|
||||
} while (swapped);
|
||||
# else
|
||||
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
|
||||
# endif
|
||||
}
|
||||
#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -180,25 +180,10 @@ ccl_device_inline
|
||||
|
||||
/* todo: optimize so primitive visibility flag indicates if
|
||||
* the primitive has a transparent shadow shader? */
|
||||
int prim = kernel_tex_fetch(__prim_index, isect_array->prim);
|
||||
int shader = 0;
|
||||
|
||||
#ifdef __HAIR__
|
||||
if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
|
||||
#endif
|
||||
{
|
||||
shader = kernel_tex_fetch(__tri_shader, prim);
|
||||
}
|
||||
#ifdef __HAIR__
|
||||
else {
|
||||
float4 str = kernel_tex_fetch(__curves, prim);
|
||||
shader = __float_as_int(str.z);
|
||||
}
|
||||
#endif
|
||||
int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
|
||||
const int flags = intersection_get_shader_flags(kg, isect_array);
|
||||
|
||||
/* if no transparent shadows, all light is blocked */
|
||||
if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
|
||||
if (!(flags & SD_HAS_TRANSPARENT_SHADOW)) {
|
||||
return true;
|
||||
}
|
||||
/* if maximum number of hits reached, block all light */
|
||||
|
162
intern/cycles/kernel/bvh/bvh_util.h
Normal file
162
intern/cycles/kernel/bvh/bvh_util.h
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2011-2013 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Ray offset to avoid self intersection.
|
||||
*
|
||||
* This function should be used to compute a modified ray start position for
|
||||
* rays leaving from a surface. */
|
||||
|
||||
ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
|
||||
{
|
||||
#ifdef __INTERSECTION_REFINE__
|
||||
const float epsilon_f = 1e-5f;
|
||||
/* ideally this should match epsilon_f, but instancing and motion blur
|
||||
* precision makes it problematic */
|
||||
const float epsilon_test = 1.0f;
|
||||
const int epsilon_i = 32;
|
||||
|
||||
float3 res;
|
||||
|
||||
/* x component */
|
||||
if (fabsf(P.x) < epsilon_test) {
|
||||
res.x = P.x + Ng.x * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint ix = __float_as_uint(P.x);
|
||||
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.x = __uint_as_float(ix);
|
||||
}
|
||||
|
||||
/* y component */
|
||||
if (fabsf(P.y) < epsilon_test) {
|
||||
res.y = P.y + Ng.y * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint iy = __float_as_uint(P.y);
|
||||
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.y = __uint_as_float(iy);
|
||||
}
|
||||
|
||||
/* z component */
|
||||
if (fabsf(P.z) < epsilon_test) {
|
||||
res.z = P.z + Ng.z * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint iz = __float_as_uint(P.z);
|
||||
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.z = __uint_as_float(iz);
|
||||
}
|
||||
|
||||
return res;
|
||||
#else
|
||||
const float epsilon_f = 1e-4f;
|
||||
return P + epsilon_f * Ng;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
|
||||
/* ToDo: Move to another file? */
|
||||
ccl_device int intersections_compare(const void *a, const void *b)
|
||||
{
|
||||
const Intersection *isect_a = (const Intersection *)a;
|
||||
const Intersection *isect_b = (const Intersection *)b;
|
||||
|
||||
if (isect_a->t < isect_b->t)
|
||||
return -1;
|
||||
else if (isect_a->t > isect_b->t)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__SHADOW_RECORD_ALL__)
|
||||
ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
|
||||
{
|
||||
kernel_assert(num_hits > 0);
|
||||
|
||||
# ifdef __KERNEL_GPU__
|
||||
/* Use bubble sort which has more friendly memory pattern on GPU. */
|
||||
bool swapped;
|
||||
do {
|
||||
swapped = false;
|
||||
for (int j = 0; j < num_hits - 1; ++j) {
|
||||
if (hits[j].t > hits[j + 1].t) {
|
||||
struct Intersection tmp = hits[j];
|
||||
hits[j] = hits[j + 1];
|
||||
hits[j + 1] = tmp;
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
--num_hits;
|
||||
} while (swapped);
|
||||
# else
|
||||
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
|
||||
# endif
|
||||
}
|
||||
#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
|
||||
|
||||
/* Utility to quickly get a shader flags from an intersection. */
|
||||
|
||||
ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_restrict kg,
|
||||
const Intersection *isect)
|
||||
{
|
||||
const int prim = kernel_tex_fetch(__prim_index, isect->prim);
|
||||
int shader = 0;
|
||||
|
||||
#ifdef __HAIR__
|
||||
if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
|
||||
#endif
|
||||
{
|
||||
shader = kernel_tex_fetch(__tri_shader, prim);
|
||||
}
|
||||
#ifdef __HAIR__
|
||||
else {
|
||||
float4 str = kernel_tex_fetch(__curves, prim);
|
||||
shader = __float_as_int(str.z);
|
||||
}
|
||||
#endif
|
||||
|
||||
return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
|
||||
}
|
||||
|
||||
ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict kg,
|
||||
const Intersection *isect)
|
||||
{
|
||||
const int prim = kernel_tex_fetch(__prim_index, isect->prim);
|
||||
int shader = 0;
|
||||
|
||||
#ifdef __HAIR__
|
||||
if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
|
||||
#endif
|
||||
{
|
||||
shader = kernel_tex_fetch(__tri_shader, prim);
|
||||
}
|
||||
#ifdef __HAIR__
|
||||
else {
|
||||
float4 str = kernel_tex_fetch(__curves, prim);
|
||||
shader = __float_as_int(str.z);
|
||||
}
|
||||
#endif
|
||||
|
||||
return shader & SHADER_MASK;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
@@ -65,7 +65,6 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg,
|
||||
uint visibility = path_state_ray_visibility(kg, state);
|
||||
|
||||
if (path_state_ao_bounce(kg, state)) {
|
||||
visibility = PATH_RAY_SHADOW;
|
||||
ray->t = kernel_data.background.ao_distance;
|
||||
}
|
||||
|
||||
@@ -416,7 +415,13 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
|
||||
break;
|
||||
}
|
||||
else if (path_state_ao_bounce(kg, state)) {
|
||||
break;
|
||||
if (intersection_get_shader_flags(kg, &isect) &
|
||||
(SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
|
||||
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup shader data. */
|
||||
@@ -554,7 +559,13 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
|
||||
break;
|
||||
}
|
||||
else if (path_state_ao_bounce(kg, state)) {
|
||||
break;
|
||||
if (intersection_get_shader_flags(kg, &isect) &
|
||||
(SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
|
||||
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup shader data. */
|
||||
|
@@ -605,6 +605,13 @@ ccl_device_noinline
|
||||
if (hit) {
|
||||
t = ray->t;
|
||||
}
|
||||
else if (bounce == 0) {
|
||||
/* Restore original position if nothing was hit after the first bounce.
|
||||
* Otherwise if the ray_offset() to avoid self-intersection is relatively
|
||||
* large compared to the scattering radius, we go never backup high enough
|
||||
* to exit the surface. */
|
||||
ray->P = sd->P;
|
||||
}
|
||||
|
||||
/* Advance to new scatter location. */
|
||||
ray->P += t * ray->D;
|
||||
|
@@ -895,6 +895,8 @@ enum ShaderDataFlag {
|
||||
SD_HAS_CONSTANT_EMISSION = (1 << 27),
|
||||
/* Needs to access attributes for volume rendering */
|
||||
SD_NEED_VOLUME_ATTRIBUTES = (1 << 28),
|
||||
/* Shader has emission */
|
||||
SD_HAS_EMISSION = (1 << 29),
|
||||
|
||||
SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME |
|
||||
SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR |
|
||||
|
@@ -528,6 +528,8 @@ void ShaderManager::device_update_common(Device *device,
|
||||
|
||||
if (shader->get_use_mis())
|
||||
flag |= SD_USE_MIS;
|
||||
if (shader->has_surface_emission)
|
||||
flag |= SD_HAS_EMISSION;
|
||||
if (shader->has_surface_transparent && shader->get_use_transparent_shadow())
|
||||
flag |= SD_HAS_TRANSPARENT_SHADOW;
|
||||
if (shader->has_volume) {
|
||||
|
@@ -124,7 +124,7 @@ static struct StepTy {
|
||||
template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const type &a)
|
||||
{
|
||||
if (i0 == i1 && i0 == i2 && i0 == i3) {
|
||||
return vdupq_laneq_s32(a, i0);
|
||||
return type(vdupq_laneq_s32(int32x4_t(a), i0));
|
||||
}
|
||||
static const uint8_t tbl[16] = {(i0 * 4) + 0,
|
||||
(i0 * 4) + 1,
|
||||
@@ -143,7 +143,7 @@ template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const typ
|
||||
(i3 * 4) + 2,
|
||||
(i3 * 4) + 3};
|
||||
|
||||
return vqtbl1q_s8(int8x16_t(a), *(int8x16_t *)tbl);
|
||||
return type(vqtbl1q_s8(int8x16_t(a), *(uint8x16_t *)tbl));
|
||||
}
|
||||
|
||||
template<class type, int i0, int i1, int i2, int i3>
|
||||
@@ -167,7 +167,7 @@ type shuffle_neon(const type &a, const type &b)
|
||||
(i3 * 4) + 2,
|
||||
(i3 * 4) + 3};
|
||||
|
||||
return vqtbl1q_s8(int8x16_t(b), *(int8x16_t *)tbl);
|
||||
return type(vqtbl1q_s8(int8x16_t(b), *(uint8x16_t *)tbl));
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -188,7 +188,7 @@ type shuffle_neon(const type &a, const type &b)
|
||||
(i3 * 4) + 2 + 16,
|
||||
(i3 * 4) + 3 + 16};
|
||||
|
||||
return vqtbl2q_s8((int8x16x2_t){a, b}, *(int8x16_t *)tbl);
|
||||
return type(vqtbl2q_s8((int8x16x2_t){int8x16_t(a), int8x16_t(b)}, *(uint8x16_t *)tbl));
|
||||
}
|
||||
}
|
||||
#endif /* __KERNEL_NEON */
|
||||
|
@@ -283,7 +283,7 @@ __forceinline uint32_t popcnt(const sseb &a)
|
||||
{
|
||||
# if defined(__KERNEL_NEON__)
|
||||
const int32x4_t mask = {1, 1, 1, 1};
|
||||
int32x4_t t = vandq_s32(a.m128, mask);
|
||||
int32x4_t t = vandq_s32(vreinterpretq_s32_m128(a.m128), mask);
|
||||
return vaddvq_s32(t);
|
||||
# else
|
||||
return _mm_popcnt_u32(_mm_movemask_ps(a));
|
||||
@@ -299,7 +299,7 @@ __forceinline uint32_t popcnt(const sseb &a)
|
||||
__forceinline bool reduce_and(const sseb &a)
|
||||
{
|
||||
# if defined(__KERNEL_NEON__)
|
||||
return vaddvq_s32(a.m128) == -4;
|
||||
return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) == -4;
|
||||
# else
|
||||
return _mm_movemask_ps(a) == 0xf;
|
||||
# endif
|
||||
@@ -307,7 +307,7 @@ __forceinline bool reduce_and(const sseb &a)
|
||||
__forceinline bool reduce_or(const sseb &a)
|
||||
{
|
||||
# if defined(__KERNEL_NEON__)
|
||||
return vaddvq_s32(a.m128) != 0x0;
|
||||
return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) != 0x0;
|
||||
# else
|
||||
return _mm_movemask_ps(a) != 0x0;
|
||||
# endif
|
||||
@@ -315,7 +315,7 @@ __forceinline bool reduce_or(const sseb &a)
|
||||
__forceinline bool all(const sseb &b)
|
||||
{
|
||||
# if defined(__KERNEL_NEON__)
|
||||
return vaddvq_s32(b.m128) == -4;
|
||||
return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == -4;
|
||||
# else
|
||||
return _mm_movemask_ps(b) == 0xf;
|
||||
# endif
|
||||
@@ -323,7 +323,7 @@ __forceinline bool all(const sseb &b)
|
||||
__forceinline bool any(const sseb &b)
|
||||
{
|
||||
# if defined(__KERNEL_NEON__)
|
||||
return vaddvq_s32(b.m128) != 0x0;
|
||||
return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) != 0x0;
|
||||
# else
|
||||
return _mm_movemask_ps(b) != 0x0;
|
||||
# endif
|
||||
@@ -331,7 +331,7 @@ __forceinline bool any(const sseb &b)
|
||||
__forceinline bool none(const sseb &b)
|
||||
{
|
||||
# if defined(__KERNEL_NEON__)
|
||||
return vaddvq_s32(b.m128) == 0x0;
|
||||
return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == 0x0;
|
||||
# else
|
||||
return _mm_movemask_ps(b) == 0x0;
|
||||
# endif
|
||||
|
@@ -596,7 +596,7 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
|
||||
__forceinline const ssef shuffle(const ssef &b)
|
||||
{
|
||||
# ifdef __KERNEL_NEON__
|
||||
return shuffle_neon<ssef, i0, i1, i2, i3>(b.m128);
|
||||
return shuffle_neon<float32x4_t, i0, i1, i2, i3>(b.m128);
|
||||
# else
|
||||
return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(b), _MM_SHUFFLE(i3, i2, i1, i0)));
|
||||
# endif
|
||||
@@ -625,7 +625,7 @@ __forceinline const ssef shuffle(const ssef &a, const ssef &b)
|
||||
template<size_t i0> __forceinline const ssef shuffle(const ssef &a, const ssef &b)
|
||||
{
|
||||
# ifdef __KERNEL_NEON__
|
||||
return shuffle<float32x4_t, i0, i0, i0, i0>(a, b);
|
||||
return shuffle_neon<float32x4_t, i0, i0, i0, i0>(a, b);
|
||||
# else
|
||||
return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i0, i0, i0, i0));
|
||||
# endif
|
||||
|
@@ -446,7 +446,8 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
|
||||
__forceinline const ssei shuffle(const ssei &a)
|
||||
{
|
||||
# ifdef __KERNEL_NEON__
|
||||
return shuffle_neon<ssei, i0, i1, i2, i3>(a);
|
||||
int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a));
|
||||
return vreinterpretq_m128i_s32(result);
|
||||
# else
|
||||
return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
|
||||
# endif
|
||||
@@ -456,7 +457,9 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
|
||||
__forceinline const ssei shuffle(const ssei &a, const ssei &b)
|
||||
{
|
||||
# ifdef __KERNEL_NEON__
|
||||
return shuffle_neon<ssei, i0, i1, i2, i3>(a, b);
|
||||
int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a),
|
||||
vreinterpretq_s32_m128i(b));
|
||||
return vreinterpretq_m128i_s32(result);
|
||||
# else
|
||||
return _mm_castps_si128(
|
||||
_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(i3, i2, i1, i0)));
|
||||
@@ -514,7 +517,7 @@ __forceinline const ssei vreduce_add(const ssei &v)
|
||||
__forceinline int reduce_min(const ssei &v)
|
||||
{
|
||||
# ifdef __KERNEL_NEON__
|
||||
return vminvq_s32(v);
|
||||
return vminvq_s32(vreinterpretq_s32_m128i(v));
|
||||
# else
|
||||
return extract<0>(vreduce_min(v));
|
||||
# endif
|
||||
@@ -522,7 +525,7 @@ __forceinline int reduce_min(const ssei &v)
|
||||
__forceinline int reduce_max(const ssei &v)
|
||||
{
|
||||
# ifdef __KERNEL_NEON__
|
||||
return vmaxvq_s32(v);
|
||||
return vmaxvq_s32(vreinterpretq_s32_m128i(v));
|
||||
# else
|
||||
return extract<0>(vreduce_max(v));
|
||||
# endif
|
||||
@@ -530,7 +533,7 @@ __forceinline int reduce_max(const ssei &v)
|
||||
__forceinline int reduce_add(const ssei &v)
|
||||
{
|
||||
# ifdef __KERNEL_NEON__
|
||||
return vaddvq_s32(v);
|
||||
return vaddvq_s32(vreinterpretq_s32_m128i(v));
|
||||
# else
|
||||
return extract<0>(vreduce_add(v));
|
||||
# endif
|
||||
|
@@ -166,12 +166,33 @@ static void __cpuid(int data[4], int selector)
|
||||
|
||||
string system_cpu_brand_string()
|
||||
{
|
||||
#if !defined(WIN32) && !defined(__x86_64__) && !defined(__i386__)
|
||||
FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
|
||||
if (cpuinfo != nullptr) {
|
||||
char cpuinfo_buf[513] = "";
|
||||
fread(cpuinfo_buf, sizeof(cpuinfo_buf) - 1, 1, cpuinfo);
|
||||
fclose(cpuinfo);
|
||||
|
||||
char *modelname = strstr(cpuinfo_buf, "model name");
|
||||
if (modelname != nullptr) {
|
||||
modelname = strchr(modelname, ':');
|
||||
if (modelname != nullptr) {
|
||||
modelname += 2;
|
||||
char *modelname_end = strchr(modelname, '\n');
|
||||
if (modelname_end != nullptr) {
|
||||
*modelname_end = '\0';
|
||||
return modelname;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
char buf[49] = {0};
|
||||
int result[4] = {0};
|
||||
|
||||
__cpuid(result, 0x80000000);
|
||||
|
||||
if (result[0] >= (int)0x80000004) {
|
||||
if (result[0] != 0 && result[0] >= (int)0x80000004) {
|
||||
__cpuid((int *)(buf + 0), 0x80000002);
|
||||
__cpuid((int *)(buf + 16), 0x80000003);
|
||||
__cpuid((int *)(buf + 32), 0x80000004);
|
||||
@@ -183,7 +204,7 @@ string system_cpu_brand_string()
|
||||
|
||||
return brand;
|
||||
}
|
||||
|
||||
#endif
|
||||
return "Unknown CPU";
|
||||
}
|
||||
|
||||
|
Submodule release/scripts/addons updated: 81815ea92c...4cb833e84a
@@ -923,7 +923,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
# Perform a "natural sort", so 20 comes after 3 (for example).
|
||||
files.sort(
|
||||
key=lambda file_path:
|
||||
tuple(int(t) if t.isdigit() else t for t in re.split("(\d+)", file_path[0].lower())),
|
||||
tuple(int(t) if t.isdigit() else t for t in re.split(r"(\d+)", file_path[0].lower())),
|
||||
)
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
@@ -219,6 +219,40 @@ def RKS_GEN_scaling(_ksi, _context, ks, data):
|
||||
else:
|
||||
ks.paths.add(id_block, path)
|
||||
|
||||
|
||||
# Custom Properties
|
||||
def RKS_GEN_custom_props(_ksi, _context, ks, data):
|
||||
# get id-block and path info
|
||||
id_block, base_path, grouping = get_transform_generators_base_info(data)
|
||||
|
||||
# Only some RNA types can be animated.
|
||||
prop_type_compat = {bpy.types.BoolProperty,
|
||||
bpy.types.IntProperty,
|
||||
bpy.types.FloatProperty}
|
||||
|
||||
# When working with a pose, 'id_block' is the armature object (which should
|
||||
# get the animation data), whereas 'data' is the bone being keyed.
|
||||
for cprop_name in data.keys():
|
||||
# ignore special "_RNA_UI" used for UI editing
|
||||
if cprop_name == "_RNA_UI":
|
||||
continue
|
||||
|
||||
prop_path = '["%s"]' % bpy.utils.escape_identifier(cprop_name)
|
||||
try:
|
||||
rna_property = data.path_resolve(prop_path, False)
|
||||
except ValueError as ex:
|
||||
# This happens when a custom property is set to None. In that case it cannot
|
||||
# be converted to an FCurve-compatible value, so we can't keyframe it anyway.
|
||||
continue
|
||||
if rna_property.rna_type not in prop_type_compat:
|
||||
continue
|
||||
|
||||
path = "%s%s" % (base_path, prop_path)
|
||||
if grouping:
|
||||
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
|
||||
else:
|
||||
ks.paths.add(id_block, path)
|
||||
|
||||
# ------
|
||||
|
||||
|
||||
|
@@ -821,6 +821,12 @@ class GreasePencilLayerMasksPanel:
|
||||
col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="")
|
||||
col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="")
|
||||
|
||||
col2.separator()
|
||||
|
||||
sub = col2.column(align=True)
|
||||
sub.operator("gpencil.layer_mask_move", icon='TRIA_UP', text="").type = 'UP'
|
||||
sub.operator("gpencil.layer_mask_move", icon='TRIA_DOWN', text="").type = 'DOWN'
|
||||
|
||||
|
||||
class GreasePencilLayerRelationsPanel:
|
||||
|
||||
|
@@ -286,19 +286,17 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
mat = context.material
|
||||
lineart = mat.lineart
|
||||
|
||||
layout.prop(lineart, "use_transparency")
|
||||
|
||||
if lineart.use_transparency:
|
||||
|
||||
layout.label(text="Transparency Masks:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
for i in range(8):
|
||||
row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
|
||||
row = layout.row(align=True, heading="Masks")
|
||||
row.active = lineart.use_transparency
|
||||
for i in range(8):
|
||||
row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
|
||||
|
||||
|
||||
classes = (
|
||||
|
@@ -329,7 +329,9 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
|
||||
|
||||
row = layout.row(heading="Override Crease")
|
||||
row.prop(lineart, "use_crease_override", text="")
|
||||
row.prop(lineart, "crease_threshold", slider=True, text="")
|
||||
subrow = row.row()
|
||||
subrow.active = lineart.use_crease_override
|
||||
subrow.prop(lineart, "crease_threshold", slider=True, text="")
|
||||
|
||||
|
||||
class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel):
|
||||
|
@@ -92,16 +92,15 @@ class INFO_MT_area(Menu):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("screen.area_dupli", icon='WINDOW')
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("screen.screen_full_area")
|
||||
layout.operator(
|
||||
"screen.screen_full_area",
|
||||
text="Toggle Fullscreen Area",
|
||||
icon='FULLSCREEN_ENTER',
|
||||
).use_hide_panels = True
|
||||
text="Toggle Fullscreen Area").use_hide_panels = True
|
||||
layout.operator("screen.area_dupli")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("screen.area_close")
|
||||
|
||||
|
||||
class INFO_MT_context_menu(Menu):
|
||||
|
@@ -570,7 +570,7 @@ class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel):
|
||||
layout.prop(system, "audio_device", expand=False)
|
||||
|
||||
sub = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
|
||||
sub.active = system.audio_device not in {'NONE', 'Null'}
|
||||
sub.active = system.audio_device not in {'NONE', 'None'}
|
||||
sub.prop(system, "audio_channels", text="Channels")
|
||||
sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer")
|
||||
sub.prop(system, "audio_sample_rate", text="Sample Rate")
|
||||
|
@@ -44,6 +44,7 @@ ANIM_KS_LOCATION_ID = "Location"
|
||||
ANIM_KS_ROTATION_ID = "Rotation"
|
||||
ANIM_KS_SCALING_ID = "Scaling"
|
||||
ANIM_KS_LOC_ROT_SCALE_ID = "LocRotScale"
|
||||
ANIM_KS_LOC_ROT_SCALE_CPROP_ID = "LocRotScaleCProp"
|
||||
ANIM_KS_AVAILABLE_ID = "Available"
|
||||
ANIM_KS_WHOLE_CHARACTER_ID = "WholeCharacter"
|
||||
ANIM_KS_WHOLE_CHARACTER_SELECTED_ID = "WholeCharacterSelected"
|
||||
@@ -159,6 +160,22 @@ class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
|
||||
keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
|
||||
|
||||
|
||||
# LocRotScaleCProp
|
||||
class BUILTIN_KSI_LocRotScaleCProp(KeyingSetInfo):
|
||||
"""Key location/rotation/scale as well as custom properties"""
|
||||
bl_idname = ANIM_KS_LOC_ROT_SCALE_CPROP_ID
|
||||
bl_label = "Location, Rotation, Scale & Custom Properties"
|
||||
|
||||
poll = keyingsets_utils.RKS_POLL_selected_items
|
||||
iterator = keyingsets_utils.RKS_ITER_selected_item
|
||||
|
||||
def generate(self, context, ks, data):
|
||||
keyingsets_utils.RKS_GEN_location(self, context, ks, data)
|
||||
keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
|
||||
keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
|
||||
keyingsets_utils.RKS_GEN_custom_props(self, context, ks, data)
|
||||
|
||||
|
||||
# RotScale
|
||||
class BUILTIN_KSI_RotScale(KeyingSetInfo):
|
||||
"""Insert a keyframe on each of the rotation and scale channels"""
|
||||
@@ -350,7 +367,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
|
||||
bl_label = "Available"
|
||||
|
||||
# poll - selected objects or selected object with animation data
|
||||
def poll(ksi, context):
|
||||
def poll(self, context):
|
||||
ob = context.active_object
|
||||
if ob:
|
||||
# TODO: this fails if one animation-less object is active, but many others are selected
|
||||
@@ -366,14 +383,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
|
||||
|
||||
###############################
|
||||
|
||||
|
||||
# All properties that are likely to get animated in a character rig
|
||||
class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
|
||||
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
|
||||
"""(useful when blocking out a shot)"""
|
||||
bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
|
||||
bl_label = "Whole Character"
|
||||
|
||||
class WholeCharacterMixin:
|
||||
# these prefixes should be avoided, as they are not really bones
|
||||
# that animators should be touching (or need to touch)
|
||||
badBonePrefixes = (
|
||||
@@ -387,38 +397,37 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
|
||||
)
|
||||
|
||||
# poll - pose-mode on active object only
|
||||
def poll(ksi, context):
|
||||
def poll(self, context):
|
||||
return ((context.active_object) and (context.active_object.pose) and
|
||||
(context.active_object.mode == 'POSE'))
|
||||
|
||||
# iterator - all bones regardless of selection
|
||||
def iterator(ksi, context, ks):
|
||||
def iterator(self, context, ks):
|
||||
for bone in context.active_object.pose.bones:
|
||||
if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
|
||||
ksi.generate(context, ks, bone)
|
||||
if not bone.name.startswith(self.badBonePrefixes):
|
||||
self.generate(context, ks, bone)
|
||||
|
||||
# generator - all unlocked bone transforms + custom properties
|
||||
def generate(ksi, context, ks, bone):
|
||||
def generate(self, context, ks, bone):
|
||||
# loc, rot, scale - only include unlocked ones
|
||||
if not bone.bone.use_connect:
|
||||
ksi.doLoc(ks, bone)
|
||||
self.doLoc(ks, bone)
|
||||
|
||||
if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
|
||||
ksi.doRot4d(ks, bone)
|
||||
self.doRot4d(ks, bone)
|
||||
else:
|
||||
ksi.doRot3d(ks, bone)
|
||||
ksi.doScale(ks, bone)
|
||||
self.doRot3d(ks, bone)
|
||||
self.doScale(ks, bone)
|
||||
|
||||
# bbone properties?
|
||||
ksi.doBBone(context, ks, bone)
|
||||
self.doBBone(context, ks, bone)
|
||||
|
||||
# custom props?
|
||||
ksi.doCustomProps(ks, bone)
|
||||
|
||||
self.doCustomProps(ks, bone)
|
||||
# ----------------
|
||||
|
||||
# helper to add some bone's property to the Keying Set
|
||||
def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
|
||||
def addProp(self, ks, bone, prop, index=-1, use_groups=True):
|
||||
# add the property name to the base path
|
||||
id_path = bone.path_from_id()
|
||||
id_block = bone.id_data
|
||||
@@ -439,16 +448,16 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
|
||||
# ----------------
|
||||
|
||||
# location properties
|
||||
def doLoc(ksi, ks, bone):
|
||||
def doLoc(self, ks, bone):
|
||||
if bone.lock_location == (False, False, False):
|
||||
ksi.addProp(ks, bone, "location")
|
||||
self.addProp(ks, bone, "location")
|
||||
else:
|
||||
for i in range(3):
|
||||
if not bone.lock_location[i]:
|
||||
ksi.addProp(ks, bone, "location", i)
|
||||
self.addProp(ks, bone, "location", i)
|
||||
|
||||
# rotation properties
|
||||
def doRot4d(ksi, ks, bone):
|
||||
def doRot4d(self, ks, bone):
|
||||
# rotation mode affects the property used
|
||||
if bone.rotation_mode == 'QUATERNION':
|
||||
prop = "rotation_quaternion"
|
||||
@@ -459,40 +468,40 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
|
||||
if bone.lock_rotations_4d:
|
||||
# can check individually
|
||||
if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w is False):
|
||||
ksi.addProp(ks, bone, prop)
|
||||
self.addProp(ks, bone, prop)
|
||||
else:
|
||||
if bone.lock_rotation_w is False:
|
||||
ksi.addProp(ks, bone, prop, 0) # w = 0
|
||||
self.addProp(ks, bone, prop, 0) # w = 0
|
||||
|
||||
for i in range(3):
|
||||
if not bone.lock_rotation[i]:
|
||||
ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
|
||||
self.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
|
||||
elif True not in bone.lock_rotation:
|
||||
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
|
||||
# other than all open unless we keyframe the whole lot
|
||||
ksi.addProp(ks, bone, prop)
|
||||
self.addProp(ks, bone, prop)
|
||||
|
||||
def doRot3d(ksi, ks, bone):
|
||||
def doRot3d(self, ks, bone):
|
||||
if bone.lock_rotation == (False, False, False):
|
||||
ksi.addProp(ks, bone, "rotation_euler")
|
||||
self.addProp(ks, bone, "rotation_euler")
|
||||
else:
|
||||
for i in range(3):
|
||||
if not bone.lock_rotation[i]:
|
||||
ksi.addProp(ks, bone, "rotation_euler", i)
|
||||
self.addProp(ks, bone, "rotation_euler", i)
|
||||
|
||||
# scale properties
|
||||
def doScale(ksi, ks, bone):
|
||||
def doScale(self, ks, bone):
|
||||
if bone.lock_scale == (0, 0, 0):
|
||||
ksi.addProp(ks, bone, "scale")
|
||||
self.addProp(ks, bone, "scale")
|
||||
else:
|
||||
for i in range(3):
|
||||
if not bone.lock_scale[i]:
|
||||
ksi.addProp(ks, bone, "scale", i)
|
||||
self.addProp(ks, bone, "scale", i)
|
||||
|
||||
# ----------------
|
||||
|
||||
# bendy bone properties
|
||||
def doBBone(ksi, context, ks, pchan):
|
||||
def doBBone(self, context, ks, pchan):
|
||||
bone = pchan.bone
|
||||
|
||||
# This check is crude, but is the best we can do for now
|
||||
@@ -500,12 +509,12 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
|
||||
# (and the bone is a control bone). This may lead to some
|
||||
# false positives...
|
||||
if bone.bbone_segments > 1:
|
||||
keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan)
|
||||
keyingsets_utils.RKS_GEN_bendy_bones(self, context, ks, pchan)
|
||||
|
||||
# ----------------
|
||||
|
||||
# custom properties
|
||||
def doCustomProps(ksi, ks, bone):
|
||||
def doCustomProps(self, ks, bone):
|
||||
|
||||
prop_type_compat = {bpy.types.BoolProperty,
|
||||
bpy.types.IntProperty,
|
||||
@@ -528,39 +537,34 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
|
||||
# be converted to an FCurve-compatible value, so we can't keyframe it anyway.
|
||||
continue
|
||||
if rna_property.rna_type in prop_type_compat:
|
||||
ksi.addProp(ks, bone, prop_path)
|
||||
self.addProp(ks, bone, prop_path)
|
||||
elif prop_rna.is_animatable:
|
||||
ksi.addProp(ks, bone, prop)
|
||||
|
||||
# All properties that are likely to get animated in a character rig, only selected bones.
|
||||
self.addProp(ks, bone, prop)
|
||||
|
||||
|
||||
class BUILTIN_KSI_WholeCharacterSelected(KeyingSetInfo):
|
||||
class BUILTIN_KSI_WholeCharacter(WholeCharacterMixin, KeyingSetInfo):
|
||||
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
|
||||
"""(useful when blocking out a shot)"""
|
||||
bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
|
||||
bl_label = "Whole Character"
|
||||
|
||||
|
||||
class BUILTIN_KSI_WholeCharacterSelected(WholeCharacterMixin, KeyingSetInfo):
|
||||
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
|
||||
"""(only selected bones)"""
|
||||
bl_idname = ANIM_KS_WHOLE_CHARACTER_SELECTED_ID
|
||||
bl_label = "Whole Character (Selected Bones Only)"
|
||||
|
||||
# iterator - all bones regardless of selection
|
||||
def iterator(ksi, context, ks):
|
||||
def iterator(self, context, ks):
|
||||
# Use either the selected bones, or all of them if none are selected.
|
||||
bones = context.selected_pose_bones_from_active_object or context.active_object.pose.bones
|
||||
|
||||
for bone in bones:
|
||||
if bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
|
||||
if bone.name.startswith(self.badBonePrefixes):
|
||||
continue
|
||||
ksi.generate(context, ks, bone)
|
||||
self.generate(context, ks, bone)
|
||||
|
||||
# Poor man's subclassing. Blender breaks when we actually subclass BUILTIN_KSI_WholeCharacter.
|
||||
poll = BUILTIN_KSI_WholeCharacter.poll
|
||||
generate = BUILTIN_KSI_WholeCharacter.generate
|
||||
addProp = BUILTIN_KSI_WholeCharacter.addProp
|
||||
doLoc = BUILTIN_KSI_WholeCharacter.doLoc
|
||||
doRot4d = BUILTIN_KSI_WholeCharacter.doRot4d
|
||||
doRot3d = BUILTIN_KSI_WholeCharacter.doRot3d
|
||||
doScale = BUILTIN_KSI_WholeCharacter.doScale
|
||||
doBBone = BUILTIN_KSI_WholeCharacter.doBBone
|
||||
doCustomProps = BUILTIN_KSI_WholeCharacter.doCustomProps
|
||||
|
||||
###############################
|
||||
|
||||
@@ -578,7 +582,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo):
|
||||
iterator = keyingsets_utils.RKS_ITER_selected_objects
|
||||
|
||||
# generator - delta location channels only
|
||||
def generate(ksi, context, ks, data):
|
||||
def generate(self, context, ks, data):
|
||||
# get id-block and path info
|
||||
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
|
||||
|
||||
@@ -604,7 +608,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
|
||||
iterator = keyingsets_utils.RKS_ITER_selected_objects
|
||||
|
||||
# generator - delta location channels only
|
||||
def generate(ksi, context, ks, data):
|
||||
def generate(self, context, ks, data):
|
||||
# get id-block and path info
|
||||
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
|
||||
|
||||
@@ -638,7 +642,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
|
||||
iterator = keyingsets_utils.RKS_ITER_selected_objects
|
||||
|
||||
# generator - delta location channels only
|
||||
def generate(ksi, context, ks, data):
|
||||
def generate(self, context, ks, data):
|
||||
# get id-block and path info
|
||||
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
|
||||
|
||||
@@ -664,6 +668,7 @@ classes = (
|
||||
BUILTIN_KSI_Scaling,
|
||||
BUILTIN_KSI_LocRot,
|
||||
BUILTIN_KSI_LocRotScale,
|
||||
BUILTIN_KSI_LocRotScaleCProp,
|
||||
BUILTIN_KSI_LocScale,
|
||||
BUILTIN_KSI_RotScale,
|
||||
BUILTIN_KSI_DeltaLocation,
|
||||
|
@@ -504,6 +504,12 @@ geometry_node_categories = [
|
||||
NodeItem("ShaderNodeSeparateRGB"),
|
||||
NodeItem("ShaderNodeCombineRGB"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_CURVE", "Curve", items=[
|
||||
NodeItem("GeometryNodeCurveToMesh"),
|
||||
NodeItem("GeometryNodeTransformTest"),
|
||||
NodeItem("GeometryNodeCurveTrim"),
|
||||
NodeItem("GeometryNodeCurveSamplePoints"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
|
||||
NodeItem("GeometryNodeBoundBox"),
|
||||
NodeItem("GeometryNodeTransform"),
|
||||
@@ -550,6 +556,7 @@ geometry_node_categories = [
|
||||
NodeItem("ShaderNodeMath"),
|
||||
NodeItem("FunctionNodeBooleanMath"),
|
||||
NodeItem("FunctionNodeFloatCompare"),
|
||||
NodeItem("GeometryNodeSwitch"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
|
||||
NodeItem("ShaderNodeSeparateXYZ"),
|
||||
|
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_math_color_blend.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
@@ -640,18 +641,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
|
||||
(size_t)buf_info->ch);
|
||||
float *fbuf = buf_info->fbuf + buf_ofs;
|
||||
|
||||
if (a >= 1.0f) {
|
||||
fbuf[0] = b_col_float[0];
|
||||
fbuf[1] = b_col_float[1];
|
||||
fbuf[2] = b_col_float[2];
|
||||
fbuf[3] = 1.0f;
|
||||
}
|
||||
else {
|
||||
fbuf[0] = (b_col_float[0] * a) + (fbuf[0] * (1.0f - a));
|
||||
fbuf[1] = (b_col_float[1] * a) + (fbuf[1] * (1.0f - a));
|
||||
fbuf[2] = (b_col_float[2] * a) + (fbuf[2] * (1.0f - a));
|
||||
fbuf[3] = MIN2(fbuf[3] + a, 1.0f); /* clamp to 1.0 */
|
||||
}
|
||||
float font_pixel[4];
|
||||
font_pixel[0] = b_col_float[0] * a;
|
||||
font_pixel[1] = b_col_float[1] * a;
|
||||
font_pixel[2] = b_col_float[2] * a;
|
||||
font_pixel[3] = a;
|
||||
blend_color_mix_float(fbuf, fbuf, font_pixel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,19 +672,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
|
||||
(size_t)buf_info->ch);
|
||||
unsigned char *cbuf = buf_info->cbuf + buf_ofs;
|
||||
|
||||
if (a >= 1.0f) {
|
||||
cbuf[0] = b_col_char[0];
|
||||
cbuf[1] = b_col_char[1];
|
||||
cbuf[2] = b_col_char[2];
|
||||
cbuf[3] = 255;
|
||||
}
|
||||
else {
|
||||
cbuf[0] = (unsigned char)((b_col_char[0] * a) + (cbuf[0] * (1.0f - a)));
|
||||
cbuf[1] = (unsigned char)((b_col_char[1] * a) + (cbuf[1] * (1.0f - a)));
|
||||
cbuf[2] = (unsigned char)((b_col_char[2] * a) + (cbuf[2] * (1.0f - a)));
|
||||
/* clamp to 255 */
|
||||
cbuf[3] = (unsigned char)MIN2((int)cbuf[3] + (int)(a * 255), 255);
|
||||
}
|
||||
uchar font_pixel[4];
|
||||
font_pixel[0] = b_col_char[0];
|
||||
font_pixel[1] = b_col_char[1];
|
||||
font_pixel[2] = b_col_char[2];
|
||||
font_pixel[3] = unit_float_to_uchar_clamp(a);
|
||||
blend_color_mix_byte(cbuf, cbuf, font_pixel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "FN_cpp_type.hh"
|
||||
#include "FN_generic_span.hh"
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
@@ -30,6 +31,10 @@
|
||||
namespace blender::bke {
|
||||
|
||||
using fn::CPPType;
|
||||
using fn::GVArray;
|
||||
using fn::GVArrayPtr;
|
||||
using fn::GVMutableArray;
|
||||
using fn::GVMutableArrayPtr;
|
||||
|
||||
const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
|
||||
CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
|
||||
@@ -37,112 +42,97 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
|
||||
AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
|
||||
|
||||
/**
|
||||
* This class offers an indirection for reading an attribute.
|
||||
* This is useful for the following reasons:
|
||||
* - Blender does not store all attributes the same way.
|
||||
* The simplest case are custom data layers with primitive types.
|
||||
* A bit more complex are mesh attributes like the position of vertices,
|
||||
* which are embedded into the MVert struct.
|
||||
* Even more complex to access are vertex weights.
|
||||
* - Sometimes attributes are stored on one domain, but we want to access
|
||||
* the attribute on a different domain. Therefore, we have to interpolate
|
||||
* between the domains.
|
||||
* Used when looking up a "plain attribute" based on a name for reading from it.
|
||||
*/
|
||||
class ReadAttribute {
|
||||
protected:
|
||||
const AttributeDomain domain_;
|
||||
const CPPType &cpp_type_;
|
||||
const CustomDataType custom_data_type_;
|
||||
const int64_t size_;
|
||||
struct ReadAttributeLookup {
|
||||
/* The virtual array that is used to read from this attribute. */
|
||||
GVArrayPtr varray;
|
||||
/* Domain the attribute lives on in the geometry. */
|
||||
AttributeDomain domain;
|
||||
|
||||
/* Protects the span below, so that no two threads initialize it at the same time. */
|
||||
mutable std::mutex span_mutex_;
|
||||
/* When it is not null, it points to the attribute array or a temporary array that contains all
|
||||
* the attribute values. */
|
||||
mutable void *array_buffer_ = nullptr;
|
||||
/* Is true when the buffer above is owned by the attribute accessor. */
|
||||
mutable bool array_is_temporary_ = false;
|
||||
|
||||
public:
|
||||
ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
|
||||
: domain_(domain),
|
||||
cpp_type_(cpp_type),
|
||||
custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
|
||||
size_(size)
|
||||
/* Convenience function to check if the attribute has been found. */
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray.get() != nullptr;
|
||||
}
|
||||
|
||||
virtual ~ReadAttribute();
|
||||
|
||||
AttributeDomain domain() const
|
||||
{
|
||||
return domain_;
|
||||
}
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return cpp_type_;
|
||||
}
|
||||
|
||||
CustomDataType custom_data_type() const
|
||||
{
|
||||
return custom_data_type_;
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->get_internal(index, r_value);
|
||||
}
|
||||
|
||||
/* Get a span that contains all attribute values. */
|
||||
fn::GSpan get_span() const;
|
||||
|
||||
template<typename T> Span<T> get_span() const
|
||||
{
|
||||
return this->get_span().typed<T>();
|
||||
}
|
||||
|
||||
protected:
|
||||
/* r_value is expected to be uninitialized. */
|
||||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
|
||||
virtual void initialize_span() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* This exists for similar reasons as the ReadAttribute class, except that
|
||||
* it does not deal with interpolation between domains.
|
||||
* Used when looking up a "plain attribute" based on a name for reading from it and writing to it.
|
||||
*/
|
||||
class WriteAttribute {
|
||||
protected:
|
||||
const AttributeDomain domain_;
|
||||
const CPPType &cpp_type_;
|
||||
const CustomDataType custom_data_type_;
|
||||
const int64_t size_;
|
||||
struct WriteAttributeLookup {
|
||||
/* The virtual array that is used to read from and write to the attribute. */
|
||||
GVMutableArrayPtr varray;
|
||||
/* Domain the attributes lives on in the geometry. */
|
||||
AttributeDomain domain;
|
||||
|
||||
/* When not null, this points either to the attribute array or to a temporary array. */
|
||||
void *array_buffer_ = nullptr;
|
||||
/* True, when the buffer points to a temporary array. */
|
||||
bool array_is_temporary_ = false;
|
||||
/* This helps to protect against forgetting to apply changes done to the array. */
|
||||
bool array_should_be_applied_ = false;
|
||||
/* Convenience function to check if the attribute has been found. */
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray.get() != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An output attribute allows writing to an attribute (and optionally reading as well). It adds
|
||||
* some convenience features on top of `GVMutableArray` that are very commonly used.
|
||||
*
|
||||
* Supported convenience features:
|
||||
* - Implicit type conversion when writing to builtin attributes.
|
||||
* - Supports simple access to a span containing the attribute values (that avoids the use of
|
||||
* VMutableArray_Span in many cases).
|
||||
* - An output attribute can live side by side with an existing attribute with a different domain
|
||||
* or data type. The old attribute will only be overwritten when the #save function is called.
|
||||
*/
|
||||
class OutputAttribute {
|
||||
public:
|
||||
using SaveFn = std::function<void(OutputAttribute &)>;
|
||||
|
||||
private:
|
||||
GVMutableArrayPtr varray_;
|
||||
AttributeDomain domain_;
|
||||
SaveFn save_;
|
||||
std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
|
||||
bool ignore_old_values_ = false;
|
||||
bool save_has_been_called_ = false;
|
||||
|
||||
public:
|
||||
WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
|
||||
: domain_(domain),
|
||||
cpp_type_(cpp_type),
|
||||
custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
|
||||
size_(size)
|
||||
OutputAttribute() = default;
|
||||
|
||||
OutputAttribute(GVMutableArrayPtr varray,
|
||||
AttributeDomain domain,
|
||||
SaveFn save,
|
||||
const bool ignore_old_values)
|
||||
: varray_(std::move(varray)),
|
||||
domain_(domain),
|
||||
save_(std::move(save)),
|
||||
ignore_old_values_(ignore_old_values)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~WriteAttribute();
|
||||
OutputAttribute(OutputAttribute &&other) = default;
|
||||
|
||||
~OutputAttribute();
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return varray_.get() != nullptr;
|
||||
}
|
||||
|
||||
GVMutableArray &operator*()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
GVMutableArray *operator->()
|
||||
{
|
||||
return varray_.get();
|
||||
}
|
||||
|
||||
GVMutableArray &varray()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
AttributeDomain domain() const
|
||||
{
|
||||
@@ -151,168 +141,94 @@ class WriteAttribute {
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return cpp_type_;
|
||||
return varray_->type();
|
||||
}
|
||||
|
||||
CustomDataType custom_data_type() const
|
||||
{
|
||||
return custom_data_type_;
|
||||
return cpp_type_to_custom_data_type(this->cpp_type());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
fn::GMutableSpan as_span()
|
||||
{
|
||||
return size_;
|
||||
if (!optional_span_varray_.has_value()) {
|
||||
const bool materialize_old_values = !ignore_old_values_;
|
||||
optional_span_varray_.emplace(*varray_, materialize_old_values);
|
||||
}
|
||||
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
|
||||
return span_varray;
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const
|
||||
template<typename T> MutableSpan<T> as_span()
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->get_internal(index, r_value);
|
||||
return this->as_span().typed<T>();
|
||||
}
|
||||
|
||||
void set(const int64_t index, const void *value)
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->set_internal(index, value);
|
||||
}
|
||||
|
||||
/* Get a span that new attribute values can be written into. When all values have been changed,
|
||||
* #apply_span has to be called. */
|
||||
fn::GMutableSpan get_span();
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
fn::GMutableSpan get_span_for_write_only();
|
||||
/* Write the changes to the span into the actual attribute, if they aren't already. */
|
||||
void apply_span();
|
||||
|
||||
template<typename T> MutableSpan<T> get_span()
|
||||
{
|
||||
return this->get_span().typed<T>();
|
||||
}
|
||||
|
||||
template<typename T> MutableSpan<T> get_span_for_write_only()
|
||||
{
|
||||
return this->get_span_for_write_only().typed<T>();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
virtual void set_internal(const int64_t index, const void *value) = 0;
|
||||
|
||||
virtual void initialize_span(const bool write_only);
|
||||
virtual void apply_span_if_necessary();
|
||||
void save();
|
||||
};
|
||||
|
||||
using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
|
||||
using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
|
||||
|
||||
/* This provides type safe access to an attribute.
|
||||
* The underlying ReadAttribute is owned optionally. */
|
||||
template<typename T> class TypedReadAttribute {
|
||||
/**
|
||||
* Same as OutputAttribute, but should be used when the data type is known at compile time.
|
||||
*/
|
||||
template<typename T> class OutputAttribute_Typed {
|
||||
private:
|
||||
std::unique_ptr<const ReadAttribute> owned_attribute_;
|
||||
const ReadAttribute *attribute_;
|
||||
OutputAttribute attribute_;
|
||||
std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
|
||||
VMutableArray<T> *varray_ = nullptr;
|
||||
|
||||
public:
|
||||
TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute)
|
||||
OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
|
||||
{
|
||||
owned_attribute_ = std::move(attribute);
|
||||
BLI_assert(owned_attribute_);
|
||||
if (attribute_) {
|
||||
optional_varray_.emplace(attribute_.varray());
|
||||
varray_ = &**optional_varray_;
|
||||
}
|
||||
}
|
||||
|
||||
TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute)
|
||||
operator bool() const
|
||||
{
|
||||
BLI_assert(attribute_->cpp_type().is<T>());
|
||||
return varray_ != nullptr;
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
VMutableArray<T> &operator*()
|
||||
{
|
||||
return attribute_->size();
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
VMutableArray<T> *operator->()
|
||||
{
|
||||
BLI_assert(index < attribute_->size());
|
||||
T value;
|
||||
value.~T();
|
||||
attribute_->get(index, &value);
|
||||
return value;
|
||||
return varray_;
|
||||
}
|
||||
|
||||
/* Get a span to that contains all attribute values for faster and more convenient access. */
|
||||
Span<T> get_span() const
|
||||
VMutableArray<T> &varray()
|
||||
{
|
||||
return attribute_->get_span().template typed<T>();
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
AttributeDomain domain() const
|
||||
{
|
||||
return attribute_.domain();
|
||||
}
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return CPPType::get<T>();
|
||||
}
|
||||
|
||||
CustomDataType custom_data_type() const
|
||||
{
|
||||
return cpp_type_to_custom_data_type(this->cpp_type());
|
||||
}
|
||||
|
||||
MutableSpan<T> as_span()
|
||||
{
|
||||
return attribute_.as_span<T>();
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
attribute_.save();
|
||||
}
|
||||
};
|
||||
|
||||
/* This provides type safe access to an attribute.
|
||||
* The underlying WriteAttribute is owned optionally. */
|
||||
template<typename T> class TypedWriteAttribute {
|
||||
private:
|
||||
std::unique_ptr<WriteAttribute> owned_attribute_;
|
||||
WriteAttribute *attribute_;
|
||||
|
||||
public:
|
||||
TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute)
|
||||
{
|
||||
owned_attribute_ = std::move(attribute);
|
||||
BLI_assert(owned_attribute_);
|
||||
}
|
||||
|
||||
TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute)
|
||||
{
|
||||
BLI_assert(attribute_->cpp_type().is<T>());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return attribute_->size();
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index < attribute_->size());
|
||||
T value;
|
||||
value.~T();
|
||||
attribute_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(const int64_t index, const T &value)
|
||||
{
|
||||
attribute_->set(index, &value);
|
||||
}
|
||||
|
||||
/* Get a span that new values can be written into. Once all values have been updated #apply_span
|
||||
* has to be called. */
|
||||
MutableSpan<T> get_span()
|
||||
{
|
||||
return attribute_->get_span().typed<T>();
|
||||
}
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
MutableSpan<T> get_span_for_write_only()
|
||||
{
|
||||
return attribute_->get_span_for_write_only().typed<T>();
|
||||
}
|
||||
|
||||
/* Write back all changes to the actual attribute, if necessary. */
|
||||
void apply_span()
|
||||
{
|
||||
attribute_->apply_span();
|
||||
}
|
||||
};
|
||||
|
||||
using BooleanReadAttribute = TypedReadAttribute<bool>;
|
||||
using FloatReadAttribute = TypedReadAttribute<float>;
|
||||
using Float2ReadAttribute = TypedReadAttribute<float2>;
|
||||
using Float3ReadAttribute = TypedReadAttribute<float3>;
|
||||
using Int32ReadAttribute = TypedReadAttribute<int>;
|
||||
using Color4fReadAttribute = TypedReadAttribute<Color4f>;
|
||||
using BooleanWriteAttribute = TypedWriteAttribute<bool>;
|
||||
using FloatWriteAttribute = TypedWriteAttribute<float>;
|
||||
using Float2WriteAttribute = TypedWriteAttribute<float2>;
|
||||
using Float3WriteAttribute = TypedWriteAttribute<float3>;
|
||||
using Int32WriteAttribute = TypedWriteAttribute<int>;
|
||||
using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@@ -14,6 +14,8 @@
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_float2.hh"
|
||||
@@ -21,13 +23,17 @@
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "FN_cpp_type.hh"
|
||||
|
||||
namespace blender::attribute_math {
|
||||
|
||||
using fn::CPPType;
|
||||
|
||||
/**
|
||||
* Utility function that simplifies calling a templated function based on a custom data type.
|
||||
*/
|
||||
template<typename Func>
|
||||
void convert_to_static_type(const CustomDataType data_type, const Func &func)
|
||||
inline void convert_to_static_type(const CustomDataType data_type, const Func &func)
|
||||
{
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT:
|
||||
@@ -54,6 +60,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func)
|
||||
{
|
||||
if (cpp_type.is<float>()) {
|
||||
func(float());
|
||||
}
|
||||
else if (cpp_type.is<float2>()) {
|
||||
func(float2());
|
||||
}
|
||||
else if (cpp_type.is<float3>()) {
|
||||
func(float3());
|
||||
}
|
||||
else if (cpp_type.is<int>()) {
|
||||
func(int());
|
||||
}
|
||||
else if (cpp_type.is<bool>()) {
|
||||
func(bool());
|
||||
}
|
||||
else if (cpp_type.is<Color4f>()) {
|
||||
func(Color4f());
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mix three values of the same type.
|
||||
*
|
||||
|
@@ -112,7 +112,9 @@ bool BKE_scene_collections_object_remove(struct Main *bmain,
|
||||
struct Object *object,
|
||||
const bool free_us);
|
||||
void BKE_collections_object_remove_nulls(struct Main *bmain);
|
||||
void BKE_collections_child_remove_nulls(struct Main *bmain, struct Collection *old_collection);
|
||||
void BKE_collections_child_remove_nulls(struct Main *bmain,
|
||||
struct Collection *parent_collection,
|
||||
struct Collection *child_collection);
|
||||
|
||||
/* Dependencies. */
|
||||
|
||||
|
@@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area);
|
||||
void CTX_wm_region_set(bContext *C, struct ARegion *region);
|
||||
void CTX_wm_menu_set(bContext *C, struct ARegion *menu);
|
||||
void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup);
|
||||
const char *CTX_wm_operator_poll_msg_get(struct bContext *C);
|
||||
|
||||
/**
|
||||
* Values to create the message that describes the reason poll failed.
|
||||
*
|
||||
* \note This must be called in the same context as the poll function that created it.
|
||||
*/
|
||||
struct bContextPollMsgDyn_Params {
|
||||
/** The result is allocated . */
|
||||
char *(*get_fn)(bContext *C, void *user_data);
|
||||
/** Optionally free the user-data. */
|
||||
void (*free_fn)(bContext *C, void *user_data);
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free);
|
||||
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg);
|
||||
void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
|
||||
const struct bContextPollMsgDyn_Params *params);
|
||||
void CTX_wm_operator_poll_msg_clear(struct bContext *C);
|
||||
|
||||
/* Data Context
|
||||
*
|
||||
|
@@ -36,6 +36,7 @@ typedef enum GeometryComponentType {
|
||||
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
|
||||
GEO_COMPONENT_TYPE_INSTANCES = 2,
|
||||
GEO_COMPONENT_TYPE_VOLUME = 3,
|
||||
GEO_COMPONENT_TYPE_CURVE = 4,
|
||||
} GeometryComponentType;
|
||||
|
||||
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
|
||||
|
@@ -34,11 +34,13 @@
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_geometry_set.h"
|
||||
|
||||
struct SplineGroup;
|
||||
struct Collection;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct PointCloud;
|
||||
struct Volume;
|
||||
struct SplineGroup;
|
||||
|
||||
enum class GeometryOwnershipType {
|
||||
/* The geometry is owned. This implies that it can be changed. */
|
||||
@@ -55,60 +57,6 @@ class ComponentAttributeProviders;
|
||||
|
||||
class GeometryComponent;
|
||||
|
||||
/**
|
||||
* An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final
|
||||
* destination yet. Therefore, once the attribute has been filled with data, the #save method has
|
||||
* to be called, to store the attribute where it belongs (possibly by replacing an existing
|
||||
* attribute with the same name).
|
||||
*
|
||||
* This is useful for example in the Attribute Color Ramp node, when the same attribute name is
|
||||
* used as input and output. Typically the input is a float attribute, and the output is a color.
|
||||
* Those two attributes cannot exist at the same time, due to a name collision. To handle this
|
||||
* situation well, first the output colors have to be computed before the input floats are deleted.
|
||||
* Therefore, the outputs have to be written to a temporary buffer that replaces the existing
|
||||
* attribute once all computations are done.
|
||||
*/
|
||||
class OutputAttributePtr {
|
||||
private:
|
||||
blender::bke::WriteAttributePtr attribute_;
|
||||
|
||||
public:
|
||||
OutputAttributePtr() = default;
|
||||
OutputAttributePtr(blender::bke::WriteAttributePtr attribute);
|
||||
OutputAttributePtr(GeometryComponent &component,
|
||||
AttributeDomain domain,
|
||||
std::string name,
|
||||
CustomDataType data_type);
|
||||
|
||||
~OutputAttributePtr();
|
||||
|
||||
/* Returns false, when this wrapper is empty. */
|
||||
operator bool() const
|
||||
{
|
||||
return static_cast<bool>(attribute_);
|
||||
}
|
||||
|
||||
/* Get a reference to the underlying #WriteAttribute. */
|
||||
blender::bke::WriteAttribute &get()
|
||||
{
|
||||
BLI_assert(attribute_);
|
||||
return *attribute_;
|
||||
}
|
||||
|
||||
blender::bke::WriteAttribute &operator*()
|
||||
{
|
||||
return *attribute_;
|
||||
}
|
||||
|
||||
blender::bke::WriteAttribute *operator->()
|
||||
{
|
||||
return attribute_.get();
|
||||
}
|
||||
|
||||
void save();
|
||||
void apply_span_and_save();
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains information about an attribute in a geometry component.
|
||||
* More information can be added in the future. E.g. whether the attribute is builtin and how it is
|
||||
@@ -123,6 +71,65 @@ struct AttributeMetaData {
|
||||
using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
|
||||
const AttributeMetaData &meta_data)>;
|
||||
|
||||
/**
|
||||
* Base class for the attribute intializer types described below.
|
||||
*/
|
||||
struct AttributeInit {
|
||||
enum class Type {
|
||||
Default,
|
||||
VArray,
|
||||
MoveArray,
|
||||
};
|
||||
Type type;
|
||||
AttributeInit(const Type type) : type(type)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute using the default value for the data type.
|
||||
* The default values may depend on the attribute provider implementation.
|
||||
*/
|
||||
struct AttributeInitDefault : public AttributeInit {
|
||||
AttributeInitDefault() : AttributeInit(Type::Default)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute by copying data from an existing virtual array. The virtual array
|
||||
* must have the same type as the newly created attribute.
|
||||
*
|
||||
* Note that this can be used to fill the new attribute with the default
|
||||
*/
|
||||
struct AttributeInitVArray : public AttributeInit {
|
||||
const blender::fn::GVArray *varray;
|
||||
|
||||
AttributeInitVArray(const blender::fn::GVArray *varray)
|
||||
: AttributeInit(Type::VArray), varray(varray)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
|
||||
* Sometimes data is created before a geometry component is available. In that case, it's
|
||||
* preferable to move data directly to the created attribute to avoid a new allocation and a copy.
|
||||
*
|
||||
* Note that this will only have a benefit for attributes that are stored directly as contiguous
|
||||
* arrays, so not for some built-in attributes.
|
||||
*
|
||||
* The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
|
||||
* can't be used directly, and that is generally how Blender expects custom data to be allocated.
|
||||
*/
|
||||
struct AttributeInitMove : public AttributeInit {
|
||||
void *data = nullptr;
|
||||
|
||||
AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the base class for specialized geometry component types.
|
||||
*/
|
||||
@@ -156,26 +163,34 @@ class GeometryComponent {
|
||||
/* Return true when any attribute with this name exists, including built in attributes. */
|
||||
bool attribute_exists(const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Return the data type and domain of an attribute with the given name if it exists. */
|
||||
std::optional<AttributeMetaData> attribute_get_meta_data(
|
||||
const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Returns true when the geometry component supports this attribute domain. */
|
||||
bool attribute_domain_supported(const AttributeDomain domain) const;
|
||||
/* Can only be used with supported domain types. */
|
||||
virtual int attribute_domain_size(const AttributeDomain domain) const;
|
||||
|
||||
bool attribute_is_builtin(const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Get read-only access to the highest priority attribute with the given name.
|
||||
* Returns null if the attribute does not exist. */
|
||||
blender::bke::ReadAttributePtr attribute_try_get_for_read(
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Get read and write access to the highest priority attribute with the given name.
|
||||
* Returns null if the attribute does not exist. */
|
||||
blender::bke::WriteAttributePtr attribute_try_get_for_write(
|
||||
blender::bke::WriteAttributeLookup attribute_try_get_for_write(
|
||||
const blender::StringRef attribute_name);
|
||||
|
||||
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
|
||||
* interpolate from one domain to another.
|
||||
* Returns null if the interpolation is not implemented. */
|
||||
virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
|
||||
blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const;
|
||||
virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
|
||||
std::unique_ptr<blender::fn::GVArray> varray,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const;
|
||||
|
||||
/* Returns true when the attribute has been deleted. */
|
||||
bool attribute_try_delete(const blender::StringRef attribute_name);
|
||||
@@ -183,82 +198,104 @@ class GeometryComponent {
|
||||
/* Returns true when the attribute has been created. */
|
||||
bool attribute_try_create(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type);
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
/* Try to create the builtin attribute with the given name. No data type or domain has to be
|
||||
* provided, because those are fixed for builtin attributes. */
|
||||
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
blender::Set<std::string> attribute_names() const;
|
||||
bool attribute_foreach(const AttributeForeachCallback callback) const;
|
||||
|
||||
virtual bool is_empty() const;
|
||||
|
||||
/* Get a read-only attribute for the given domain and data type.
|
||||
* Returns null when it does not exist. */
|
||||
blender::bke::ReadAttributePtr attribute_try_get_for_read(
|
||||
/* Get a virtual array to read the data of an attribute on the given domain and data type.
|
||||
* Returns null when the attribute does not exist or cannot be converted to the requested domain
|
||||
* and data type. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const;
|
||||
|
||||
/* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
|
||||
* Returns null when the attribute does not exist. */
|
||||
blender::bke::ReadAttributePtr attribute_try_get_for_read(
|
||||
/* Get a virtual array to read the data of an attribute on the given domain. The data type is
|
||||
* left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
|
||||
* requested domain. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain) const;
|
||||
|
||||
/* Get a read-only attribute for the given domain and data type.
|
||||
* Returns a constant attribute based on the default value if the attribute does not exist.
|
||||
* Never returns null. */
|
||||
blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const;
|
||||
/* Get a virtual array to read data of an attribute with the given data type. The domain is
|
||||
* left unchanged. Returns null when the attribute does not exist or cannot be converted to the
|
||||
* requested data type. */
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name, const CustomDataType data_type) const;
|
||||
|
||||
/* Get a typed read-only attribute for the given domain and type. */
|
||||
template<typename T>
|
||||
blender::bke::TypedReadAttribute<T> attribute_get_for_read(
|
||||
/* Get a virtual array to read the data of an attribute. If that is not possible, the returned
|
||||
* virtual array will contain a default value. This never returns null. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
|
||||
}
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr) const;
|
||||
|
||||
/* Get a read-only dummy attribute that always returns the same value. */
|
||||
blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *value) const;
|
||||
|
||||
/* Create a read-only dummy attribute that always returns the same value.
|
||||
* The given value is converted to the correct type if necessary. */
|
||||
blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted(
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType in_data_type,
|
||||
const CustomDataType out_data_type,
|
||||
const void *value) const;
|
||||
|
||||
/* Get a read-only dummy attribute that always returns the same value. */
|
||||
/* Should be used instead of the method above when the requested data type is known at compile
|
||||
* time for better type safety. */
|
||||
template<typename T>
|
||||
blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
|
||||
const T &value) const
|
||||
blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_get_constant_for_read(domain, type, &value);
|
||||
std::unique_ptr varray = this->attribute_get_for_read(
|
||||
attribute_name, domain, type, &default_value);
|
||||
return blender::fn::GVArray_Typed<T>(std::move(varray));
|
||||
}
|
||||
|
||||
/**
|
||||
* If an attribute with the given params exist, it is returned.
|
||||
* If no attribute with the given name exists, create it and
|
||||
* fill it with the default value if it is provided.
|
||||
* If an attribute with the given name but different domain or type exists, a temporary attribute
|
||||
* is created that has to be saved after the output has been computed. This avoids deleting
|
||||
* another attribute, before a computation is finished.
|
||||
* Returns an "output attribute", which is essentially a mutable virtual array with some commonly
|
||||
* used convince features. The returned output attribute might be empty if requested attribute
|
||||
* cannot exist on the geometry.
|
||||
*
|
||||
* This might return no attribute when the attribute cannot exist on the component.
|
||||
* The included convenience features are:
|
||||
* - Implicit type conversion when writing to builtin attributes.
|
||||
* - If the attribute name exists already, but has a different type/domain, a temporary attribute
|
||||
* is created that will overwrite the existing attribute in the end.
|
||||
*/
|
||||
OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr);
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr);
|
||||
|
||||
/* Same as attribute_try_get_for_output, but should be used when the original values in the
|
||||
* attributes are not read, i.e. the attribute is used only for output. Since values are not read
|
||||
* from this attribute, no default value is necessary. */
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type);
|
||||
|
||||
/* Statically typed method corresponding to the equally named generic one. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value);
|
||||
}
|
||||
|
||||
/* Statically typed method corresponding to the equally named generic one. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name, const AttributeDomain domain)
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
|
||||
@@ -328,23 +365,34 @@ struct GeometrySet {
|
||||
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
static GeometrySet create_with_pointcloud(
|
||||
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
static GeometrySet create_with_curve(
|
||||
SplineGroup *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
|
||||
/* Utility methods for access. */
|
||||
bool has_mesh() const;
|
||||
bool has_pointcloud() const;
|
||||
bool has_instances() const;
|
||||
bool has_volume() const;
|
||||
bool has_curve() const;
|
||||
|
||||
const Mesh *get_mesh_for_read() const;
|
||||
const PointCloud *get_pointcloud_for_read() const;
|
||||
const Volume *get_volume_for_read() const;
|
||||
const SplineGroup *get_curve_for_read() const;
|
||||
|
||||
Mesh *get_mesh_for_write();
|
||||
PointCloud *get_pointcloud_for_write();
|
||||
Volume *get_volume_for_write();
|
||||
SplineGroup *get_curve_for_write();
|
||||
|
||||
/* Utility methods for replacement. */
|
||||
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
void replace_pointcloud(PointCloud *pointcloud,
|
||||
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
void replace_volume(Volume *volume,
|
||||
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
void replace_curve(SplineGroup *mesh,
|
||||
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
};
|
||||
|
||||
/** A geometry component that can store a mesh. */
|
||||
@@ -377,8 +425,10 @@ class MeshComponent : public GeometryComponent {
|
||||
Mesh *get_for_write();
|
||||
|
||||
int attribute_domain_size(const AttributeDomain domain) const final;
|
||||
blender::bke::ReadAttributePtr attribute_try_adapt_domain(
|
||||
blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final;
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
|
||||
std::unique_ptr<blender::fn::GVArray> varray,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
@@ -424,6 +474,38 @@ class PointCloudComponent : public GeometryComponent {
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
};
|
||||
|
||||
/** A geometry component that stores curve data, in other words, a group of splines. */
|
||||
class CurveComponent : public GeometryComponent {
|
||||
private:
|
||||
SplineGroup *curve_ = nullptr;
|
||||
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
|
||||
|
||||
public:
|
||||
CurveComponent();
|
||||
~CurveComponent();
|
||||
GeometryComponent *copy() const override;
|
||||
|
||||
void clear();
|
||||
bool has_curve() const;
|
||||
void replace(SplineGroup *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
SplineGroup *release();
|
||||
|
||||
const SplineGroup *get_for_read() const;
|
||||
SplineGroup *get_for_write();
|
||||
|
||||
int attribute_domain_size(const AttributeDomain domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
bool owns_direct_data() const override;
|
||||
void ensure_owns_direct_data() override;
|
||||
|
||||
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
|
||||
|
||||
private:
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
};
|
||||
|
||||
/** A geometry component that stores instances. */
|
||||
class InstancesComponent : public GeometryComponent {
|
||||
private:
|
||||
|
55
source/blender/blenkernel/BKE_mesh_sample.hh
Normal file
55
source/blender/blenkernel/BKE_mesh_sample.hh
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
struct Mesh;
|
||||
|
||||
namespace blender::bke::mesh_surface_sample {
|
||||
|
||||
using fn::CPPType;
|
||||
using fn::GMutableSpan;
|
||||
using fn::GSpan;
|
||||
using fn::GVArray;
|
||||
|
||||
void sample_point_attribute(const Mesh &mesh,
|
||||
Span<int> looptri_indices,
|
||||
Span<float3> bary_coords,
|
||||
const GVArray &data_in,
|
||||
GMutableSpan data_out);
|
||||
|
||||
void sample_corner_attribute(const Mesh &mesh,
|
||||
Span<int> looptri_indices,
|
||||
Span<float3> bary_coords,
|
||||
const GVArray &data_in,
|
||||
GMutableSpan data_out);
|
||||
|
||||
void sample_face_attribute(const Mesh &mesh,
|
||||
Span<int> looptri_indices,
|
||||
const GVArray &data_in,
|
||||
GMutableSpan data_out);
|
||||
|
||||
} // namespace blender::bke::mesh_surface_sample
|
@@ -258,10 +258,6 @@ typedef struct ModifierTypeInfo {
|
||||
const struct ModifierEvalContext *ctx,
|
||||
struct GeometrySet *geometry_set);
|
||||
|
||||
struct Volume *(*modifyVolume)(struct ModifierData *md,
|
||||
const struct ModifierEvalContext *ctx,
|
||||
struct Volume *volume);
|
||||
|
||||
/********************* Optional functions *********************/
|
||||
|
||||
/**
|
||||
|
@@ -1413,6 +1413,11 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
|
||||
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
|
||||
#define GEO_NODE_BOUNDING_BOX 1042
|
||||
#define GEO_NODE_SWITCH 1043
|
||||
#define GEO_NODE_CURVE_TO_MESH 1044
|
||||
#define GEO_NODE_CURVE_TRANSFORM_TEST 1045
|
||||
#define GEO_NODE_CURVE_TRIM 1046
|
||||
#define GEO_NODE_CURVE_SAMPLE_POINTS 1047
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -152,6 +152,7 @@ void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bma
|
||||
void BKE_scene_graph_evaluated_ensure(struct Depsgraph *depsgraph, struct Main *bmain);
|
||||
|
||||
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph);
|
||||
void BKE_scene_graph_update_for_newframe_ex(struct Depsgraph *depsgraph, const bool clear_recalc);
|
||||
|
||||
void BKE_scene_view_layer_graph_evaluated_ensure(struct Main *bmain,
|
||||
struct Scene *scene,
|
||||
|
434
source/blender/blenkernel/BKE_spline.hh
Normal file
434
source/blender/blenkernel/BKE_spline.hh
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
||||
struct Curve;
|
||||
|
||||
class Spline;
|
||||
using SplinePtr = std::unique_ptr<Spline>;
|
||||
|
||||
/**
|
||||
* A spline is an abstraction of a single branchless curve section, its evaluation methods,
|
||||
* and data. The spline data itself is just control points and a set of attributes.
|
||||
*
|
||||
* Any derived class of Spline has to manage two things:
|
||||
* 1. Evaluating the positions based on the stored control point data.
|
||||
* 2. Interpolating arbitrary attribute data from the control points to evaluated points.
|
||||
*
|
||||
* Beyond that, everything else is the bas class's responsibility, with minor exceptions. Futher
|
||||
* evaluation happens in a layer on top of the evaluated points generated by the derived types.
|
||||
* There are a few methods to evaluate a spline:
|
||||
*
|
||||
* Common evaluated data is stored in caches on the spline itself. That way operations on splines
|
||||
* don't need to worry about taking ownership of evaluated data when they don't need to.
|
||||
*/
|
||||
class Spline {
|
||||
public:
|
||||
enum Type {
|
||||
Bezier,
|
||||
NURBS,
|
||||
Poly,
|
||||
};
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
||||
public:
|
||||
bool is_cyclic = false;
|
||||
|
||||
enum NormalCalculationMode {
|
||||
ZUp,
|
||||
Minimum,
|
||||
Tangent,
|
||||
};
|
||||
NormalCalculationMode normal_mode;
|
||||
|
||||
protected:
|
||||
mutable bool tangent_cache_dirty_ = true;
|
||||
mutable std::mutex tangent_cache_mutex_;
|
||||
mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
|
||||
|
||||
mutable bool normal_cache_dirty_ = true;
|
||||
mutable std::mutex normal_cache_mutex_;
|
||||
mutable blender::Vector<blender::float3> evaluated_normals_cache_;
|
||||
|
||||
mutable bool length_cache_dirty_ = true;
|
||||
mutable std::mutex length_cache_mutex_;
|
||||
mutable blender::Vector<float> evaluated_lengths_cache_;
|
||||
|
||||
public:
|
||||
virtual ~Spline() = default;
|
||||
Spline(const Type type) : type_(type){};
|
||||
Spline(Spline &other)
|
||||
: type_(other.type_), is_cyclic(other.is_cyclic), normal_mode(other.normal_mode)
|
||||
{
|
||||
}
|
||||
|
||||
virtual SplinePtr copy() const = 0;
|
||||
|
||||
Spline::Type type() const;
|
||||
|
||||
virtual int size() const = 0;
|
||||
int segments_size() const;
|
||||
virtual int resolution() const = 0;
|
||||
virtual void set_resolution(const int value) = 0;
|
||||
|
||||
virtual void drop_front(const int count) = 0;
|
||||
virtual void drop_back(const int count) = 0;
|
||||
|
||||
virtual blender::MutableSpan<blender::float3> positions() = 0;
|
||||
virtual blender::Span<blender::float3> positions() const = 0;
|
||||
virtual blender::MutableSpan<float> radii() = 0;
|
||||
virtual blender::Span<float> radii() const = 0;
|
||||
virtual blender::MutableSpan<float> tilts() = 0;
|
||||
virtual blender::Span<float> tilts() const = 0;
|
||||
|
||||
/**
|
||||
* Mark all caches for recomputation. This must be called after any operation that would
|
||||
* change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
|
||||
*/
|
||||
virtual void mark_cache_invalid() = 0;
|
||||
virtual int evaluated_points_size() const = 0;
|
||||
int evaluated_edges_size() const;
|
||||
|
||||
float length() const;
|
||||
|
||||
virtual blender::Span<blender::float3> evaluated_positions() const = 0;
|
||||
blender::Span<float> evaluated_lengths() const;
|
||||
blender::Span<blender::float3> evaluated_tangents() const;
|
||||
blender::Span<blender::float3> evaluated_normals() const;
|
||||
|
||||
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
|
||||
|
||||
struct LookupResult {
|
||||
/*
|
||||
* The index of the evaluated point before the result location.
|
||||
* In other words, the index of the edge that the result lies on.
|
||||
*/
|
||||
int evaluated_index;
|
||||
/*
|
||||
* The index of the evaluated point after the result location,
|
||||
* accounting for wrapping when the spline is cyclic.
|
||||
*/
|
||||
int next_evaluated_index;
|
||||
/**
|
||||
* The portion of the way from the evaluated point at #evaluated_index to the next point.
|
||||
*/
|
||||
float factor;
|
||||
};
|
||||
LookupResult lookup_evaluated_factor(const float factor) const;
|
||||
LookupResult lookup_evaluated_length(const float length) const;
|
||||
|
||||
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const = 0;
|
||||
|
||||
protected:
|
||||
virtual void correct_end_tangents() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
|
||||
* by constraining the alignment of curve handles. Evaluation stores the positions and a map of
|
||||
* factors and indices in a list of floats, which is then used to interpolate any other data.
|
||||
*/
|
||||
class BezierSpline final : public Spline {
|
||||
public:
|
||||
enum HandleType {
|
||||
Free,
|
||||
Auto,
|
||||
Vector,
|
||||
Align,
|
||||
};
|
||||
|
||||
private:
|
||||
blender::Vector<HandleType> handle_types_start_;
|
||||
blender::Vector<blender::float3> handle_positions_start_;
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<HandleType> handle_types_end_;
|
||||
blender::Vector<blender::float3> handle_positions_end_;
|
||||
blender::Vector<float> radii_;
|
||||
blender::Vector<float> tilts_;
|
||||
int resolution_;
|
||||
|
||||
mutable bool offset_cache_dirty_ = true;
|
||||
mutable std::mutex offset_cache_mutex_;
|
||||
mutable blender::Vector<int> offset_cache_;
|
||||
|
||||
mutable bool position_cache_dirty_ = true;
|
||||
mutable std::mutex position_cache_mutex_;
|
||||
mutable blender::Vector<blender::float3> evaluated_position_cache_;
|
||||
|
||||
mutable bool mapping_cache_dirty_ = true;
|
||||
mutable std::mutex mapping_cache_mutex_;
|
||||
mutable blender::Vector<float> evaluated_mapping_cache_;
|
||||
|
||||
public:
|
||||
virtual SplinePtr copy() const final;
|
||||
BezierSpline() : Spline(Type::Bezier){};
|
||||
BezierSpline(const BezierSpline &other)
|
||||
: Spline((Spline &)other),
|
||||
handle_types_start_(other.handle_types_start_),
|
||||
handle_positions_start_(other.handle_positions_start_),
|
||||
positions_(other.positions_),
|
||||
handle_types_end_(other.handle_types_end_),
|
||||
handle_positions_end_(other.handle_positions_end_),
|
||||
radii_(other.radii_),
|
||||
tilts_(other.tilts_),
|
||||
resolution_(other.resolution_)
|
||||
{
|
||||
}
|
||||
|
||||
int size() const final;
|
||||
int resolution() const final;
|
||||
void set_resolution(const int value) final;
|
||||
|
||||
void add_point(const blender::float3 position,
|
||||
const HandleType handle_type_start,
|
||||
const blender::float3 handle_position_start,
|
||||
const HandleType handle_type_end,
|
||||
const blender::float3 handle_position_end,
|
||||
const float radius,
|
||||
const float tilt);
|
||||
|
||||
void drop_front(const int count) final;
|
||||
void drop_back(const int count) final;
|
||||
|
||||
blender::MutableSpan<blender::float3> positions() final;
|
||||
blender::Span<blender::float3> positions() const final;
|
||||
blender::MutableSpan<float> radii() final;
|
||||
blender::Span<float> radii() const final;
|
||||
blender::MutableSpan<float> tilts() final;
|
||||
blender::Span<float> tilts() const final;
|
||||
|
||||
blender::Span<HandleType> handle_types_start() const;
|
||||
blender::MutableSpan<HandleType> handle_types_start();
|
||||
blender::Span<blender::float3> handle_positions_start() const;
|
||||
blender::MutableSpan<blender::float3> handle_positions_start();
|
||||
blender::Span<HandleType> handle_types_end() const;
|
||||
blender::MutableSpan<HandleType> handle_types_end();
|
||||
blender::Span<blender::float3> handle_positions_end() const;
|
||||
blender::MutableSpan<blender::float3> handle_positions_end();
|
||||
|
||||
bool point_is_sharp(const int index) const;
|
||||
bool handle_start_is_automatic(const int index) const;
|
||||
bool handle_end_is_automatic(const int index) const;
|
||||
|
||||
void move_control_point(const int index, const blender::float3 new_position);
|
||||
|
||||
void mark_cache_invalid() final;
|
||||
int evaluated_points_size() const final;
|
||||
|
||||
blender::Span<int> control_point_offsets() const;
|
||||
blender::Span<float> evaluated_mappings() const;
|
||||
blender::Span<blender::float3> evaluated_positions() const final;
|
||||
struct InterpolationData {
|
||||
int control_point_index;
|
||||
int next_control_point_index;
|
||||
float factor;
|
||||
};
|
||||
InterpolationData interpolation_data_from_map(const float map) const;
|
||||
|
||||
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const;
|
||||
|
||||
private:
|
||||
void correct_end_tangents() const final;
|
||||
bool segment_is_vector(const int start_index) const;
|
||||
void evaluate_bezier_segment(const int index,
|
||||
const int next_index,
|
||||
blender::MutableSpan<blender::float3> positions) const;
|
||||
blender::Array<int> evaluated_point_offsets() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
|
||||
* influenced by a vector of knots, weights for each point, and the order of the spline. Every
|
||||
* mapping of data to evaluated points is handled the same way, but the posistions are cached in
|
||||
* the spline.
|
||||
*/
|
||||
class NURBSpline final : public Spline {
|
||||
public:
|
||||
enum KnotsMode {
|
||||
Normal,
|
||||
EndPoint,
|
||||
Bezier,
|
||||
};
|
||||
KnotsMode knots_mode;
|
||||
|
||||
struct BasisCache {
|
||||
blender::Vector<float> weights;
|
||||
int start_index;
|
||||
};
|
||||
|
||||
private:
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<float> radii_;
|
||||
blender::Vector<float> tilts_;
|
||||
blender::Vector<float> weights_;
|
||||
int resolution_;
|
||||
uint8_t order_;
|
||||
|
||||
mutable bool knots_dirty_ = true;
|
||||
mutable std::mutex knots_mutex_;
|
||||
mutable blender::Vector<float> knots_;
|
||||
|
||||
mutable bool position_cache_dirty_ = true;
|
||||
mutable std::mutex position_cache_mutex_;
|
||||
mutable blender::Vector<blender::float3> evaluated_position_cache_;
|
||||
|
||||
mutable bool basis_cache_dirty_ = true;
|
||||
mutable std::mutex basis_cache_mutex_;
|
||||
mutable blender::Vector<BasisCache> basis_cache_;
|
||||
|
||||
public:
|
||||
SplinePtr copy() const final;
|
||||
NURBSpline() : Spline(Type::NURBS){};
|
||||
NURBSpline(const NURBSpline &other)
|
||||
: Spline((Spline &)other),
|
||||
positions_(other.positions_),
|
||||
radii_(other.radii_),
|
||||
tilts_(other.tilts_),
|
||||
weights_(other.weights_),
|
||||
resolution_(other.resolution_),
|
||||
order_(other.order_)
|
||||
{
|
||||
}
|
||||
|
||||
int size() const final;
|
||||
int resolution() const final;
|
||||
void set_resolution(const int value) final;
|
||||
uint8_t order() const;
|
||||
void set_order(const uint8_t value);
|
||||
|
||||
void add_point(const blender::float3 position,
|
||||
const float radius,
|
||||
const float tilt,
|
||||
const float weight);
|
||||
|
||||
void drop_front(const int count) final;
|
||||
void drop_back(const int count) final;
|
||||
|
||||
bool check_valid_size_and_order() const;
|
||||
int knots_size() const;
|
||||
|
||||
blender::MutableSpan<blender::float3> positions() final;
|
||||
blender::Span<blender::float3> positions() const final;
|
||||
blender::MutableSpan<float> radii() final;
|
||||
blender::Span<float> radii() const final;
|
||||
blender::MutableSpan<float> tilts() final;
|
||||
blender::Span<float> tilts() const final;
|
||||
|
||||
blender::Span<float> knots() const;
|
||||
|
||||
blender::MutableSpan<float> weights();
|
||||
blender::Span<float> weights() const;
|
||||
|
||||
void mark_cache_invalid() final;
|
||||
int evaluated_points_size() const final;
|
||||
|
||||
blender::Span<blender::float3> evaluated_positions() const final;
|
||||
|
||||
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const final;
|
||||
|
||||
protected:
|
||||
void correct_end_tangents() const final;
|
||||
void calculate_knots() const;
|
||||
void calculate_basis_cache() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* A poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
|
||||
* the two is for reduced complexity and increased performance, since interpolating data to control
|
||||
* points does not change it.
|
||||
*/
|
||||
class PolySpline final : public Spline {
|
||||
public:
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<float> radii_;
|
||||
blender::Vector<float> tilts_;
|
||||
|
||||
private:
|
||||
public:
|
||||
SplinePtr copy() const final;
|
||||
PolySpline() : Spline(Type::Bezier){};
|
||||
PolySpline(const PolySpline &other)
|
||||
: Spline((Spline &)other),
|
||||
positions_(other.positions_),
|
||||
radii_(other.radii_),
|
||||
tilts_(other.tilts_)
|
||||
{
|
||||
}
|
||||
|
||||
int size() const final;
|
||||
int resolution() const final;
|
||||
void set_resolution(const int value) final;
|
||||
|
||||
void add_point(const blender::float3 position, const float radius, const float tilt);
|
||||
|
||||
void drop_front(const int count) final;
|
||||
void drop_back(const int count) final;
|
||||
|
||||
blender::MutableSpan<blender::float3> positions() final;
|
||||
blender::Span<blender::float3> positions() const final;
|
||||
blender::MutableSpan<float> radii() final;
|
||||
blender::Span<float> radii() const final;
|
||||
blender::MutableSpan<float> tilts() final;
|
||||
blender::Span<float> tilts() const final;
|
||||
|
||||
void mark_cache_invalid() final;
|
||||
int evaluated_points_size() const final;
|
||||
|
||||
blender::Span<blender::float3> evaluated_positions() const final;
|
||||
|
||||
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const final;
|
||||
|
||||
protected:
|
||||
void correct_end_tangents() const final;
|
||||
};
|
||||
|
||||
/**
|
||||
* A #SplineGroup corresponds to the #Curve object data. The name is different for clarity, since
|
||||
* more of the data is stored in the splines, but also just to be different than the name in DNA.
|
||||
*/
|
||||
class SplineGroup {
|
||||
public:
|
||||
blender::Vector<SplinePtr> splines;
|
||||
|
||||
SplineGroup *copy();
|
||||
|
||||
void translate(const blender::float3 translation);
|
||||
void transform(const blender::float4x4 &matrix);
|
||||
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
|
||||
};
|
||||
|
||||
SplineGroup *dcurve_from_dna_curve(const Curve &curve);
|
@@ -117,7 +117,7 @@ set(SRC
|
||||
intern/customdata_file.c
|
||||
intern/data_transfer.c
|
||||
intern/deform.c
|
||||
intern/displist.c
|
||||
intern/displist.cc
|
||||
intern/displist_tangent.c
|
||||
intern/dynamicpaint.c
|
||||
intern/editlattice.c
|
||||
@@ -133,6 +133,7 @@ set(SRC
|
||||
intern/fmodifier.c
|
||||
intern/font.c
|
||||
intern/freestyle.c
|
||||
intern/geometry_component_curve.cc
|
||||
intern/geometry_component_instances.cc
|
||||
intern/geometry_component_mesh.cc
|
||||
intern/geometry_component_pointcloud.cc
|
||||
@@ -190,6 +191,7 @@ set(SRC
|
||||
intern/mesh_remap.c
|
||||
intern/mesh_remesh_voxel.c
|
||||
intern/mesh_runtime.c
|
||||
intern/mesh_sample.cc
|
||||
intern/mesh_tangent.c
|
||||
intern/mesh_validate.c
|
||||
intern/mesh_validate.cc
|
||||
@@ -240,6 +242,11 @@ set(SRC
|
||||
intern/softbody.c
|
||||
intern/sound.c
|
||||
intern/speaker.c
|
||||
intern/spline_base.cc
|
||||
intern/spline_bezier.cc
|
||||
intern/spline_group.cc
|
||||
intern/spline_nurbs.cc
|
||||
intern/spline_poly.cc
|
||||
intern/studiolight.c
|
||||
intern/subdiv.c
|
||||
intern/subdiv_ccg.c
|
||||
@@ -321,6 +328,7 @@ set(SRC
|
||||
BKE_customdata_file.h
|
||||
BKE_data_transfer.h
|
||||
BKE_deform.h
|
||||
BKE_spline.hh
|
||||
BKE_displist.h
|
||||
BKE_displist_tangent.h
|
||||
BKE_duplilist.h
|
||||
@@ -379,6 +387,7 @@ set(SRC
|
||||
BKE_mesh_remap.h
|
||||
BKE_mesh_remesh_voxel.h
|
||||
BKE_mesh_runtime.h
|
||||
BKE_mesh_sample.hh
|
||||
BKE_mesh_tangent.h
|
||||
BKE_mesh_types.h
|
||||
BKE_mesh_wrapper.h
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -24,166 +24,8 @@
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
class ConstantReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
void *value_;
|
||||
|
||||
public:
|
||||
ConstantReadAttribute(AttributeDomain domain,
|
||||
const int64_t size,
|
||||
const CPPType &type,
|
||||
const void *value)
|
||||
: ReadAttribute(domain, type, size)
|
||||
{
|
||||
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
|
||||
type.copy_to_uninitialized(value, value_);
|
||||
}
|
||||
|
||||
~ConstantReadAttribute() override
|
||||
{
|
||||
this->cpp_type_.destruct(value_);
|
||||
MEM_freeN(value_);
|
||||
}
|
||||
|
||||
void get_internal(const int64_t UNUSED(index), void *r_value) const override
|
||||
{
|
||||
this->cpp_type_.copy_to_uninitialized(value_, r_value);
|
||||
}
|
||||
|
||||
void initialize_span() const override
|
||||
{
|
||||
const int element_size = cpp_type_.size();
|
||||
array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
|
||||
array_is_temporary_ = true;
|
||||
cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class ArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Span<T> data_;
|
||||
|
||||
public:
|
||||
ArrayReadAttribute(AttributeDomain domain, Span<T> data)
|
||||
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
|
||||
void initialize_span() const override
|
||||
{
|
||||
/* The data will not be modified, so this const_cast is fine. */
|
||||
array_buffer_ = const_cast<T *>(data_.data());
|
||||
array_is_temporary_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Array<T> data_;
|
||||
|
||||
public:
|
||||
OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
|
||||
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
|
||||
void initialize_span() const override
|
||||
{
|
||||
/* The data will not be modified, so this const_cast is fine. */
|
||||
array_buffer_ = const_cast<T *>(data_.data());
|
||||
array_is_temporary_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
class DerivedArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Span<StructT> data_;
|
||||
|
||||
public:
|
||||
DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
|
||||
: ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
const StructT &struct_value = data_[index];
|
||||
const ElemT value = GetFunc(struct_value);
|
||||
new (r_value) ElemT(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MutableSpan<T> data_;
|
||||
|
||||
public:
|
||||
ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
|
||||
: WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
data_[index] = *reinterpret_cast<const T *>(value);
|
||||
}
|
||||
|
||||
void initialize_span(const bool UNUSED(write_only)) override
|
||||
{
|
||||
array_buffer_ = data_.data();
|
||||
array_is_temporary_ = false;
|
||||
}
|
||||
|
||||
void apply_span_if_necessary() override
|
||||
{
|
||||
/* Do nothing, because the span contains the attribute itself already. */
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, const ElemT &)>
|
||||
class DerivedArrayWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MutableSpan<StructT> data_;
|
||||
|
||||
public:
|
||||
DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
|
||||
: WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
const StructT &struct_value = data_[index];
|
||||
const ElemT value = GetFunc(struct_value);
|
||||
new (r_value) ElemT(value);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
StructT &struct_value = data_[index];
|
||||
const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
|
||||
SetFunc(struct_value, typed_value);
|
||||
}
|
||||
};
|
||||
using fn::GVArrayPtr;
|
||||
using fn::GVMutableArrayPtr;
|
||||
|
||||
/**
|
||||
* Utility to group together multiple functions that are used to access custom data on geometry
|
||||
@@ -244,10 +86,11 @@ class BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
|
||||
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
|
||||
virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
|
||||
virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const = 0;
|
||||
virtual bool exists(const GeometryComponent &component) const = 0;
|
||||
|
||||
StringRefNull name() const
|
||||
@@ -272,15 +115,16 @@ class BuiltinAttributeProvider {
|
||||
*/
|
||||
class DynamicAttributesProvider {
|
||||
public:
|
||||
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component),
|
||||
const StringRef UNUSED(attribute_name),
|
||||
const AttributeDomain UNUSED(domain),
|
||||
const CustomDataType UNUSED(data_type)) const
|
||||
const CustomDataType UNUSED(data_type),
|
||||
const AttributeInit &UNUSED(initializer)) const
|
||||
{
|
||||
/* Some providers should not create new attributes. */
|
||||
return false;
|
||||
@@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
|
||||
|
||||
bool try_create(GeometryComponent &component,
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const final;
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const final;
|
||||
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final;
|
||||
@@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
{
|
||||
return std::make_unique<ArrayReadAttribute<T>>(
|
||||
domain_, Span(static_cast<const T *>(layer.data), domain_size));
|
||||
return {std::make_unique<fn::GVArray_For_Span<T>>(
|
||||
Span(static_cast<const T *>(layer.data), domain_size)),
|
||||
domain_};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
|
||||
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
{
|
||||
return std::make_unique<ArrayWriteAttribute<T>>(
|
||||
domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
|
||||
return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
|
||||
MutableSpan(static_cast<T *>(layer.data), domain_size)),
|
||||
domain_};
|
||||
}
|
||||
|
||||
bool type_is_supported(CustomDataType data_type) const
|
||||
@@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
*/
|
||||
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
private:
|
||||
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
|
||||
using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
|
||||
const AttributeDomain domain_;
|
||||
const CustomDataType attribute_type_;
|
||||
const CustomDataType stored_type_;
|
||||
@@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final;
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final;
|
||||
@@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
* the #MVert struct, but is exposed as float3 attribute.
|
||||
*/
|
||||
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
|
||||
using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
|
||||
using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
|
||||
using UpdateOnRead = void (*)(const GeometryComponent &component);
|
||||
using UpdateOnWrite = void (*)(GeometryComponent &component);
|
||||
const CustomDataType stored_type_;
|
||||
@@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
|
||||
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
|
||||
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
|
||||
bool try_delete(GeometryComponent &component) const final;
|
||||
bool try_create(GeometryComponent &component) const final;
|
||||
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
|
||||
bool exists(const GeometryComponent &component) const final;
|
||||
};
|
||||
|
||||
|
@@ -1555,6 +1555,10 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
|
||||
bvh_cache_type,
|
||||
bvh_cache_p,
|
||||
mesh_eval_mutex);
|
||||
|
||||
if (looptri_mask != NULL) {
|
||||
MEM_freeN(looptri_mask);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Setup BVHTreeFromMesh */
|
||||
|
@@ -1150,6 +1150,8 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
|
||||
BKE_main_collection_sync(bmain);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1201,6 +1203,8 @@ bool BKE_collection_object_remove(Main *bmain,
|
||||
BKE_main_collection_sync(bmain);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1302,41 +1306,50 @@ static void collection_missing_parents_remove(Collection *collection)
|
||||
*
|
||||
* \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
|
||||
*
|
||||
* \param collection: may be \a NULL,
|
||||
* \param parent_collection: The collection owning the pointers that were remapped. May be \a NULL,
|
||||
* in which case whole \a bmain database of collections is checked.
|
||||
* \param child_collection: The collection that was remapped to another pointer. May be \a NULL,
|
||||
* in which case whole \a bmain database of collections is checked.
|
||||
*/
|
||||
void BKE_collections_child_remove_nulls(Main *bmain, Collection *collection)
|
||||
void BKE_collections_child_remove_nulls(Main *bmain,
|
||||
Collection *parent_collection,
|
||||
Collection *child_collection)
|
||||
{
|
||||
if (collection == NULL) {
|
||||
/* We need to do the checks in two steps when more than one collection may be involved,
|
||||
* otherwise we can miss some cases...
|
||||
* Also, master collections are not in bmain, so we also need to loop over scenes.
|
||||
*/
|
||||
for (collection = bmain->collections.first; collection != NULL;
|
||||
collection = collection->id.next) {
|
||||
collection_null_children_remove(collection);
|
||||
if (child_collection == NULL) {
|
||||
if (parent_collection != NULL) {
|
||||
collection_null_children_remove(parent_collection);
|
||||
}
|
||||
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
|
||||
collection_null_children_remove(scene->master_collection);
|
||||
else {
|
||||
/* We need to do the checks in two steps when more than one collection may be involved,
|
||||
* otherwise we can miss some cases...
|
||||
* Also, master collections are not in bmain, so we also need to loop over scenes.
|
||||
*/
|
||||
for (child_collection = bmain->collections.first; child_collection != NULL;
|
||||
child_collection = child_collection->id.next) {
|
||||
collection_null_children_remove(child_collection);
|
||||
}
|
||||
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
|
||||
collection_null_children_remove(scene->master_collection);
|
||||
}
|
||||
}
|
||||
|
||||
for (collection = bmain->collections.first; collection != NULL;
|
||||
collection = collection->id.next) {
|
||||
collection_missing_parents_remove(collection);
|
||||
for (child_collection = bmain->collections.first; child_collection != NULL;
|
||||
child_collection = child_collection->id.next) {
|
||||
collection_missing_parents_remove(child_collection);
|
||||
}
|
||||
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
|
||||
collection_missing_parents_remove(scene->master_collection);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (CollectionParent *parent = collection->parents.first, *parent_next; parent;
|
||||
for (CollectionParent *parent = child_collection->parents.first, *parent_next; parent;
|
||||
parent = parent_next) {
|
||||
parent_next = parent->next;
|
||||
|
||||
collection_null_children_remove(parent->collection);
|
||||
|
||||
if (!collection_find_child(parent->collection, collection)) {
|
||||
BLI_freelinkN(&collection->parents, parent);
|
||||
if (!collection_find_child(parent->collection, child_collection)) {
|
||||
BLI_freelinkN(&child_collection->parents, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -80,7 +80,17 @@ struct bContext {
|
||||
struct ARegion *menu;
|
||||
struct wmGizmoGroup *gizmo_group;
|
||||
struct bContextStore *store;
|
||||
const char *operator_poll_msg; /* reason for poll failing */
|
||||
|
||||
/* Operator poll. */
|
||||
/**
|
||||
* Store the reason the poll function fails (static string, not allocated).
|
||||
* For more advanced formatting use `operator_poll_msg_dyn_params`.
|
||||
*/
|
||||
const char *operator_poll_msg;
|
||||
/**
|
||||
* Store values to dynamically to create the string (called when a tool-tip is shown).
|
||||
*/
|
||||
struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
|
||||
} wm;
|
||||
|
||||
/* data context */
|
||||
@@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C)
|
||||
{
|
||||
bContext *newC = MEM_dupallocN((void *)C);
|
||||
|
||||
memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params));
|
||||
|
||||
return newC;
|
||||
}
|
||||
|
||||
void CTX_free(bContext *C)
|
||||
{
|
||||
/* This may contain a dynamically allocated message, free. */
|
||||
CTX_wm_operator_poll_msg_clear(C);
|
||||
|
||||
MEM_freeN(C);
|
||||
}
|
||||
|
||||
@@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup)
|
||||
C->wm.gizmo_group = gzgroup;
|
||||
}
|
||||
|
||||
void CTX_wm_operator_poll_msg_clear(bContext *C)
|
||||
{
|
||||
struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
|
||||
if (params->free_fn != NULL) {
|
||||
params->free_fn(C, params->user_data);
|
||||
}
|
||||
params->get_fn = NULL;
|
||||
params->free_fn = NULL;
|
||||
params->user_data = NULL;
|
||||
|
||||
C->wm.operator_poll_msg = NULL;
|
||||
}
|
||||
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
|
||||
{
|
||||
CTX_wm_operator_poll_msg_clear(C);
|
||||
|
||||
C->wm.operator_poll_msg = msg;
|
||||
}
|
||||
|
||||
const char *CTX_wm_operator_poll_msg_get(bContext *C)
|
||||
void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
|
||||
const struct bContextPollMsgDyn_Params *params)
|
||||
{
|
||||
CTX_wm_operator_poll_msg_clear(C);
|
||||
|
||||
C->wm.operator_poll_msg_dyn_params = *params;
|
||||
}
|
||||
|
||||
const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
|
||||
{
|
||||
struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
|
||||
if (params->get_fn != NULL) {
|
||||
char *msg = params->get_fn(C, params->user_data);
|
||||
if (msg != NULL) {
|
||||
*r_free = true;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
*r_free = false;
|
||||
return IFACE_(C->wm.operator_poll_msg);
|
||||
}
|
||||
|
||||
|
@@ -21,9 +21,9 @@
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_displist.h"
|
||||
#include "BKE_font.h"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_lib_id.h"
|
||||
@@ -54,6 +55,7 @@
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
#include "BLI_sys_types.h" // for intptr_t support
|
||||
|
||||
@@ -82,7 +84,7 @@ void BKE_displist_free(ListBase *lb)
|
||||
{
|
||||
DispList *dl;
|
||||
|
||||
while ((dl = BLI_pophead(lb))) {
|
||||
while ((dl = (DispList *)BLI_pophead(lb))) {
|
||||
BKE_displist_elem_free(dl);
|
||||
}
|
||||
}
|
||||
@@ -95,7 +97,7 @@ DispList *BKE_displist_find_or_create(ListBase *lb, int type)
|
||||
}
|
||||
}
|
||||
|
||||
DispList *dl = MEM_callocN(sizeof(DispList), "find_disp");
|
||||
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "find_disp");
|
||||
dl->type = type;
|
||||
BLI_addtail(lb, dl);
|
||||
|
||||
@@ -110,7 +112,7 @@ DispList *BKE_displist_find(ListBase *lb, int type)
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BKE_displist_has_faces(const ListBase *lb)
|
||||
@@ -129,11 +131,11 @@ void BKE_displist_copy(ListBase *lbn, const ListBase *lb)
|
||||
BKE_displist_free(lbn);
|
||||
|
||||
LISTBASE_FOREACH (const DispList *, dl, lb) {
|
||||
DispList *dln = MEM_dupallocN(dl);
|
||||
DispList *dln = (DispList *)MEM_dupallocN(dl);
|
||||
BLI_addtail(lbn, dln);
|
||||
dln->verts = MEM_dupallocN(dl->verts);
|
||||
dln->nors = MEM_dupallocN(dl->nors);
|
||||
dln->index = MEM_dupallocN(dl->index);
|
||||
dln->verts = (float *)MEM_dupallocN(dl->verts);
|
||||
dln->nors = (float *)MEM_dupallocN(dl->nors);
|
||||
dln->index = (int *)MEM_dupallocN(dl->index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,8 +148,8 @@ void BKE_displist_normals_add(ListBase *lb)
|
||||
|
||||
LISTBASE_FOREACH (DispList *, dl, lb) {
|
||||
if (dl->type == DL_INDEX3) {
|
||||
if (dl->nors == NULL) {
|
||||
dl->nors = MEM_callocN(sizeof(float[3]), "dlnors");
|
||||
if (dl->nors == nullptr) {
|
||||
dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors");
|
||||
|
||||
if (dl->flag & DL_BACK_CURVE) {
|
||||
dl->nors[2] = -1.0f;
|
||||
@@ -158,8 +160,8 @@ void BKE_displist_normals_add(ListBase *lb)
|
||||
}
|
||||
}
|
||||
else if (dl->type == DL_SURF) {
|
||||
if (dl->nors == NULL) {
|
||||
dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
|
||||
if (dl->nors == nullptr) {
|
||||
dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
|
||||
|
||||
vdata = dl->verts;
|
||||
ndata = dl->nors;
|
||||
@@ -338,9 +340,9 @@ static void curve_to_displist(const Curve *cu,
|
||||
* and resolution > 1. */
|
||||
const bool use_cyclic_sample = is_cyclic && (samples_len != 2);
|
||||
|
||||
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
|
||||
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
|
||||
/* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */
|
||||
dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
|
||||
dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
|
||||
BLI_addtail(r_dispbase, dl);
|
||||
dl->parts = 1;
|
||||
dl->nr = samples_len;
|
||||
@@ -393,8 +395,8 @@ static void curve_to_displist(const Curve *cu,
|
||||
}
|
||||
else if (nu->type == CU_NURBS) {
|
||||
const int len = (resolution * SEGMENTSU(nu));
|
||||
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
|
||||
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
|
||||
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
BLI_addtail(r_dispbase, dl);
|
||||
dl->parts = 1;
|
||||
dl->nr = len;
|
||||
@@ -402,12 +404,12 @@ static void curve_to_displist(const Curve *cu,
|
||||
dl->charidx = nu->charidx;
|
||||
dl->type = is_cyclic ? DL_POLY : DL_SEGM;
|
||||
|
||||
BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3]));
|
||||
BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3]));
|
||||
}
|
||||
else if (nu->type == CU_POLY) {
|
||||
const int len = nu->pntsu;
|
||||
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
|
||||
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
|
||||
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
BLI_addtail(r_dispbase, dl);
|
||||
dl->parts = 1;
|
||||
dl->nr = len;
|
||||
@@ -435,7 +437,7 @@ void BKE_displist_fill(const ListBase *dispbase,
|
||||
const float normal_proj[3],
|
||||
const bool flip_normal)
|
||||
{
|
||||
if (dispbase == NULL) {
|
||||
if (dispbase == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (BLI_listbase_is_empty(dispbase)) {
|
||||
@@ -471,14 +473,14 @@ void BKE_displist_fill(const ListBase *dispbase,
|
||||
sf_ctx.poly_nr++;
|
||||
|
||||
/* Make verts and edges. */
|
||||
ScanFillVert *sf_vert = NULL;
|
||||
ScanFillVert *sf_vert_last = NULL;
|
||||
ScanFillVert *sf_vert_new = NULL;
|
||||
ScanFillVert *sf_vert = nullptr;
|
||||
ScanFillVert *sf_vert_last = nullptr;
|
||||
ScanFillVert *sf_vert_new = nullptr;
|
||||
for (int i = 0; i < dl->nr; i++) {
|
||||
sf_vert_last = sf_vert;
|
||||
sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]);
|
||||
totvert++;
|
||||
if (sf_vert_last == NULL) {
|
||||
if (sf_vert_last == nullptr) {
|
||||
sf_vert_new = sf_vert;
|
||||
}
|
||||
else {
|
||||
@@ -486,7 +488,7 @@ void BKE_displist_fill(const ListBase *dispbase,
|
||||
}
|
||||
}
|
||||
|
||||
if (sf_vert != NULL && sf_vert_new != NULL) {
|
||||
if (sf_vert != nullptr && sf_vert_new != nullptr) {
|
||||
BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new);
|
||||
}
|
||||
}
|
||||
@@ -503,7 +505,7 @@ void BKE_displist_fill(const ListBase *dispbase,
|
||||
|
||||
const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj);
|
||||
if (totvert != 0 && triangles_len != 0) {
|
||||
DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist");
|
||||
DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist");
|
||||
dlnew->type = DL_INDEX3;
|
||||
dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE));
|
||||
dlnew->rt = (dl_rt_accum & CU_SMOOTH);
|
||||
@@ -511,8 +513,8 @@ void BKE_displist_fill(const ListBase *dispbase,
|
||||
dlnew->nr = totvert;
|
||||
dlnew->parts = triangles_len;
|
||||
|
||||
dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
|
||||
dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
|
||||
dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
|
||||
dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
|
||||
|
||||
/* vert data */
|
||||
int i;
|
||||
@@ -551,16 +553,16 @@ void BKE_displist_fill(const ListBase *dispbase,
|
||||
|
||||
static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
|
||||
{
|
||||
ListBase front = {NULL, NULL};
|
||||
ListBase back = {NULL, NULL};
|
||||
ListBase front = {nullptr, nullptr};
|
||||
ListBase back = {nullptr, nullptr};
|
||||
|
||||
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
|
||||
if (dl->type == DL_SURF) {
|
||||
if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) {
|
||||
if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) {
|
||||
DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
|
||||
DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
|
||||
BLI_addtail(&front, dlnew);
|
||||
dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
|
||||
dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
|
||||
dlnew->nr = dl->parts;
|
||||
dlnew->parts = 1;
|
||||
dlnew->type = DL_POLY;
|
||||
@@ -577,9 +579,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
|
||||
}
|
||||
}
|
||||
if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) {
|
||||
DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
|
||||
DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
|
||||
BLI_addtail(&back, dlnew);
|
||||
dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
|
||||
dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
|
||||
dlnew->nr = dl->parts;
|
||||
dlnew->parts = 1;
|
||||
dlnew->type = DL_POLY;
|
||||
@@ -634,16 +636,16 @@ static float displist_calc_taper(Depsgraph *depsgraph,
|
||||
Object *taperobj,
|
||||
float fac)
|
||||
{
|
||||
DispList *dl;
|
||||
|
||||
if (taperobj == NULL || taperobj->type != OB_CURVE) {
|
||||
if (taperobj == nullptr || taperobj->type != OB_CURVE) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL;
|
||||
if (dl == NULL) {
|
||||
DispList *dl = taperobj->runtime.curve_cache ?
|
||||
(DispList *)taperobj->runtime.curve_cache->disp.first :
|
||||
nullptr;
|
||||
if (dl == nullptr) {
|
||||
BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false);
|
||||
dl = taperobj->runtime.curve_cache->disp.first;
|
||||
dl = (DispList *)taperobj->runtime.curve_cache->disp.first;
|
||||
}
|
||||
if (dl) {
|
||||
float minx, dx, *fp;
|
||||
@@ -693,7 +695,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
|
||||
BKE_displist_free(&(ob->runtime.curve_cache->disp));
|
||||
}
|
||||
else {
|
||||
ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall");
|
||||
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
|
||||
"CurveCache for MBall");
|
||||
}
|
||||
|
||||
BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp);
|
||||
@@ -738,9 +741,9 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene,
|
||||
required_mode |= eModifierMode_Editmode;
|
||||
}
|
||||
|
||||
pretessellatePoint = NULL;
|
||||
pretessellatePoint = nullptr;
|
||||
for (; md; md = md->next) {
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
||||
|
||||
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
|
||||
continue;
|
||||
@@ -777,22 +780,22 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
|
||||
VirtualModifierData virtualModifierData;
|
||||
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
|
||||
ModifierData *pretessellatePoint;
|
||||
Curve *cu = ob->data;
|
||||
Curve *cu = (Curve *)ob->data;
|
||||
int numElems = 0, numVerts = 0;
|
||||
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
|
||||
ModifierApplyFlag apply_flag = 0;
|
||||
float(*deformedVerts)[3] = NULL;
|
||||
float *keyVerts = NULL;
|
||||
ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
|
||||
float(*deformedVerts)[3] = nullptr;
|
||||
float *keyVerts = nullptr;
|
||||
int required_mode;
|
||||
bool modified = false;
|
||||
|
||||
BKE_modifiers_clear_errors(ob);
|
||||
|
||||
if (editmode) {
|
||||
apply_flag |= MOD_APPLY_USECACHE;
|
||||
apply_flag = MOD_APPLY_USECACHE;
|
||||
}
|
||||
if (for_render) {
|
||||
apply_flag |= MOD_APPLY_RENDER;
|
||||
apply_flag = MOD_APPLY_RENDER;
|
||||
required_mode = eModifierMode_Render;
|
||||
}
|
||||
else {
|
||||
@@ -823,7 +826,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
|
||||
|
||||
if (pretessellatePoint) {
|
||||
for (; md; md = md->next) {
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
||||
|
||||
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
|
||||
continue;
|
||||
@@ -836,7 +839,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
|
||||
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts);
|
||||
}
|
||||
|
||||
mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
|
||||
mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts);
|
||||
modified = true;
|
||||
|
||||
if (md == pretessellatePoint) {
|
||||
@@ -869,7 +872,7 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[
|
||||
*r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr;
|
||||
}
|
||||
|
||||
allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts");
|
||||
allverts = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__);
|
||||
fp = (float *)allverts;
|
||||
LISTBASE_FOREACH (DispList *, dl, dispbase) {
|
||||
int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
|
||||
@@ -892,27 +895,72 @@ static void displist_vert_coords_apply(ListBase *dispbase, const float (*allvert
|
||||
}
|
||||
}
|
||||
|
||||
static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
|
||||
const ModifierEvalContext &mectx,
|
||||
const Curve *curve,
|
||||
Object *ob,
|
||||
Mesh *input_mesh,
|
||||
GeometrySet &geometry_set)
|
||||
{
|
||||
Mesh *mesh_output = nullptr;
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
||||
if (mti->modifyGeometrySet == nullptr) {
|
||||
mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh);
|
||||
}
|
||||
else {
|
||||
/* Adds a new mesh component to the geometry set based on the #input_mesh. */
|
||||
BLI_assert(!geometry_set.has<MeshComponent>());
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
mesh_component.replace(input_mesh, GeometryOwnershipType::Editable);
|
||||
mesh_component.copy_vertex_group_names_from_object(*ob);
|
||||
|
||||
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
curve_component.replace(dcurve_from_dna_curve(*curve));
|
||||
|
||||
/* Let the modifier change the geometry set. */
|
||||
mti->modifyGeometrySet(md, &mectx, &geometry_set);
|
||||
|
||||
/* Release the mesh from the geometry set again. */
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
mesh_output = mesh_component.release();
|
||||
geometry_set.remove<MeshComponent>();
|
||||
}
|
||||
|
||||
/* Return an empty mesh instead of null. */
|
||||
if (mesh_output == nullptr) {
|
||||
mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
|
||||
BKE_mesh_copy_settings(mesh_output, input_mesh);
|
||||
}
|
||||
}
|
||||
|
||||
return mesh_output;
|
||||
}
|
||||
|
||||
static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
ListBase *dispbase,
|
||||
Mesh **r_final,
|
||||
const bool for_render,
|
||||
const bool force_mesh_conversion)
|
||||
const bool force_mesh_conversion,
|
||||
GeometrySet **r_geometry_set)
|
||||
{
|
||||
GeometrySet geometry_set_final;
|
||||
|
||||
VirtualModifierData virtualModifierData;
|
||||
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
|
||||
ModifierData *pretessellatePoint;
|
||||
const Curve *cu = ob->data;
|
||||
const Curve *cu = (const Curve *)ob->data;
|
||||
int required_mode = 0, totvert = 0;
|
||||
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
|
||||
Mesh *modified = NULL, *mesh_applied;
|
||||
float(*vertCos)[3] = NULL;
|
||||
Mesh *modified = nullptr, *mesh_applied;
|
||||
float(*vertCos)[3] = nullptr;
|
||||
int useCache = !for_render;
|
||||
ModifierApplyFlag apply_flag = 0;
|
||||
ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
|
||||
|
||||
if (for_render) {
|
||||
apply_flag |= MOD_APPLY_RENDER;
|
||||
apply_flag = MOD_APPLY_RENDER;
|
||||
required_mode = eModifierMode_Render;
|
||||
}
|
||||
else {
|
||||
@@ -920,9 +968,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
}
|
||||
|
||||
const ModifierEvalContext mectx_deform = {
|
||||
depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
|
||||
depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
|
||||
const ModifierEvalContext mectx_apply = {
|
||||
depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
|
||||
depsgraph, ob, useCache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
|
||||
|
||||
pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
|
||||
|
||||
@@ -935,22 +983,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
}
|
||||
|
||||
if (r_final && *r_final) {
|
||||
BKE_id_free(NULL, *r_final);
|
||||
BKE_id_free(nullptr, *r_final);
|
||||
}
|
||||
|
||||
for (; md; md = md->next) {
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
||||
|
||||
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we need normals, no choice, have to convert to mesh now. */
|
||||
bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md);
|
||||
bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md);
|
||||
/* XXX 2.8 : now that batch cache is stored inside the ob->data
|
||||
* we need to create a Mesh for each curve that uses modifiers. */
|
||||
if (modified == NULL /* && need_normal */) {
|
||||
if (vertCos != NULL) {
|
||||
if (modified == nullptr /* && need_normal */) {
|
||||
if (vertCos != nullptr) {
|
||||
displist_vert_coords_apply(dispbase, vertCos);
|
||||
}
|
||||
|
||||
@@ -976,7 +1024,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
if (!vertCos) {
|
||||
vertCos = displist_vert_coords_alloc(dispbase, &totvert);
|
||||
}
|
||||
mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert);
|
||||
mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -991,8 +1039,8 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
if (modified) {
|
||||
if (vertCos) {
|
||||
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
|
||||
NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
|
||||
BKE_id_free(NULL, modified);
|
||||
nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
|
||||
BKE_id_free(nullptr, modified);
|
||||
modified = temp_mesh;
|
||||
|
||||
BKE_mesh_vert_coords_apply(modified, vertCos);
|
||||
@@ -1013,19 +1061,21 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
if (vertCos) {
|
||||
/* Vertex coordinates were applied to necessary data, could free it */
|
||||
MEM_freeN(vertCos);
|
||||
vertCos = NULL;
|
||||
vertCos = nullptr;
|
||||
}
|
||||
|
||||
if (need_normal) {
|
||||
BKE_mesh_ensure_normals(modified);
|
||||
}
|
||||
mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
|
||||
|
||||
mesh_applied = modifier_modify_mesh_and_geometry_set(
|
||||
md, mectx_apply, cu, ob, modified, geometry_set_final);
|
||||
|
||||
if (mesh_applied) {
|
||||
/* Modifier returned a new derived mesh */
|
||||
|
||||
if (modified && modified != mesh_applied) { /* Modifier */
|
||||
BKE_id_free(NULL, modified);
|
||||
BKE_id_free(nullptr, modified);
|
||||
}
|
||||
modified = mesh_applied;
|
||||
}
|
||||
@@ -1034,8 +1084,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
|
||||
if (vertCos) {
|
||||
if (modified) {
|
||||
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
|
||||
BKE_id_free(NULL, modified);
|
||||
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
|
||||
nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
|
||||
BKE_id_free(nullptr, modified);
|
||||
modified = temp_mesh;
|
||||
|
||||
BKE_mesh_vert_coords_apply(modified, vertCos);
|
||||
@@ -1046,7 +1097,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
else {
|
||||
displist_vert_coords_apply(dispbase, vertCos);
|
||||
MEM_freeN(vertCos);
|
||||
vertCos = NULL;
|
||||
vertCos = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1081,18 +1132,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
|
||||
MEM_SAFE_FREE(modified->mat);
|
||||
/* Set flag which makes it easier to see what's going on in a debugger. */
|
||||
modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
|
||||
modified->mat = MEM_dupallocN(cu->mat);
|
||||
modified->mat = (Material **)MEM_dupallocN(cu->mat);
|
||||
modified->totcol = cu->totcol;
|
||||
|
||||
(*r_final) = modified;
|
||||
}
|
||||
else {
|
||||
(*r_final) = NULL;
|
||||
(*r_final) = nullptr;
|
||||
}
|
||||
}
|
||||
else if (modified != NULL) {
|
||||
else if (modified != nullptr) {
|
||||
/* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */
|
||||
BKE_id_free(NULL, modified);
|
||||
BKE_id_free(nullptr, modified);
|
||||
}
|
||||
|
||||
if (r_geometry_set) {
|
||||
*r_geometry_set = new GeometrySet(std::move(geometry_set_final));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1103,8 +1158,8 @@ static void displist_surf_indices(DispList *dl)
|
||||
|
||||
dl->totindex = 0;
|
||||
|
||||
index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
|
||||
"index array nurbs");
|
||||
index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
|
||||
"index array nurbs");
|
||||
|
||||
for (a = 0; a < dl->parts; a++) {
|
||||
|
||||
@@ -1136,8 +1191,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
|
||||
const bool for_render,
|
||||
const bool for_orco)
|
||||
{
|
||||
ListBase nubase = {NULL, NULL};
|
||||
Curve *cu = ob->data;
|
||||
ListBase nubase = {nullptr, nullptr};
|
||||
Curve *cu = (Curve *)ob->data;
|
||||
DispList *dl;
|
||||
float *data;
|
||||
int len;
|
||||
@@ -1174,8 +1229,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
|
||||
if (nu->pntsv == 1) {
|
||||
len = SEGMENTSU(nu) * resolu;
|
||||
|
||||
dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
|
||||
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
|
||||
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
|
||||
BLI_addtail(dispbase, dl);
|
||||
dl->parts = 1;
|
||||
@@ -1195,13 +1250,13 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
|
||||
dl->type = DL_SEGM;
|
||||
}
|
||||
|
||||
BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3]));
|
||||
BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3]));
|
||||
}
|
||||
else {
|
||||
len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
|
||||
|
||||
dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
|
||||
dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
|
||||
dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
|
||||
BLI_addtail(dispbase, dl);
|
||||
|
||||
dl->col = nu->mat_nr;
|
||||
@@ -1233,7 +1288,7 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
|
||||
if (!for_orco) {
|
||||
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
|
||||
curve_calc_modifiers_post(
|
||||
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion);
|
||||
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion, nullptr);
|
||||
}
|
||||
|
||||
BKE_nurbList_free(&nubase);
|
||||
@@ -1258,7 +1313,7 @@ static void rotateBevelPiece(const Curve *cu,
|
||||
vec[1] = fp[2];
|
||||
vec[2] = 0.0;
|
||||
|
||||
if (nbevp == NULL) {
|
||||
if (nbevp == nullptr) {
|
||||
copy_v3_v3(data, bevp->vec);
|
||||
copy_qt_qt(quat, bevp->quat);
|
||||
}
|
||||
@@ -1276,7 +1331,7 @@ static void rotateBevelPiece(const Curve *cu,
|
||||
else {
|
||||
float sina, cosa;
|
||||
|
||||
if (nbevp == NULL) {
|
||||
if (nbevp == nullptr) {
|
||||
copy_v3_v3(data, bevp->vec);
|
||||
sina = bevp->sina;
|
||||
cosa = bevp->cosa;
|
||||
@@ -1307,8 +1362,8 @@ static void fillBevelCap(const Nurb *nu,
|
||||
{
|
||||
DispList *dl;
|
||||
|
||||
dl = MEM_callocN(sizeof(DispList), "makeDispListbev2");
|
||||
dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
|
||||
dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2");
|
||||
dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
|
||||
memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr);
|
||||
|
||||
dl->type = DL_POLY;
|
||||
@@ -1467,9 +1522,10 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
ListBase *dispbase,
|
||||
const bool for_render,
|
||||
const bool for_orco,
|
||||
Mesh **r_final)
|
||||
Mesh **r_final,
|
||||
GeometrySet **r_geometry_set)
|
||||
{
|
||||
Curve *cu = ob->data;
|
||||
Curve *cu = (Curve *)ob->data;
|
||||
|
||||
/* we do allow duplis... this is only displist on curve level */
|
||||
if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) {
|
||||
@@ -1481,7 +1537,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
}
|
||||
else if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
|
||||
ListBase dlbev;
|
||||
ListBase nubase = {NULL, NULL};
|
||||
ListBase nubase = {nullptr, nullptr};
|
||||
bool force_mesh_conversion = false;
|
||||
|
||||
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
|
||||
@@ -1494,7 +1550,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
if (ob->runtime.curve_cache->anim_path_accum_length) {
|
||||
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
|
||||
}
|
||||
ob->runtime.curve_cache->anim_path_accum_length = NULL;
|
||||
ob->runtime.curve_cache->anim_path_accum_length = nullptr;
|
||||
}
|
||||
|
||||
if (ob->type == OB_FONT) {
|
||||
@@ -1520,8 +1576,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
}
|
||||
else {
|
||||
const float widfac = cu->width - 1.0f;
|
||||
BevList *bl = ob->runtime.curve_cache->bev.first;
|
||||
Nurb *nu = nubase.first;
|
||||
BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first;
|
||||
Nurb *nu = (Nurb *)nubase.first;
|
||||
|
||||
for (; bl && nu; bl = bl->next, nu = nu->next) {
|
||||
float *data;
|
||||
@@ -1532,8 +1588,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
|
||||
/* exception handling; curve without bevel or extrude, with width correction */
|
||||
if (BLI_listbase_is_empty(&dlbev)) {
|
||||
DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev");
|
||||
dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
|
||||
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev");
|
||||
dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
|
||||
BLI_addtail(dispbase, dl);
|
||||
|
||||
if (bl->poly != -1) {
|
||||
@@ -1565,8 +1621,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
}
|
||||
}
|
||||
else {
|
||||
ListBase bottom_capbase = {NULL, NULL};
|
||||
ListBase top_capbase = {NULL, NULL};
|
||||
ListBase bottom_capbase = {nullptr, nullptr};
|
||||
ListBase top_capbase = {nullptr, nullptr};
|
||||
float bottom_no[3] = {0.0f};
|
||||
float top_no[3] = {0.0f};
|
||||
float first_blend = 0.0f, last_blend = 0.0f;
|
||||
@@ -1585,8 +1641,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
|
||||
LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
|
||||
/* for each part of the bevel use a separate displblock */
|
||||
DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1");
|
||||
dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
|
||||
DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1");
|
||||
dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
|
||||
BLI_addtail(dispbase, dl);
|
||||
|
||||
dl->type = DL_SURF;
|
||||
@@ -1616,7 +1672,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
float radius_factor = 1.0;
|
||||
float *cur_data = data;
|
||||
|
||||
if (cu->taperobj == NULL) {
|
||||
if (cu->taperobj == nullptr) {
|
||||
radius_factor = bevp->radius;
|
||||
}
|
||||
else {
|
||||
@@ -1666,7 +1722,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
|
||||
}
|
||||
else {
|
||||
rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data);
|
||||
rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data);
|
||||
}
|
||||
|
||||
if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
|
||||
@@ -1707,8 +1763,14 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
|
||||
}
|
||||
|
||||
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
|
||||
curve_calc_modifiers_post(
|
||||
depsgraph, scene, ob, dispbase, r_final, for_render, force_mesh_conversion);
|
||||
curve_calc_modifiers_post(depsgraph,
|
||||
scene,
|
||||
ob,
|
||||
dispbase,
|
||||
r_final,
|
||||
for_render,
|
||||
force_mesh_conversion,
|
||||
r_geometry_set);
|
||||
}
|
||||
|
||||
if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) {
|
||||
@@ -1737,16 +1799,25 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
|
||||
BKE_object_free_derived_caches(ob);
|
||||
|
||||
if (!ob->runtime.curve_cache) {
|
||||
ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types");
|
||||
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
|
||||
"CurveCache for curve types");
|
||||
}
|
||||
|
||||
dispbase = &(ob->runtime.curve_cache->disp);
|
||||
|
||||
Mesh *mesh_eval = NULL;
|
||||
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
|
||||
Mesh *mesh_eval = nullptr;
|
||||
GeometrySet *geometry_set_eval = nullptr;
|
||||
do_makeDispListCurveTypes(
|
||||
depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval, &geometry_set_eval);
|
||||
|
||||
if (mesh_eval != NULL) {
|
||||
if (mesh_eval != nullptr) {
|
||||
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
|
||||
|
||||
/* Add the final mesh as read-only non-owning component to the geometry set. */
|
||||
BLI_assert(!geometry_set_eval->has<MeshComponent>());
|
||||
MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>();
|
||||
mesh_component.replace(mesh_eval, GeometryOwnershipType::ReadOnly);
|
||||
ob->runtime.geometry_set_eval = geometry_set_eval;
|
||||
}
|
||||
|
||||
boundbox_displist_object(ob);
|
||||
@@ -1759,11 +1830,12 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph,
|
||||
Mesh **r_final,
|
||||
const bool for_orco)
|
||||
{
|
||||
if (ob->runtime.curve_cache == NULL) {
|
||||
ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
|
||||
if (ob->runtime.curve_cache == nullptr) {
|
||||
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
|
||||
"CurveCache for Curve");
|
||||
}
|
||||
|
||||
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final);
|
||||
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final, nullptr);
|
||||
}
|
||||
|
||||
void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
|
||||
@@ -1797,8 +1869,8 @@ static void boundbox_displist_object(Object *ob)
|
||||
*/
|
||||
|
||||
/* object's BB is calculated from final displist */
|
||||
if (ob->runtime.bb == NULL) {
|
||||
ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
|
||||
if (ob->runtime.bb == nullptr) {
|
||||
ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
|
||||
}
|
||||
|
||||
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
|
403
source/blender/blenkernel/intern/geometry_component_curve.cc
Normal file
403
source/blender/blenkernel/intern/geometry_component_curve.cc
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
#include "attribute_access_intern.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Geometry Component Implementation
|
||||
* \{ */
|
||||
|
||||
CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
|
||||
{
|
||||
}
|
||||
|
||||
CurveComponent::~CurveComponent()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
GeometryComponent *CurveComponent::copy() const
|
||||
{
|
||||
CurveComponent *new_component = new CurveComponent();
|
||||
if (curve_ != nullptr) {
|
||||
new_component->curve_ = curve_->copy();
|
||||
new_component->ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
return new_component;
|
||||
}
|
||||
|
||||
void CurveComponent::clear()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
if (curve_ != nullptr) {
|
||||
if (ownership_ == GeometryOwnershipType::Owned) {
|
||||
delete curve_;
|
||||
}
|
||||
curve_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool CurveComponent::has_curve() const
|
||||
{
|
||||
return curve_ != nullptr;
|
||||
}
|
||||
|
||||
/* Clear the component and replace it with the new curve. */
|
||||
void CurveComponent::replace(SplineGroup *curve, GeometryOwnershipType ownership)
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
this->clear();
|
||||
curve_ = curve;
|
||||
ownership_ = ownership;
|
||||
}
|
||||
|
||||
SplineGroup *CurveComponent::release()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
SplineGroup *curve = curve_;
|
||||
curve_ = nullptr;
|
||||
return curve;
|
||||
}
|
||||
|
||||
const SplineGroup *CurveComponent::get_for_read() const
|
||||
{
|
||||
return curve_;
|
||||
}
|
||||
|
||||
SplineGroup *CurveComponent::get_for_write()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
if (ownership_ == GeometryOwnershipType::ReadOnly) {
|
||||
curve_ = curve_->copy();
|
||||
ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
return curve_;
|
||||
}
|
||||
|
||||
bool CurveComponent::is_empty() const
|
||||
{
|
||||
return curve_ == nullptr;
|
||||
}
|
||||
|
||||
bool CurveComponent::owns_direct_data() const
|
||||
{
|
||||
return ownership_ == GeometryOwnershipType::Owned;
|
||||
}
|
||||
|
||||
void CurveComponent::ensure_owns_direct_data()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
if (ownership_ != GeometryOwnershipType::Owned) {
|
||||
curve_ = curve_->copy();
|
||||
ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Attribute Access
|
||||
* \{ */
|
||||
|
||||
int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
|
||||
{
|
||||
if (curve_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
int total = 0;
|
||||
for (const SplinePtr &spline : curve_->splines) {
|
||||
total += spline->size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_CURVE) {
|
||||
return curve_->splines.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
|
||||
using AsReadAttribute = GVArrayPtr (*)(const SplineGroup &data);
|
||||
using AsWriteAttribute = GVMutableArrayPtr (*)(SplineGroup &data);
|
||||
using UpdateOnWrite = void (*)(Spline &spline);
|
||||
const AsReadAttribute as_read_attribute_;
|
||||
const AsWriteAttribute as_write_attribute_;
|
||||
|
||||
public:
|
||||
BuiltinSplineAttributeProvider(std::string attribute_name,
|
||||
const CustomDataType attribute_type,
|
||||
const WritableEnum writable,
|
||||
const AsReadAttribute as_read_attribute,
|
||||
const AsWriteAttribute as_write_attribute)
|
||||
: BuiltinAttributeProvider(std::move(attribute_name),
|
||||
ATTR_DOMAIN_CURVE,
|
||||
attribute_type,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
writable,
|
||||
BuiltinAttributeProvider::NonDeletable),
|
||||
as_read_attribute_(as_read_attribute),
|
||||
as_write_attribute_(as_write_attribute)
|
||||
{
|
||||
}
|
||||
|
||||
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
|
||||
{
|
||||
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
|
||||
const SplineGroup *curve = curve_component.get_for_read();
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return as_read_attribute_(*curve);
|
||||
}
|
||||
|
||||
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
|
||||
{
|
||||
if (writable_ != Writable) {
|
||||
return {};
|
||||
}
|
||||
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
|
||||
SplineGroup *curve = curve_component.get_for_write();
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return as_write_attribute_(*curve);
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &UNUSED(component)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
{
|
||||
return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
static int get_spline_resolution(const SplinePtr &spline)
|
||||
{
|
||||
return spline->resolution();
|
||||
}
|
||||
|
||||
static void set_spline_resolution(SplinePtr &spline, const int resolution)
|
||||
{
|
||||
spline->set_resolution(std::max(resolution, 1));
|
||||
}
|
||||
|
||||
static GVArrayPtr make_resolution_read_attribute(const SplineGroup &curve)
|
||||
{
|
||||
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
|
||||
curve.splines.as_span());
|
||||
}
|
||||
|
||||
static GVMutableArrayPtr make_resolution_write_attribute(SplineGroup &curve)
|
||||
{
|
||||
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
|
||||
int,
|
||||
get_spline_resolution,
|
||||
set_spline_resolution>>(
|
||||
curve.splines.as_mutable_span());
|
||||
}
|
||||
|
||||
static float get_spline_length(const SplinePtr &spline)
|
||||
{
|
||||
return spline->length();
|
||||
}
|
||||
|
||||
static GVArrayPtr make_length_attribute(const SplineGroup &curve)
|
||||
{
|
||||
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, float, get_spline_length>>(
|
||||
curve.splines.as_span());
|
||||
}
|
||||
|
||||
static bool get_cyclic_value(const SplinePtr &spline)
|
||||
{
|
||||
return spline->is_cyclic;
|
||||
}
|
||||
|
||||
static void set_cyclic_value(SplinePtr &spline, const bool value)
|
||||
{
|
||||
if (spline->is_cyclic != value) {
|
||||
spline->is_cyclic = value;
|
||||
spline->mark_cache_invalid();
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr make_cyclic_read_attribute(const SplineGroup &curve)
|
||||
{
|
||||
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
|
||||
curve.splines.as_span());
|
||||
}
|
||||
|
||||
static GVMutableArrayPtr make_cyclic_write_attribute(SplineGroup &curve)
|
||||
{
|
||||
return std::make_unique<
|
||||
fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
|
||||
curve.splines.as_mutable_span());
|
||||
}
|
||||
|
||||
/**
|
||||
* \note Currently this uses an inefficient method, copying data from each spline into a single
|
||||
* array and then passing that as the attribute. Also, currently attributes are only read-only.
|
||||
*/
|
||||
class BuiltinPointAttributeProvider final : public BuiltinAttributeProvider {
|
||||
using GetSplineData = void (*)(const Spline &spline, fn::GMutableSpan r_data);
|
||||
using SetSplineData = void (*)(Spline &spline, fn::GSpan data);
|
||||
const GetSplineData get_spline_data_;
|
||||
const SetSplineData set_spline_data_;
|
||||
|
||||
public:
|
||||
BuiltinPointAttributeProvider(std::string attribute_name,
|
||||
const CustomDataType attribute_type,
|
||||
const WritableEnum writable,
|
||||
const GetSplineData get_spline_data,
|
||||
const SetSplineData set_spline_data)
|
||||
: BuiltinAttributeProvider(std::move(attribute_name),
|
||||
ATTR_DOMAIN_POINT,
|
||||
attribute_type,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
writable,
|
||||
BuiltinAttributeProvider::NonDeletable),
|
||||
get_spline_data_(get_spline_data),
|
||||
set_spline_data_(set_spline_data)
|
||||
{
|
||||
}
|
||||
|
||||
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
|
||||
{
|
||||
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
|
||||
const SplineGroup *curve = curve_component.get_for_read();
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
GVArrayPtr varray;
|
||||
attribute_math::convert_to_static_type(data_type_, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
Array<T> values(curve_component.attribute_domain_size(ATTR_DOMAIN_POINT));
|
||||
|
||||
int offset = 0;
|
||||
for (const SplinePtr &spline : curve->splines) {
|
||||
const int points_len = spline->size();
|
||||
MutableSpan<T> spline_data = values.as_mutable_span().slice(offset, points_len);
|
||||
fn::GMutableSpan generic_spline_data(spline_data);
|
||||
get_spline_data_(*spline, generic_spline_data);
|
||||
offset += points_len;
|
||||
}
|
||||
|
||||
varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
});
|
||||
|
||||
return varray;
|
||||
}
|
||||
|
||||
GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &UNUSED(component)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
{
|
||||
return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
static void get_spline_radius_data(const Spline &spline, fn::GMutableSpan r_data)
|
||||
{
|
||||
MutableSpan<float> r_span = r_data.typed<float>();
|
||||
r_span.copy_from(spline.radii());
|
||||
}
|
||||
|
||||
static void get_spline_position_data(const Spline &spline, fn::GMutableSpan r_data)
|
||||
{
|
||||
MutableSpan<float3> r_span = r_data.typed<float3>();
|
||||
r_span.copy_from(spline.positions());
|
||||
}
|
||||
|
||||
/**
|
||||
* In this function all the attribute providers for a curve component are created. Most data
|
||||
* in this function is statically allocated, because it does not change over time.
|
||||
*/
|
||||
static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
{
|
||||
static BuiltinSplineAttributeProvider resolution("resolution",
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
make_resolution_read_attribute,
|
||||
make_resolution_write_attribute);
|
||||
|
||||
static BuiltinSplineAttributeProvider length(
|
||||
"length", CD_PROP_FLOAT, BuiltinAttributeProvider::Readonly, make_length_attribute, nullptr);
|
||||
|
||||
static BuiltinSplineAttributeProvider cyclic("cyclic",
|
||||
CD_PROP_BOOL,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
make_cyclic_read_attribute,
|
||||
make_cyclic_write_attribute);
|
||||
|
||||
static BuiltinPointAttributeProvider position("position",
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::Readonly,
|
||||
get_spline_position_data,
|
||||
nullptr);
|
||||
|
||||
static BuiltinPointAttributeProvider radius("radius",
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Readonly,
|
||||
get_spline_radius_data,
|
||||
nullptr);
|
||||
|
||||
return ComponentAttributeProviders({&resolution, &length, &cyclic, &position, &radius}, {});
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
|
||||
{
|
||||
static blender::bke::ComponentAttributeProviders providers =
|
||||
blender::bke::create_attribute_providers_for_curve();
|
||||
return &providers;
|
||||
}
|
||||
|
||||
/** \} */
|
@@ -44,6 +44,7 @@ GeometryComponent *InstancesComponent::copy() const
|
||||
InstancesComponent *new_component = new InstancesComponent();
|
||||
new_component->transforms_ = transforms_;
|
||||
new_component->instanced_data_ = instanced_data_;
|
||||
new_component->ids_ = ids_;
|
||||
return new_component;
|
||||
}
|
||||
|
||||
@@ -51,6 +52,7 @@ void InstancesComponent::clear()
|
||||
{
|
||||
instanced_data_.clear();
|
||||
transforms_.clear();
|
||||
ids_.clear();
|
||||
}
|
||||
|
||||
void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
|
||||
|
@@ -32,7 +32,7 @@
|
||||
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
|
||||
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
|
||||
|
||||
using blender::bke::ReadAttributePtr;
|
||||
using blender::fn::GVArray;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Geometry Component Implementation
|
||||
@@ -201,14 +201,14 @@ namespace blender::bke {
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
||||
const TypedReadAttribute<T> &attribute,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
attribute_math::DefaultMixer<T> mixer(r_values);
|
||||
|
||||
for (const int loop_index : IndexRange(mesh.totloop)) {
|
||||
const T value = attribute[loop_index];
|
||||
const T value = old_values[loop_index];
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const int point_index = loop.v;
|
||||
mixer.mix_in(point_index, value);
|
||||
@@ -216,43 +216,40 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
/* We compute all interpolated values at once, because for this interpolation, one has to
|
||||
* iterate over all loops anyway. */
|
||||
Array<T> values(mesh.totvert);
|
||||
adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
|
||||
const TypedReadAttribute<T> &attribute,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
|
||||
for (const int loop_index : IndexRange(mesh.totloop)) {
|
||||
const int vertex_index = mesh.mloop[loop_index].v;
|
||||
r_values[loop_index] = attribute[vertex_index];
|
||||
r_values[loop_index] = old_values[vertex_index];
|
||||
}
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
/* It is not strictly necessary to compute the value for all corners here. Instead one could
|
||||
@@ -260,11 +257,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
* when an algorithm only accesses very few of the corner values. However, for the algorithms
|
||||
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +270,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
@@ -291,26 +287,24 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
@@ -332,26 +326,24 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totedge);
|
||||
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
@@ -370,26 +362,24 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totvert);
|
||||
adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
@@ -401,26 +391,24 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
|
||||
}
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
@@ -437,21 +425,19 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totedge);
|
||||
adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -461,7 +447,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
@@ -478,21 +464,19 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,7 +486,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
@@ -517,26 +501,24 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totedge);
|
||||
adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
@@ -547,7 +529,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
|
||||
/* For every corner, mix the values from the adjacent edges on the face. */
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const int loop_index_prev = (loop_index - 1) % poly.totloop;
|
||||
const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop;
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const MLoop &loop_prev = mesh.mloop[loop_index_prev];
|
||||
mixer.mix_in(loop_index, old_values[loop.e]);
|
||||
@@ -558,26 +540,24 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
@@ -593,21 +573,19 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totvert);
|
||||
adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,7 +595,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
const VArray<T> &old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
@@ -634,87 +612,86 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
GVArrayPtr new_varray;
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
}
|
||||
});
|
||||
return new_attribute;
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
|
||||
const AttributeDomain new_domain) const
|
||||
blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
|
||||
blender::fn::GVArrayPtr varray,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const
|
||||
{
|
||||
if (!attribute) {
|
||||
if (!varray) {
|
||||
return {};
|
||||
}
|
||||
if (attribute->size() == 0) {
|
||||
if (varray->size() == 0) {
|
||||
return {};
|
||||
}
|
||||
const AttributeDomain old_domain = attribute->domain();
|
||||
if (old_domain == new_domain) {
|
||||
return attribute;
|
||||
if (from_domain == to_domain) {
|
||||
return varray;
|
||||
}
|
||||
|
||||
switch (old_domain) {
|
||||
switch (from_domain) {
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_EDGE: {
|
||||
switch (new_domain) {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -743,25 +720,21 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
AttributeDomain Domain>
|
||||
static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>(
|
||||
Domain, Span<StructT>((const StructT *)data, domain_size));
|
||||
return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
|
||||
Span<StructT>((const StructT *)data, domain_size));
|
||||
}
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, const ElemT &),
|
||||
AttributeDomain Domain>
|
||||
static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
|
||||
void (*SetFunc)(StructT &, ElemT)>
|
||||
static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>(
|
||||
Domain, MutableSpan<StructT>((StructT *)data, domain_size));
|
||||
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
|
||||
MutableSpan<StructT>((StructT *)data, domain_size));
|
||||
}
|
||||
|
||||
static float3 get_vertex_position(const MVert &vert)
|
||||
@@ -769,7 +742,7 @@ static float3 get_vertex_position(const MVert &vert)
|
||||
return float3(vert.co);
|
||||
}
|
||||
|
||||
static void set_vertex_position(MVert &vert, const float3 &position)
|
||||
static void set_vertex_position(MVert &vert, float3 position)
|
||||
{
|
||||
copy_v3_v3(vert.co, position);
|
||||
}
|
||||
@@ -787,7 +760,7 @@ static int get_material_index(const MPoly &mpoly)
|
||||
return static_cast<int>(mpoly.mat_nr);
|
||||
}
|
||||
|
||||
static void set_material_index(MPoly &mpoly, const int &index)
|
||||
static void set_material_index(MPoly &mpoly, int index)
|
||||
{
|
||||
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
|
||||
}
|
||||
@@ -797,7 +770,7 @@ static bool get_shade_smooth(const MPoly &mpoly)
|
||||
return mpoly.flag & ME_SMOOTH;
|
||||
}
|
||||
|
||||
static void set_shade_smooth(MPoly &mpoly, const bool &value)
|
||||
static void set_shade_smooth(MPoly &mpoly, bool value)
|
||||
{
|
||||
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
|
||||
}
|
||||
@@ -807,7 +780,7 @@ static float2 get_loop_uv(const MLoopUV &uv)
|
||||
return float2(uv.uv);
|
||||
}
|
||||
|
||||
static void set_loop_uv(MLoopUV &uv, const float2 &co)
|
||||
static void set_loop_uv(MLoopUV &uv, float2 co)
|
||||
{
|
||||
copy_v2_v2(uv.uv, co);
|
||||
}
|
||||
@@ -821,7 +794,7 @@ static Color4f get_loop_color(const MLoopCol &col)
|
||||
return linear_color;
|
||||
}
|
||||
|
||||
static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
|
||||
static void set_loop_color(MLoopCol &col, Color4f linear_color)
|
||||
{
|
||||
linearrgb_to_srgb_uchar4(&col.r, linear_color);
|
||||
}
|
||||
@@ -831,71 +804,62 @@ static float get_crease(const MEdge &edge)
|
||||
return edge.crease / 255.0f;
|
||||
}
|
||||
|
||||
static void set_crease(MEdge &edge, const float &value)
|
||||
static void set_crease(MEdge &edge, float value)
|
||||
{
|
||||
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
|
||||
}
|
||||
|
||||
class VertexWeightWriteAttribute final : public WriteAttribute {
|
||||
class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
|
||||
private:
|
||||
MDeformVert *dverts_;
|
||||
const int dvert_index_;
|
||||
|
||||
public:
|
||||
VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
|
||||
dverts_(dverts),
|
||||
dvert_index_(dvert_index)
|
||||
VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
float get_impl(const int64_t index) const override
|
||||
{
|
||||
get_internal(dverts_, dvert_index_, index, r_value);
|
||||
return get_internal(dverts_, dvert_index_, index);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
void set_impl(const int64_t index, const float value) override
|
||||
{
|
||||
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
|
||||
weight->weight = *reinterpret_cast<const float *>(value);
|
||||
weight->weight = value;
|
||||
}
|
||||
|
||||
static void get_internal(const MDeformVert *dverts,
|
||||
const int dvert_index,
|
||||
const int64_t index,
|
||||
void *r_value)
|
||||
static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index)
|
||||
{
|
||||
if (dverts == nullptr) {
|
||||
*(float *)r_value = 0.0f;
|
||||
return;
|
||||
return 0.0f;
|
||||
}
|
||||
const MDeformVert &dvert = dverts[index];
|
||||
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
|
||||
if (weight.def_nr == dvert_index) {
|
||||
*(float *)r_value = weight.weight;
|
||||
return;
|
||||
return weight.weight;
|
||||
}
|
||||
}
|
||||
*(float *)r_value = 0.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class VertexWeightReadAttribute final : public ReadAttribute {
|
||||
class VArray_For_VertexWeights final : public VArray<float> {
|
||||
private:
|
||||
const MDeformVert *dverts_;
|
||||
const int dvert_index_;
|
||||
|
||||
public:
|
||||
VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
|
||||
dverts_(dverts),
|
||||
dvert_index_(dvert_index)
|
||||
VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
float get_impl(const int64_t index) const override
|
||||
{
|
||||
VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
|
||||
return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -904,8 +868,8 @@ class VertexWeightReadAttribute final : public ReadAttribute {
|
||||
*/
|
||||
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
public:
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
@@ -917,15 +881,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
if (mesh == nullptr || mesh->dvert == nullptr) {
|
||||
static const float default_value = 0.0f;
|
||||
return std::make_unique<ConstantReadAttribute>(
|
||||
ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
|
||||
return {std::make_unique<fn::GVArray_For_SingleValueRef>(
|
||||
CPPType::get<float>(), mesh->totvert, &default_value),
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
return std::make_unique<VertexWeightReadAttribute>(
|
||||
mesh->dvert, mesh->totvert, vertex_group_index);
|
||||
return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
|
||||
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const StringRef attribute_name) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
||||
@@ -946,8 +912,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
|
||||
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
|
||||
}
|
||||
return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
|
||||
mesh->dvert, mesh->totvert, vertex_group_index);
|
||||
return {
|
||||
std::make_unique<
|
||||
fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
|
||||
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
|
||||
@@ -1009,7 +978,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
|
||||
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
|
||||
{
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
@@ -1022,8 +991,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
|
||||
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
|
||||
|
||||
return std::make_unique<ArrayReadAttribute<float3>>(
|
||||
ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
|
||||
return std::make_unique<fn::GVArray_For_Span<float3>>(
|
||||
Span<float3>((const float3 *)data, mesh->totpoly));
|
||||
}
|
||||
|
||||
Array<float3> normals(mesh->totpoly);
|
||||
@@ -1032,10 +1001,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
|
||||
}
|
||||
|
||||
return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
|
||||
return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
|
||||
}
|
||||
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
|
||||
GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@@ -1045,7 +1014,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component)) const final
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1105,12 +1075,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>,
|
||||
make_derived_write_attribute<MVert,
|
||||
float3,
|
||||
get_vertex_position,
|
||||
set_vertex_position,
|
||||
ATTR_DOMAIN_POINT>,
|
||||
make_derived_read_attribute<MVert, float3, get_vertex_position>,
|
||||
make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>,
|
||||
tag_normals_dirty_when_writing_position);
|
||||
|
||||
static NormalAttributeProvider normal;
|
||||
@@ -1124,12 +1090,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
face_access,
|
||||
make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>,
|
||||
make_derived_write_attribute<MPoly,
|
||||
int,
|
||||
get_material_index,
|
||||
set_material_index,
|
||||
ATTR_DOMAIN_FACE>,
|
||||
make_derived_read_attribute<MPoly, int, get_material_index>,
|
||||
make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider shade_smooth(
|
||||
@@ -1141,12 +1103,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
face_access,
|
||||
make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>,
|
||||
make_derived_write_attribute<MPoly,
|
||||
bool,
|
||||
get_shade_smooth,
|
||||
set_shade_smooth,
|
||||
ATTR_DOMAIN_FACE>,
|
||||
make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
|
||||
make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider crease(
|
||||
@@ -1158,8 +1116,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
edge_access,
|
||||
make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>,
|
||||
make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>,
|
||||
make_derived_read_attribute<MEdge, float, get_crease>,
|
||||
make_derived_write_attribute<MEdge, float, get_crease, set_crease>,
|
||||
nullptr);
|
||||
|
||||
static NamedLegacyCustomDataProvider uvs(
|
||||
@@ -1167,20 +1125,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
CD_PROP_FLOAT2,
|
||||
CD_MLOOPUV,
|
||||
corner_access,
|
||||
make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>,
|
||||
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>);
|
||||
make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
|
||||
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
|
||||
|
||||
static NamedLegacyCustomDataProvider vertex_colors(
|
||||
ATTR_DOMAIN_CORNER,
|
||||
CD_PROP_COLOR,
|
||||
CD_MLOOPCOL,
|
||||
corner_access,
|
||||
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>,
|
||||
make_derived_write_attribute<MLoopCol,
|
||||
Color4f,
|
||||
get_loop_color,
|
||||
set_loop_color,
|
||||
ATTR_DOMAIN_CORNER>);
|
||||
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>,
|
||||
make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>);
|
||||
|
||||
static VertexGroupsAttributeProvider vertex_groups;
|
||||
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
|
||||
|
@@ -140,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename T, AttributeDomain Domain>
|
||||
static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
|
||||
template<typename T>
|
||||
static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
|
||||
return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
|
||||
}
|
||||
|
||||
template<typename T, AttributeDomain Domain>
|
||||
static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
|
||||
template<typename T>
|
||||
static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
|
||||
return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
|
||||
MutableSpan<T>((T *)data, domain_size));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
||||
},
|
||||
update_custom_data_pointers};
|
||||
|
||||
static BuiltinCustomDataLayerProvider position(
|
||||
"position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
|
||||
make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider radius(
|
||||
"radius",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
|
||||
make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float3>,
|
||||
make_array_write_attribute<float3>,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider radius("radius",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
make_array_read_attribute<float>,
|
||||
make_array_write_attribute<float>,
|
||||
nullptr);
|
||||
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
|
||||
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "BKE_mesh_wrapper.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_pointcloud.h"
|
||||
#include "BKE_spline.hh"
|
||||
#include "BKE_volume.h"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
@@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
|
||||
return new InstancesComponent();
|
||||
case GEO_COMPONENT_TYPE_VOLUME:
|
||||
return new VolumeComponent();
|
||||
case GEO_COMPONENT_TYPE_CURVE:
|
||||
return new CurveComponent();
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
@@ -182,6 +185,13 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
|
||||
if (volume != nullptr) {
|
||||
BKE_volume_min_max(volume, *r_min, *r_max);
|
||||
}
|
||||
const SplineGroup *curve = this->get_curve_for_read();
|
||||
if (curve != nullptr) {
|
||||
/* Note the the choice of using the evaluated positions is somewhat arbitrary, and may counter
|
||||
* the idea that the curve is the reduced set of control point information, but it may also be
|
||||
* the expected result. */
|
||||
curve->bounds_min_max(*r_min, *r_max, true);
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
|
||||
@@ -252,6 +262,13 @@ const Volume *GeometrySet::get_volume_for_read() const
|
||||
return (component == nullptr) ? nullptr : component->get_for_read();
|
||||
}
|
||||
|
||||
/* Returns a read-only curve or null. */
|
||||
const SplineGroup *GeometrySet::get_curve_for_read() const
|
||||
{
|
||||
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
|
||||
return (component == nullptr) ? nullptr : component->get_for_read();
|
||||
}
|
||||
|
||||
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
|
||||
bool GeometrySet::has_pointcloud() const
|
||||
{
|
||||
@@ -273,6 +290,13 @@ bool GeometrySet::has_volume() const
|
||||
return component != nullptr && component->has_volume();
|
||||
}
|
||||
|
||||
/* Returns true when the geometry set has a curve component that has a curve. */
|
||||
bool GeometrySet::has_curve() const
|
||||
{
|
||||
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
|
||||
return component != nullptr && component->has_curve();
|
||||
}
|
||||
|
||||
/* Create a new geometry set that only contains the given mesh. */
|
||||
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
|
||||
{
|
||||
@@ -292,6 +316,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
|
||||
return geometry_set;
|
||||
}
|
||||
|
||||
/* Create a new geometry set that only contains the given curve. */
|
||||
GeometrySet GeometrySet::create_with_curve(SplineGroup *curve, GeometryOwnershipType ownership)
|
||||
{
|
||||
GeometrySet geometry_set;
|
||||
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
component.replace(curve, ownership);
|
||||
return geometry_set;
|
||||
}
|
||||
|
||||
/* Clear the existing mesh and replace it with the given one. */
|
||||
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
|
||||
{
|
||||
@@ -299,6 +332,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
|
||||
component.replace(mesh, ownership);
|
||||
}
|
||||
|
||||
/* Clear the existing curve and replace it with the given one. */
|
||||
void GeometrySet::replace_curve(SplineGroup *curve, GeometryOwnershipType ownership)
|
||||
{
|
||||
CurveComponent &component = this->get_component_for_write<CurveComponent>();
|
||||
component.replace(curve, ownership);
|
||||
}
|
||||
|
||||
/* Clear the existing point cloud and replace with the given one. */
|
||||
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
|
||||
{
|
||||
@@ -306,6 +346,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy
|
||||
pointcloud_component.replace(pointcloud, ownership);
|
||||
}
|
||||
|
||||
/* Clear the existing volume and replace with the given one. */
|
||||
void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership)
|
||||
{
|
||||
VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>();
|
||||
volume_component.replace(volume, ownership);
|
||||
}
|
||||
|
||||
/* Returns a mutable mesh or null. No ownership is transferred. */
|
||||
Mesh *GeometrySet::get_mesh_for_write()
|
||||
{
|
||||
@@ -327,6 +374,13 @@ Volume *GeometrySet::get_volume_for_write()
|
||||
return component.get_for_write();
|
||||
}
|
||||
|
||||
/* Returns a mutable curve or null. No ownership is transferred. */
|
||||
SplineGroup *GeometrySet::get_curve_for_write()
|
||||
{
|
||||
CurveComponent &component = this->get_component_for_write<CurveComponent>();
|
||||
return component.get_for_write();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@@ -449,13 +449,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
|
||||
result.attribute_try_create(entry.key, domain_output, data_type_output);
|
||||
WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
|
||||
if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
|
||||
write_attribute->domain() != domain_output) {
|
||||
result.attribute_try_create(
|
||||
entry.key, domain_output, data_type_output, AttributeInitDefault());
|
||||
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
|
||||
if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
|
||||
write_attribute.domain != domain_output) {
|
||||
continue;
|
||||
}
|
||||
fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
|
||||
|
||||
fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
|
||||
|
||||
int offset = 0;
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
@@ -467,11 +469,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
if (domain_size == 0) {
|
||||
continue; /* Domain size is 0, so no need to increment the offset. */
|
||||
}
|
||||
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
|
||||
GVArrayPtr source_attribute = component.attribute_try_get_for_read(
|
||||
name, domain_output, data_type_output);
|
||||
|
||||
if (source_attribute) {
|
||||
fn::GSpan src_span = source_attribute->get_span();
|
||||
fn::GVArray_GSpan src_span{*source_attribute};
|
||||
const void *src_buffer = src_span.data();
|
||||
for (const int UNUSED(i) : set_group.transforms.index_range()) {
|
||||
void *dst_buffer = dst_span[offset];
|
||||
@@ -486,7 +488,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
}
|
||||
}
|
||||
|
||||
write_attribute->apply_span();
|
||||
dst_span.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2433,7 +2433,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
|
||||
Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
|
||||
make_element_name(
|
||||
ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name);
|
||||
mat_idx = gpencil_material_find_index_by_name(ob_gp, element_name);
|
||||
mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
|
||||
if (mat_idx == -1) {
|
||||
float color[4];
|
||||
if (ma != NULL) {
|
||||
|
@@ -303,6 +303,7 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain,
|
||||
/* Can be called with both old_collection and new_collection being NULL,
|
||||
* this means we have to check whole Main database then. */
|
||||
static void libblock_remap_data_postprocess_collection_update(Main *bmain,
|
||||
Collection *owner_collection,
|
||||
Collection *UNUSED(old_collection),
|
||||
Collection *new_collection)
|
||||
{
|
||||
@@ -311,7 +312,7 @@ static void libblock_remap_data_postprocess_collection_update(Main *bmain,
|
||||
* and BKE_main_collection_sync_remap() does not tolerate any of those, so for now always check
|
||||
* whole existing collections for NULL pointers.
|
||||
* I'd consider optimizing that whole collection remapping process a TODO for later. */
|
||||
BKE_collections_child_remove_nulls(bmain, NULL /*old_collection*/);
|
||||
BKE_collections_child_remove_nulls(bmain, owner_collection, NULL /*old_collection*/);
|
||||
}
|
||||
else {
|
||||
/* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from
|
||||
@@ -523,7 +524,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
|
||||
break;
|
||||
case ID_GR:
|
||||
libblock_remap_data_postprocess_collection_update(
|
||||
bmain, (Collection *)old_id, (Collection *)new_id);
|
||||
bmain, NULL, (Collection *)old_id, (Collection *)new_id);
|
||||
break;
|
||||
case ID_ME:
|
||||
case ID_CU:
|
||||
@@ -628,6 +629,12 @@ void BKE_libblock_relink_ex(
|
||||
switch (GS(id->name)) {
|
||||
case ID_SCE:
|
||||
case ID_GR: {
|
||||
/* Note: here we know which collection we have affected, so at lest for NULL children
|
||||
* detection we can only process that one.
|
||||
* This is also a required fix in case `id` would not be in Main anymore, which can happen
|
||||
* e.g. when called from `id_delete`. */
|
||||
Collection *owner_collection = (GS(id->name) == ID_GR) ? (Collection *)id :
|
||||
((Scene *)id)->master_collection;
|
||||
if (old_id) {
|
||||
switch (GS(old_id->name)) {
|
||||
case ID_OB:
|
||||
@@ -636,7 +643,7 @@ void BKE_libblock_relink_ex(
|
||||
break;
|
||||
case ID_GR:
|
||||
libblock_remap_data_postprocess_collection_update(
|
||||
bmain, (Collection *)old_id, (Collection *)new_id);
|
||||
bmain, owner_collection, (Collection *)old_id, (Collection *)new_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -644,7 +651,7 @@ void BKE_libblock_relink_ex(
|
||||
}
|
||||
else {
|
||||
/* No choice but to check whole objects/collections. */
|
||||
libblock_remap_data_postprocess_collection_update(bmain, NULL, NULL);
|
||||
libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL);
|
||||
libblock_remap_data_postprocess_object_update(bmain, NULL, NULL);
|
||||
}
|
||||
break;
|
||||
|
@@ -1065,6 +1065,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
Curve *curve = (Curve *)object->data;
|
||||
Curve remapped_curve = *curve;
|
||||
Object remapped_object = *object;
|
||||
remapped_object.runtime.bb = NULL;
|
||||
remapped_object.data = &remapped_curve;
|
||||
|
||||
/* Clear all modifiers for the bevel object.
|
||||
@@ -1077,6 +1078,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
Object bevel_object = {{NULL}};
|
||||
if (remapped_curve.bevobj != NULL) {
|
||||
bevel_object = *remapped_curve.bevobj;
|
||||
bevel_object.runtime.bb = NULL;
|
||||
BLI_listbase_clear(&bevel_object.modifiers);
|
||||
remapped_curve.bevobj = &bevel_object;
|
||||
}
|
||||
@@ -1085,6 +1087,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
Object taper_object = {{NULL}};
|
||||
if (remapped_curve.taperobj != NULL) {
|
||||
taper_object = *remapped_curve.taperobj;
|
||||
taper_object.runtime.bb = NULL;
|
||||
BLI_listbase_clear(&taper_object.modifiers);
|
||||
remapped_curve.taperobj = &taper_object;
|
||||
}
|
||||
@@ -1107,6 +1110,10 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(remapped_object.runtime.bb);
|
||||
MEM_SAFE_FREE(taper_object.runtime.bb);
|
||||
MEM_SAFE_FREE(bevel_object.runtime.bb);
|
||||
|
||||
BKE_object_free_curve_cache(&bevel_object);
|
||||
BKE_object_free_curve_cache(&taper_object);
|
||||
}
|
||||
@@ -1535,7 +1542,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
* check whether it is still true with Mesh */
|
||||
Mesh tmp = *mesh_dst;
|
||||
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
|
||||
int did_shapekeys = 0;
|
||||
bool did_shapekeys = false;
|
||||
eCDAllocType alloctype = CD_DUPLICATE;
|
||||
|
||||
if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
|
||||
@@ -1590,7 +1597,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
}
|
||||
|
||||
shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
|
||||
did_shapekeys = 1;
|
||||
did_shapekeys = true;
|
||||
}
|
||||
|
||||
/* copy texture space */
|
||||
@@ -1619,13 +1626,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
totedge);
|
||||
}
|
||||
if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
|
||||
/* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the
|
||||
* BKE_mesh_update_customdata_pointers() call below. */
|
||||
tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
|
||||
tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
|
||||
|
||||
CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
|
||||
CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
|
||||
CustomData_add_layer(&tmp.ldata,
|
||||
CD_MLOOP,
|
||||
CD_ASSIGN,
|
||||
(alloctype == CD_ASSIGN) ? mesh_src->mloop :
|
||||
MEM_dupallocN(mesh_src->mloop),
|
||||
tmp.totloop);
|
||||
CustomData_add_layer(&tmp.pdata,
|
||||
CD_MPOLY,
|
||||
CD_ASSIGN,
|
||||
(alloctype == CD_ASSIGN) ? mesh_src->mpoly :
|
||||
MEM_dupallocN(mesh_src->mpoly),
|
||||
tmp.totpoly);
|
||||
}
|
||||
|
||||
/* object had got displacement layer, should copy this layer to save sculpted data */
|
||||
@@ -1634,15 +1646,16 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
if (totloop == mesh_dst->totloop) {
|
||||
MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
|
||||
CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
|
||||
if (alloctype == CD_ASSIGN) {
|
||||
/* Assign NULL to prevent double-free. */
|
||||
CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* yes, must be before _and_ after tessellate */
|
||||
BKE_mesh_update_customdata_pointers(&tmp, false);
|
||||
|
||||
/* since 2.65 caller must do! */
|
||||
// BKE_mesh_tessface_calc(&tmp);
|
||||
|
||||
CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
|
||||
CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
|
||||
CustomData_free(&mesh_dst->fdata, mesh_dst->totface);
|
||||
|
@@ -51,7 +51,7 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mm
|
||||
(axis == 1 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Y) ||
|
||||
(axis == 2 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Z));
|
||||
|
||||
const float bisect_distance = 0.001f;
|
||||
const float bisect_distance = mmd->bisect_threshold;
|
||||
|
||||
Mesh *result;
|
||||
BMesh *bm;
|
||||
|
158
source/blender/blenkernel/intern/mesh_sample.cc
Normal file
158
source/blender/blenkernel/intern/mesh_sample.cc
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
#include "BKE_mesh_sample.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
namespace blender::bke::mesh_surface_sample {
|
||||
|
||||
static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
|
||||
{
|
||||
/* This only updates a cache and can be considered to be logically const. */
|
||||
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
|
||||
const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
|
||||
return {looptris, looptris_len};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
|
||||
const Span<int> looptri_indices,
|
||||
const Span<float3> bary_coords,
|
||||
const VArray<T> &data_in,
|
||||
const MutableSpan<T> data_out)
|
||||
{
|
||||
const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
|
||||
|
||||
for (const int i : bary_coords.index_range()) {
|
||||
const int looptri_index = looptri_indices[i];
|
||||
const MLoopTri &looptri = looptris[looptri_index];
|
||||
const float3 &bary_coord = bary_coords[i];
|
||||
|
||||
const int v0_index = mesh.mloop[looptri.tri[0]].v;
|
||||
const int v1_index = mesh.mloop[looptri.tri[1]].v;
|
||||
const int v2_index = mesh.mloop[looptri.tri[2]].v;
|
||||
|
||||
const T v0 = data_in[v0_index];
|
||||
const T v1 = data_in[v1_index];
|
||||
const T v2 = data_in[v2_index];
|
||||
|
||||
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
|
||||
data_out[i] = interpolated_value;
|
||||
}
|
||||
}
|
||||
|
||||
void sample_point_attribute(const Mesh &mesh,
|
||||
const Span<int> looptri_indices,
|
||||
const Span<float3> bary_coords,
|
||||
const GVArray &data_in,
|
||||
const GMutableSpan data_out)
|
||||
{
|
||||
BLI_assert(data_out.size() == looptri_indices.size());
|
||||
BLI_assert(data_out.size() == bary_coords.size());
|
||||
BLI_assert(data_in.size() == mesh.totvert);
|
||||
BLI_assert(data_in.type() == data_out.type());
|
||||
|
||||
const CPPType &type = data_in.type();
|
||||
attribute_math::convert_to_static_type(type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
sample_point_attribute<T>(
|
||||
mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
|
||||
const Span<int> looptri_indices,
|
||||
const Span<float3> bary_coords,
|
||||
const VArray<T> &data_in,
|
||||
const MutableSpan<T> data_out)
|
||||
{
|
||||
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
|
||||
|
||||
for (const int i : bary_coords.index_range()) {
|
||||
const int looptri_index = looptri_indices[i];
|
||||
const MLoopTri &looptri = looptris[looptri_index];
|
||||
const float3 &bary_coord = bary_coords[i];
|
||||
|
||||
const int loop_index_0 = looptri.tri[0];
|
||||
const int loop_index_1 = looptri.tri[1];
|
||||
const int loop_index_2 = looptri.tri[2];
|
||||
|
||||
const T v0 = data_in[loop_index_0];
|
||||
const T v1 = data_in[loop_index_1];
|
||||
const T v2 = data_in[loop_index_2];
|
||||
|
||||
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
|
||||
data_out[i] = interpolated_value;
|
||||
}
|
||||
}
|
||||
|
||||
void sample_corner_attribute(const Mesh &mesh,
|
||||
const Span<int> looptri_indices,
|
||||
const Span<float3> bary_coords,
|
||||
const GVArray &data_in,
|
||||
const GMutableSpan data_out)
|
||||
{
|
||||
BLI_assert(data_out.size() == looptri_indices.size());
|
||||
BLI_assert(data_out.size() == bary_coords.size());
|
||||
BLI_assert(data_in.size() == mesh.totloop);
|
||||
BLI_assert(data_in.type() == data_out.type());
|
||||
|
||||
const CPPType &type = data_in.type();
|
||||
attribute_math::convert_to_static_type(type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
sample_corner_attribute<T>(
|
||||
mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void sample_face_attribute(const Mesh &mesh,
|
||||
const Span<int> looptri_indices,
|
||||
const VArray<T> &data_in,
|
||||
const MutableSpan<T> data_out)
|
||||
{
|
||||
Span<MLoopTri> looptris = get_mesh_looptris(mesh);
|
||||
|
||||
for (const int i : data_out.index_range()) {
|
||||
const int looptri_index = looptri_indices[i];
|
||||
const MLoopTri &looptri = looptris[looptri_index];
|
||||
const int poly_index = looptri.poly;
|
||||
data_out[i] = data_in[poly_index];
|
||||
}
|
||||
}
|
||||
|
||||
void sample_face_attribute(const Mesh &mesh,
|
||||
const Span<int> looptri_indices,
|
||||
const GVArray &data_in,
|
||||
const GMutableSpan data_out)
|
||||
{
|
||||
BLI_assert(data_out.size() == looptri_indices.size());
|
||||
BLI_assert(data_in.size() == mesh.totpoly);
|
||||
BLI_assert(data_in.type() == data_out.type());
|
||||
|
||||
const CPPType &type = data_in.type();
|
||||
attribute_math::convert_to_static_type(type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::bke::mesh_surface_sample
|
@@ -4946,6 +4946,10 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_boolean();
|
||||
register_node_type_geo_bounding_box();
|
||||
register_node_type_geo_collection_info();
|
||||
register_node_type_geo_curve_sample_points();
|
||||
register_node_type_geo_curve_to_mesh();
|
||||
register_node_type_geo_curve_transform_test();
|
||||
register_node_type_geo_curve_trim();
|
||||
register_node_type_geo_edge_split();
|
||||
register_node_type_geo_is_viewport();
|
||||
register_node_type_geo_join_geometry();
|
||||
@@ -4968,6 +4972,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_sample_texture();
|
||||
register_node_type_geo_subdivide();
|
||||
register_node_type_geo_subdivision_surface();
|
||||
register_node_type_geo_switch();
|
||||
register_node_type_geo_transform();
|
||||
register_node_type_geo_triangulate();
|
||||
register_node_type_geo_volume_to_mesh();
|
||||
|
@@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
|
||||
if (ob->type == OB_HAIR) {
|
||||
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
|
||||
}
|
||||
if (ob->type == OB_POINTCLOUD) {
|
||||
if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) {
|
||||
return (mti->modifyGeometrySet != NULL);
|
||||
}
|
||||
if (ob->type == OB_VOLUME) {
|
||||
return (mti->modifyVolume != NULL);
|
||||
}
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
|
||||
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
|
||||
return false;
|
||||
|
@@ -2636,6 +2636,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
|
||||
|
||||
Scene *scene = DEG_get_input_scene(depsgraph);
|
||||
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
|
||||
bool used_multiple_passes = false;
|
||||
|
||||
bool run_callbacks = DEG_id_type_any_updated(depsgraph);
|
||||
if (run_callbacks) {
|
||||
@@ -2672,8 +2673,6 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
|
||||
* If there are no relations changed by the callback this call will do nothing. */
|
||||
DEG_graph_relations_update(depsgraph);
|
||||
}
|
||||
/* Inform editors about possible changes. */
|
||||
DEG_editors_update(bmain, depsgraph, scene, view_layer, false);
|
||||
|
||||
/* If user callback did not tag anything for update we can skip second iteration.
|
||||
* Otherwise we update scene once again, but without running callbacks to bring
|
||||
@@ -2682,8 +2681,22 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear recalc flags for second pass, but back them up for editors update. */
|
||||
const bool backup = true;
|
||||
DEG_ids_clear_recalc(depsgraph, backup);
|
||||
used_multiple_passes = true;
|
||||
run_callbacks = false;
|
||||
}
|
||||
|
||||
/* Inform editors about changes, using recalc flags from both passes. */
|
||||
if (used_multiple_passes) {
|
||||
DEG_ids_restore_recalc(depsgraph);
|
||||
}
|
||||
const bool is_time_update = false;
|
||||
DEG_editors_update(depsgraph, is_time_update);
|
||||
|
||||
const bool backup = false;
|
||||
DEG_ids_clear_recalc(depsgraph, backup);
|
||||
}
|
||||
|
||||
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
|
||||
@@ -2697,11 +2710,11 @@ void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
|
||||
}
|
||||
|
||||
/* applies changes right away, does all sets too */
|
||||
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
|
||||
void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool clear_recalc)
|
||||
{
|
||||
Scene *scene = DEG_get_input_scene(depsgraph);
|
||||
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
|
||||
Main *bmain = DEG_get_bmain(depsgraph);
|
||||
bool used_multiple_passes = false;
|
||||
|
||||
/* Keep this first. */
|
||||
BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE);
|
||||
@@ -2738,16 +2751,38 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
|
||||
DEG_graph_relations_update(depsgraph);
|
||||
}
|
||||
|
||||
/* Inform editors about possible changes. */
|
||||
DEG_editors_update(bmain, depsgraph, scene, view_layer, true);
|
||||
|
||||
/* If user callback did not tag anything for update we can skip second iteration.
|
||||
* Otherwise we update scene once again, but without running callbacks to bring
|
||||
* scene to a fully evaluated state with user modifications taken into account. */
|
||||
if (DEG_is_fully_evaluated(depsgraph)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear recalc flags for second pass, but back them up for editors update. */
|
||||
const bool backup = true;
|
||||
DEG_ids_clear_recalc(depsgraph, backup);
|
||||
used_multiple_passes = true;
|
||||
}
|
||||
|
||||
/* Inform editors about changes, using recalc flags from both passes. */
|
||||
if (used_multiple_passes) {
|
||||
DEG_ids_restore_recalc(depsgraph);
|
||||
}
|
||||
|
||||
const bool is_time_update = true;
|
||||
DEG_editors_update(depsgraph, is_time_update);
|
||||
|
||||
/* Clear recalc flags, can be skipped for e.g. renderers that will read these
|
||||
* and clear the flags later. */
|
||||
if (clear_recalc) {
|
||||
const bool backup = false;
|
||||
DEG_ids_clear_recalc(depsgraph, backup);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
|
||||
{
|
||||
BKE_scene_graph_update_for_newframe_ex(depsgraph, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3507,8 +3542,8 @@ GHash *BKE_scene_undo_depsgraphs_extract(Main *bmain)
|
||||
|
||||
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
|
||||
if (scene->depsgraph_hash == NULL) {
|
||||
/* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will be
|
||||
* built so this pointer may be NULL. */
|
||||
/* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will
|
||||
* be built so this pointer may be NULL. */
|
||||
continue;
|
||||
}
|
||||
for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL;
|
||||
|
339
source/blender/blenkernel/intern/spline_base.cc
Normal file
339
source/blender/blenkernel/intern/spline_base.cc
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
Spline::Type Spline::type() const
|
||||
{
|
||||
return this->type_;
|
||||
}
|
||||
|
||||
int Spline::evaluated_edges_size() const
|
||||
{
|
||||
const int points_len = this->evaluated_points_size();
|
||||
|
||||
return this->is_cyclic ? points_len : points_len - 1;
|
||||
}
|
||||
|
||||
float Spline::length() const
|
||||
{
|
||||
return this->evaluated_lengths().last();
|
||||
}
|
||||
|
||||
int Spline::segments_size() const
|
||||
{
|
||||
const int points_len = this->size();
|
||||
|
||||
return this->is_cyclic ? points_len : points_len - 1;
|
||||
}
|
||||
|
||||
static void accumulate_lengths(Span<float3> positions,
|
||||
const bool is_cyclic,
|
||||
MutableSpan<float> lengths)
|
||||
{
|
||||
float length = 0.0f;
|
||||
for (const int i : IndexRange(positions.size() - 1)) {
|
||||
length += float3::distance(positions[i], positions[i + 1]);
|
||||
lengths[i] = length;
|
||||
}
|
||||
if (is_cyclic) {
|
||||
lengths.last() = length + float3::distance(positions.last(), positions.first());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
|
||||
* length of the subsequent segment, i.e. the first value is the length of the first segment rather
|
||||
* than 0. This calculation is rather trivial, and only depends on the evaluated positions.
|
||||
* However, the results are used often, so it makes sense to cache it.
|
||||
*/
|
||||
Span<float> Spline::evaluated_lengths() const
|
||||
{
|
||||
if (!this->length_cache_dirty_) {
|
||||
return evaluated_lengths_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->length_cache_mutex_};
|
||||
if (!this->length_cache_dirty_) {
|
||||
return evaluated_lengths_cache_;
|
||||
}
|
||||
|
||||
const int total = this->evaluated_edges_size();
|
||||
this->evaluated_lengths_cache_.resize(total);
|
||||
|
||||
Span<float3> positions = this->evaluated_positions();
|
||||
accumulate_lengths(positions, this->is_cyclic, this->evaluated_lengths_cache_);
|
||||
|
||||
this->length_cache_dirty_ = false;
|
||||
return evaluated_lengths_cache_;
|
||||
}
|
||||
|
||||
static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
|
||||
{
|
||||
const float3 dir_prev = (middle - prev).normalized();
|
||||
const float3 dir_next = (next - middle).normalized();
|
||||
|
||||
return (dir_prev + dir_next).normalized();
|
||||
}
|
||||
|
||||
static void calculate_tangents(Span<float3> positions,
|
||||
const bool is_cyclic,
|
||||
MutableSpan<float3> tangents)
|
||||
{
|
||||
if (positions.size() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const int i : IndexRange(1, positions.size() - 2)) {
|
||||
tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
|
||||
}
|
||||
|
||||
if (is_cyclic) {
|
||||
const float3 &second_to_last = positions[positions.size() - 2];
|
||||
const float3 &last = positions.last();
|
||||
const float3 &first = positions.first();
|
||||
const float3 &second = positions[1];
|
||||
tangents.first() = direction_bisect(last, first, second);
|
||||
tangents.last() = direction_bisect(second_to_last, last, first);
|
||||
}
|
||||
else {
|
||||
tangents.first() = (positions[1] - positions[0]).normalized();
|
||||
tangents.last() = (positions.last() - positions[positions.size() - 1]).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-owning access to the direction of the curve at each evaluated point.
|
||||
*/
|
||||
Span<float3> Spline::evaluated_tangents() const
|
||||
{
|
||||
if (!this->tangent_cache_dirty_) {
|
||||
return evaluated_tangents_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->tangent_cache_mutex_};
|
||||
if (!this->tangent_cache_dirty_) {
|
||||
return evaluated_tangents_cache_;
|
||||
}
|
||||
|
||||
const int total = this->evaluated_points_size();
|
||||
this->evaluated_tangents_cache_.resize(total);
|
||||
|
||||
Span<float3> positions = this->evaluated_positions();
|
||||
|
||||
calculate_tangents(positions, this->is_cyclic, this->evaluated_tangents_cache_);
|
||||
|
||||
this->correct_end_tangents();
|
||||
|
||||
this->tangent_cache_dirty_ = false;
|
||||
return evaluated_tangents_cache_;
|
||||
}
|
||||
|
||||
#if 0 /* Not supported yet, has errors. */
|
||||
static float3 initial_normal(const float3 first_tangent)
|
||||
{
|
||||
/* TODO: Should be is "almost" zero. */
|
||||
if (first_tangent.is_zero()) {
|
||||
return float3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
const float3 normal = float3::cross(first_tangent, float3(0.0f, 0.0f, 1.0f));
|
||||
if (!normal.is_zero()) {
|
||||
return normal.normalized();
|
||||
}
|
||||
|
||||
return float3::cross(first_tangent, float3(0.0f, 1.0f, 0.0f)).normalized();
|
||||
}
|
||||
|
||||
static float3 rotate_around_axis(const float3 dir, const float3 axis, const float angle)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(axis);
|
||||
const float3 scaled_axis = axis * float3::dot(dir, axis);
|
||||
const float3 sub = dir - scaled_axis;
|
||||
const float3 cross = float3::cross(sub, sub);
|
||||
const float sin = std::sin(angle);
|
||||
const float cos = std::cos(angle);
|
||||
return (scaled_axis + sub * cos + cross * sin).normalized();
|
||||
}
|
||||
|
||||
static float3 project_on_center_plane(const float3 vector, const float3 plane_normal)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(plane_normal);
|
||||
const float distance = float3::dot(vector, plane_normal);
|
||||
const float3 projection_vector = plane_normal * -distance;
|
||||
return vector + projection_vector;
|
||||
}
|
||||
|
||||
static float3 propagate_normal(const float3 last_normal,
|
||||
const float3 last_tangent,
|
||||
const float3 current_tangent)
|
||||
{
|
||||
const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
|
||||
|
||||
if (angle == 0.0f) {
|
||||
return last_normal;
|
||||
}
|
||||
|
||||
const float3 axis = float3::cross(last_tangent, current_tangent).normalized();
|
||||
|
||||
const float3 new_normal = rotate_around_axis(last_normal, axis, angle);
|
||||
|
||||
return project_on_center_plane(new_normal, current_tangent).normalized();
|
||||
}
|
||||
|
||||
static void apply_rotation_gradient(Span<float3> tangents,
|
||||
MutableSpan<float3> normals,
|
||||
const float full_angle)
|
||||
{
|
||||
|
||||
float remaining_rotation = full_angle;
|
||||
float done_rotation = 0.0f;
|
||||
for (const int i : IndexRange(1, normals.size() - 1)) {
|
||||
if (angle_v3v3(tangents[i], tangents[i - 1]) < 0.001f) {
|
||||
normals[i] = rotate_around_axis(normals[i], tangents[i], done_rotation);
|
||||
}
|
||||
else {
|
||||
const float angle = remaining_rotation / (normals.size() - i);
|
||||
normals[i] = rotate_around_axis(normals[i], tangents[i], angle + done_rotation);
|
||||
remaining_rotation -= angle;
|
||||
done_rotation += angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void make_normals_cyclic(Span<float3> tangents, MutableSpan<float3> normals)
|
||||
{
|
||||
const float3 last_normal = propagate_normal(normals.last(), tangents.last(), tangents.first());
|
||||
|
||||
float angle = angle_normalized_v3v3(normals.first(), last_normal);
|
||||
|
||||
const float3 cross = float3::cross(normals.first(), last_normal);
|
||||
if (float3::dot(cross, tangents.first()) <= 0.0f) {
|
||||
angle = -angle;
|
||||
}
|
||||
|
||||
apply_rotation_gradient(tangents, normals, -angle);
|
||||
}
|
||||
|
||||
/* This algorithm is a copy from animation nodes bezier normal calculation.
|
||||
* TODO: Explore different methods, this also doesn't work right now. */
|
||||
static void calculate_normals_minimum_twist(Span<float3> tangents,
|
||||
const bool is_cyclic,
|
||||
MutableSpan<float3> normals)
|
||||
{
|
||||
if (normals.size() == 1) {
|
||||
normals.first() = float3(1.0f, 0.0f, 0.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Start by calculating a simple normal for the first point. */
|
||||
normals[0] = initial_normal(tangents[0]);
|
||||
|
||||
/* Then propogate that normal along the spline. */
|
||||
for (const int i : IndexRange(1, normals.size() - 1)) {
|
||||
normals[i] = propagate_normal(normals[i - 1], tangents[i - 1], tangents[i]);
|
||||
}
|
||||
|
||||
if (is_cyclic) {
|
||||
make_normals_cyclic(tangents, normals);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
|
||||
{
|
||||
for (const int i : normals.index_range()) {
|
||||
normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-owning access to the direction vectors perpendicular to the tangents at every
|
||||
* evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
|
||||
*/
|
||||
Span<float3> Spline::evaluated_normals() const
|
||||
{
|
||||
if (!this->normal_cache_dirty_) {
|
||||
return evaluated_normals_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->normal_cache_mutex_};
|
||||
if (!this->normal_cache_dirty_) {
|
||||
return evaluated_normals_cache_;
|
||||
}
|
||||
|
||||
const int total = this->evaluated_points_size();
|
||||
this->evaluated_normals_cache_.resize(total);
|
||||
|
||||
Span<float3> tangents = this->evaluated_tangents();
|
||||
|
||||
#if 0 /* Not supported yet, has errors. */
|
||||
switch (this->normal_mode) {
|
||||
case NormalCalculationMode::Minimum:
|
||||
calculate_normals_minimum_twist(tangents, is_cyclic, this->evaluated_normals_cache_);
|
||||
break;
|
||||
case NormalCalculationMode::ZUp:
|
||||
break;
|
||||
case NormalCalculationMode::Tangent:
|
||||
calculate_normals_tangent(tangents, this->evaluated_normals_cache_);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
calculate_normals_z_up(tangents, this->evaluated_normals_cache_);
|
||||
|
||||
this->normal_cache_dirty_ = false;
|
||||
return evaluated_normals_cache_;
|
||||
}
|
||||
|
||||
Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
|
||||
{
|
||||
return this->lookup_evaluated_length(this->length() * factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* \note This does not support extrapolation currently.
|
||||
*/
|
||||
Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
|
||||
{
|
||||
BLI_assert(length >= 0.0f && length <= this->length());
|
||||
|
||||
Span<float> lengths = this->evaluated_lengths();
|
||||
|
||||
const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
|
||||
const int index = offset - lengths.begin();
|
||||
const int next_index = (index == this->size() - 1) ? 0 : index + 1;
|
||||
|
||||
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
|
||||
const float factor = (length - previous_length) / (lengths[index] - previous_length);
|
||||
|
||||
return LookupResult{index, next_index, factor};
|
||||
}
|
||||
|
||||
void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
|
||||
{
|
||||
Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
|
||||
for (const float3 &position : positions) {
|
||||
minmax_v3v3_v3(min, max, position);
|
||||
}
|
||||
}
|
483
source/blender/blenkernel/intern/spline_bezier.cc
Normal file
483
source/blender/blenkernel/intern/spline_bezier.cc
Normal file
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::Array;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
SplinePtr BezierSpline::copy() const
|
||||
{
|
||||
SplinePtr new_spline = std::make_unique<BezierSpline>(*this);
|
||||
|
||||
return new_spline;
|
||||
}
|
||||
|
||||
int BezierSpline::size() const
|
||||
{
|
||||
const int size = this->positions_.size();
|
||||
BLI_assert(this->handle_types_start_.size() == size);
|
||||
BLI_assert(this->handle_positions_start_.size() == size);
|
||||
BLI_assert(this->handle_types_end_.size() == size);
|
||||
BLI_assert(this->handle_positions_end_.size() == size);
|
||||
BLI_assert(this->radii_.size() == size);
|
||||
BLI_assert(this->tilts_.size() == size);
|
||||
return size;
|
||||
}
|
||||
|
||||
int BezierSpline::resolution() const
|
||||
{
|
||||
return this->resolution_;
|
||||
}
|
||||
|
||||
void BezierSpline::set_resolution(const int value)
|
||||
{
|
||||
this->resolution_ = value;
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
MutableSpan<float3> BezierSpline::positions()
|
||||
{
|
||||
return this->positions_;
|
||||
}
|
||||
Span<float3> BezierSpline::positions() const
|
||||
{
|
||||
return this->positions_;
|
||||
}
|
||||
MutableSpan<float> BezierSpline::radii()
|
||||
{
|
||||
return this->radii_;
|
||||
}
|
||||
Span<float> BezierSpline::radii() const
|
||||
{
|
||||
return this->radii_;
|
||||
}
|
||||
MutableSpan<float> BezierSpline::tilts()
|
||||
{
|
||||
return this->tilts_;
|
||||
}
|
||||
Span<float> BezierSpline::tilts() const
|
||||
{
|
||||
return this->tilts_;
|
||||
}
|
||||
Span<BezierSpline::HandleType> BezierSpline::handle_types_start() const
|
||||
{
|
||||
return this->handle_types_start_;
|
||||
}
|
||||
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_start()
|
||||
{
|
||||
return this->handle_types_start_;
|
||||
}
|
||||
Span<float3> BezierSpline::handle_positions_start() const
|
||||
{
|
||||
return this->handle_positions_start_;
|
||||
}
|
||||
MutableSpan<float3> BezierSpline::handle_positions_start()
|
||||
{
|
||||
return this->handle_positions_start_;
|
||||
}
|
||||
Span<BezierSpline::HandleType> BezierSpline::handle_types_end() const
|
||||
{
|
||||
return this->handle_types_end_;
|
||||
}
|
||||
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_end()
|
||||
{
|
||||
return this->handle_types_end_;
|
||||
}
|
||||
Span<float3> BezierSpline::handle_positions_end() const
|
||||
{
|
||||
return this->handle_positions_end_;
|
||||
}
|
||||
MutableSpan<float3> BezierSpline::handle_positions_end()
|
||||
{
|
||||
return this->handle_positions_end_;
|
||||
}
|
||||
|
||||
void BezierSpline::add_point(const float3 position,
|
||||
const HandleType handle_type_start,
|
||||
const float3 handle_position_start,
|
||||
const HandleType handle_type_end,
|
||||
const float3 handle_position_end,
|
||||
const float radius,
|
||||
const float tilt)
|
||||
{
|
||||
handle_types_start_.append(handle_type_start);
|
||||
handle_positions_start_.append(handle_position_start);
|
||||
positions_.append(position);
|
||||
handle_types_end_.append(handle_type_end);
|
||||
handle_positions_end_.append(handle_position_end);
|
||||
radii_.append(radius);
|
||||
tilts_.append(tilt);
|
||||
}
|
||||
|
||||
void BezierSpline::drop_front(const int count)
|
||||
{
|
||||
BLI_assert(this->size() - count > 0);
|
||||
this->handle_types_start_.remove(0, count);
|
||||
this->handle_positions_start_.remove(0, count);
|
||||
this->positions_.remove(0, count);
|
||||
this->handle_types_end_.remove(0, count);
|
||||
this->handle_positions_end_.remove(0, count);
|
||||
this->radii_.remove(0, count);
|
||||
this->tilts_.remove(0, count);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void BezierSpline::drop_back(const int count)
|
||||
{
|
||||
const int new_size = this->size() - count;
|
||||
BLI_assert(new_size > 0);
|
||||
this->handle_types_start_.resize(new_size);
|
||||
this->handle_positions_start_.resize(new_size);
|
||||
this->positions_.resize(new_size);
|
||||
this->handle_types_end_.resize(new_size);
|
||||
this->handle_positions_end_.resize(new_size);
|
||||
this->radii_.resize(new_size);
|
||||
this->tilts_.resize(new_size);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
bool BezierSpline::point_is_sharp(const int index) const
|
||||
{
|
||||
return ELEM(handle_types_start_[index], HandleType::Vector, HandleType::Free) ||
|
||||
ELEM(handle_types_end_[index], HandleType::Vector, HandleType::Free);
|
||||
}
|
||||
|
||||
bool BezierSpline::handle_start_is_automatic(const int index) const
|
||||
{
|
||||
return ELEM(handle_types_start_[index], HandleType::Free, HandleType::Align);
|
||||
}
|
||||
|
||||
bool BezierSpline::handle_end_is_automatic(const int index) const
|
||||
{
|
||||
return ELEM(handle_types_end_[index], HandleType::Free, HandleType::Align);
|
||||
}
|
||||
|
||||
void BezierSpline::move_control_point(const int index, const float3 new_position)
|
||||
{
|
||||
const float3 position_delta = new_position - positions_[index];
|
||||
if (!this->handle_start_is_automatic(index)) {
|
||||
handle_positions_start_[index] += position_delta;
|
||||
}
|
||||
if (!this->handle_end_is_automatic(index)) {
|
||||
handle_positions_end_[index] += position_delta;
|
||||
}
|
||||
positions_[index] = new_position;
|
||||
}
|
||||
|
||||
bool BezierSpline::segment_is_vector(const int index) const
|
||||
{
|
||||
if (index == this->size() - 1) {
|
||||
BLI_assert(this->is_cyclic);
|
||||
return this->handle_types_end_.last() == HandleType::Vector &&
|
||||
this->handle_types_start_.first() == HandleType::Vector;
|
||||
}
|
||||
return this->handle_types_end_[index] == HandleType::Vector &&
|
||||
this->handle_types_start_[index + 1] == HandleType::Vector;
|
||||
}
|
||||
|
||||
void BezierSpline::mark_cache_invalid()
|
||||
{
|
||||
this->offset_cache_dirty_ = true;
|
||||
this->position_cache_dirty_ = true;
|
||||
this->mapping_cache_dirty_ = true;
|
||||
this->tangent_cache_dirty_ = true;
|
||||
this->normal_cache_dirty_ = true;
|
||||
this->length_cache_dirty_ = true;
|
||||
}
|
||||
|
||||
int BezierSpline::evaluated_points_size() const
|
||||
{
|
||||
const int points_len = this->size();
|
||||
BLI_assert(points_len > 0);
|
||||
|
||||
const int last_offset = this->control_point_offsets().last();
|
||||
if (this->is_cyclic) {
|
||||
return last_offset + (this->segment_is_vector(points_len - 1) ? 0 : this->resolution_);
|
||||
}
|
||||
|
||||
return last_offset + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the spline is not cyclic, the direction for the first and last points is just the
|
||||
* direction formed by the corresponding handles and control points. In the unlikely situation
|
||||
* that the handles define a zero direction, fallback to using the direction defined by the
|
||||
* first and last evaluated segments already calculated in #Spline::evaluated_tangents().
|
||||
*/
|
||||
void BezierSpline::correct_end_tangents() const
|
||||
{
|
||||
MutableSpan<float3> tangents(this->evaluated_tangents_cache_);
|
||||
|
||||
if (handle_positions_start_.first() != positions_.first()) {
|
||||
tangents.first() = (positions_.first() - handle_positions_start_.first()).normalized();
|
||||
}
|
||||
if (handle_positions_end_.last() != positions_.last()) {
|
||||
tangents.last() = (handle_positions_end_.last() - positions_.last()).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
static void bezier_forward_difference_3d(const float3 &point_0,
|
||||
const float3 &point_1,
|
||||
const float3 &point_2,
|
||||
const float3 &point_3,
|
||||
MutableSpan<float3> result)
|
||||
{
|
||||
const float len = static_cast<float>(result.size());
|
||||
const float len_squared = len * len;
|
||||
const float len_cubed = len_squared * len;
|
||||
BLI_assert(len > 0.0f);
|
||||
|
||||
const float3 rt1 = 3.0f * (point_1 - point_0) / len;
|
||||
const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) / len_squared;
|
||||
const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) / len_cubed;
|
||||
|
||||
float3 q0 = point_0;
|
||||
float3 q1 = rt1 + rt2 + rt3;
|
||||
float3 q2 = 2.0f * rt2 + 6.0f * rt3;
|
||||
float3 q3 = 6.0f * rt3;
|
||||
for (const int i : result.index_range()) {
|
||||
result[i] = q0;
|
||||
q0 += q1;
|
||||
q1 += q2;
|
||||
q2 += q3;
|
||||
}
|
||||
}
|
||||
|
||||
void BezierSpline::evaluate_bezier_segment(const int index,
|
||||
const int next_index,
|
||||
MutableSpan<float3> positions) const
|
||||
{
|
||||
if (this->segment_is_vector(index)) {
|
||||
positions.first() = this->positions_[index];
|
||||
}
|
||||
else {
|
||||
bezier_forward_difference_3d(this->positions_[index],
|
||||
this->handle_positions_end_[index],
|
||||
this->handle_positions_start_[next_index],
|
||||
this->positions_[next_index],
|
||||
positions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access to a cache of offsets into the evaluated point array for each control point.
|
||||
* This is important because while most control point edges generate the number of edges specified
|
||||
* by the resolution, vector segments only generate one edge.
|
||||
*/
|
||||
Span<int> BezierSpline::control_point_offsets() const
|
||||
{
|
||||
if (!this->offset_cache_dirty_) {
|
||||
return this->offset_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->offset_cache_mutex_};
|
||||
if (!this->offset_cache_dirty_) {
|
||||
return this->offset_cache_;
|
||||
}
|
||||
|
||||
const int points_len = this->size();
|
||||
this->offset_cache_.resize(points_len);
|
||||
|
||||
MutableSpan<int> offsets = this->offset_cache_;
|
||||
|
||||
int offset = 0;
|
||||
for (const int i : IndexRange(points_len - 1)) {
|
||||
offsets[i] = offset;
|
||||
offset += this->segment_is_vector(i) ? 1 : this->resolution_;
|
||||
}
|
||||
offsets.last() = offset;
|
||||
|
||||
this->offset_cache_dirty_ = false;
|
||||
return offsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-owning access to an array of values ontains the information necessary to
|
||||
* interpolate values from the original control points to evaluated points. The control point
|
||||
* index is the integer part of each value, and the factor used for interpolating to the next
|
||||
* control point is the remaining factional part.
|
||||
*/
|
||||
Span<float> BezierSpline::evaluated_mappings() const
|
||||
{
|
||||
if (!this->mapping_cache_dirty_) {
|
||||
return this->evaluated_mapping_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->mapping_cache_mutex_};
|
||||
if (!this->mapping_cache_dirty_) {
|
||||
return this->evaluated_mapping_cache_;
|
||||
}
|
||||
|
||||
const int size = this->size();
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
this->evaluated_mapping_cache_.resize(eval_size);
|
||||
MutableSpan<float> mappings = this->evaluated_mapping_cache_;
|
||||
|
||||
Span<int> offsets = this->control_point_offsets();
|
||||
Span<float> lengths = this->evaluated_lengths();
|
||||
|
||||
/* Subtract one from the index into the lengths array to get the length
|
||||
* at the start point rather than the length at the end of the edge. */
|
||||
|
||||
const float first_segment_len = lengths[offsets[1] - 1];
|
||||
for (const int eval_index : IndexRange(0, offsets[1])) {
|
||||
const float point_len = eval_index == 0 ? 0.0f : lengths[eval_index - 1];
|
||||
const float length_factor = (first_segment_len == 0.0f) ? 0.0f : 1.0f / first_segment_len;
|
||||
|
||||
mappings[eval_index] = point_len * length_factor;
|
||||
}
|
||||
|
||||
const int grain_size = std::max(512 / this->resolution_, 1);
|
||||
blender::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
const float segment_start_len = lengths[offsets[i] - 1];
|
||||
const float segment_end_len = lengths[offsets[i + 1] - 1];
|
||||
const float segment_len = segment_end_len - segment_start_len;
|
||||
const float length_factor = (segment_len == 0.0f) ? 0.0f : 1.0f / segment_len;
|
||||
|
||||
for (const int eval_index : IndexRange(offsets[i], offsets[i + 1] - offsets[i])) {
|
||||
const float factor = (lengths[eval_index - 1] - segment_start_len) * length_factor;
|
||||
mappings[eval_index] = i + factor;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (this->is_cyclic) {
|
||||
const float segment_start_len = lengths[offsets.last() - 1];
|
||||
const float segment_end_len = this->length();
|
||||
const float segment_len = segment_end_len - segment_start_len;
|
||||
const float length_factor = (segment_len == 0.0f) ? 0.0f : 1.0f / segment_len;
|
||||
|
||||
for (const int eval_index : IndexRange(offsets.last(), eval_size - offsets.last())) {
|
||||
const float factor = (lengths[eval_index - 1] - segment_start_len) * length_factor;
|
||||
mappings[eval_index] = size - 1 + factor;
|
||||
}
|
||||
mappings.last() = 0.0f;
|
||||
}
|
||||
else {
|
||||
mappings.last() = size - 1;
|
||||
}
|
||||
|
||||
this->mapping_cache_dirty_ = false;
|
||||
return this->evaluated_mapping_cache_;
|
||||
}
|
||||
|
||||
Span<float3> BezierSpline::evaluated_positions() const
|
||||
{
|
||||
if (!this->position_cache_dirty_) {
|
||||
return this->evaluated_position_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->position_cache_mutex_};
|
||||
if (!this->position_cache_dirty_) {
|
||||
return this->evaluated_position_cache_;
|
||||
}
|
||||
|
||||
const int eval_total = this->evaluated_points_size();
|
||||
this->evaluated_position_cache_.resize(eval_total);
|
||||
|
||||
MutableSpan<float3> positions = this->evaluated_position_cache_;
|
||||
|
||||
Span<int> offsets = this->control_point_offsets();
|
||||
BLI_assert(offsets.last() <= eval_total);
|
||||
|
||||
const int grain_size = std::max(512 / this->resolution_, 1);
|
||||
blender::parallel_for(IndexRange(this->size() - 1), grain_size, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
this->evaluate_bezier_segment(
|
||||
i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
|
||||
}
|
||||
});
|
||||
|
||||
const int i_last = this->size() - 1;
|
||||
if (this->is_cyclic) {
|
||||
this->evaluate_bezier_segment(i_last, 0, positions.slice(offsets.last(), this->resolution_));
|
||||
}
|
||||
else {
|
||||
/* Since evaulating the bezier segment doesn't add the final point,
|
||||
* it must be added manually in the non-cyclic case. */
|
||||
positions.last() = this->positions_.last();
|
||||
}
|
||||
|
||||
this->position_cache_dirty_ = false;
|
||||
return this->evaluated_position_cache_;
|
||||
}
|
||||
|
||||
BezierSpline::InterpolationData BezierSpline::interpolation_data_from_map(const float map) const
|
||||
{
|
||||
const int points_len = this->size();
|
||||
const int index = std::floor(map);
|
||||
if (index == points_len) {
|
||||
BLI_assert(this->is_cyclic);
|
||||
return InterpolationData{points_len - 1, 0, 1.0f};
|
||||
}
|
||||
if (index == points_len - 1) {
|
||||
return InterpolationData{points_len - 2, points_len - 1, 1.0f};
|
||||
}
|
||||
return InterpolationData{index, index + 1, map - index};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void interpolate_to_evaluated_points_impl(Span<float> mappings,
|
||||
const blender::VArray<T> &source_data,
|
||||
MutableSpan<T> result_data)
|
||||
{
|
||||
const int points_len = source_data.size();
|
||||
/* TODO: Use a set of functions mix2 in attribute_math instead of DefaultMixer. */
|
||||
blender::attribute_math::DefaultMixer<T> mixer(result_data);
|
||||
|
||||
for (const int i : result_data.index_range()) {
|
||||
const int index = std::floor(mappings[i]);
|
||||
const int next_index = (index == points_len - 1) ? 0 : index + 1;
|
||||
const float factor = mappings[i] - index;
|
||||
|
||||
const T &value = source_data[index];
|
||||
const T &next_value = source_data[next_index];
|
||||
|
||||
mixer.mix_in(i, value, 1.0f - factor);
|
||||
mixer.mix_in(i, next_value, factor);
|
||||
}
|
||||
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const
|
||||
{
|
||||
BLI_assert(source_data.size() == this->size());
|
||||
Span<float> mappings = this->evaluated_mappings();
|
||||
|
||||
blender::fn::GVArrayPtr new_varray;
|
||||
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(this->evaluated_points_size());
|
||||
interpolate_to_evaluated_points_impl<T>(mappings, source_data.typed<T>(), values);
|
||||
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
|
||||
std::move(values));
|
||||
}
|
||||
});
|
||||
|
||||
return new_varray;
|
||||
}
|
205
source/blender/blenkernel/intern/spline_group.cc
Normal file
205
source/blender/blenkernel/intern/spline_group.cc
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::float3;
|
||||
using blender::float4x4;
|
||||
using blender::Span;
|
||||
|
||||
SplineGroup *SplineGroup::copy()
|
||||
{
|
||||
SplineGroup *new_curve = new SplineGroup();
|
||||
|
||||
for (SplinePtr &spline : this->splines) {
|
||||
new_curve->splines.append(spline->copy());
|
||||
}
|
||||
|
||||
return new_curve;
|
||||
}
|
||||
|
||||
void SplineGroup::translate(const float3 translation)
|
||||
{
|
||||
for (SplinePtr &spline : this->splines) {
|
||||
for (float3 &position : spline->positions()) {
|
||||
position += translation;
|
||||
}
|
||||
if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
|
||||
for (float3 &handle_position : bezier_spline->handle_positions_start()) {
|
||||
handle_position += translation;
|
||||
}
|
||||
for (float3 &handle_position : bezier_spline->handle_positions_end()) {
|
||||
handle_position += translation;
|
||||
}
|
||||
}
|
||||
spline->mark_cache_invalid();
|
||||
}
|
||||
}
|
||||
|
||||
void SplineGroup::transform(const float4x4 &matrix)
|
||||
{
|
||||
for (SplinePtr &spline : this->splines) {
|
||||
for (float3 &position : spline->positions()) {
|
||||
position = matrix * position;
|
||||
}
|
||||
if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
|
||||
for (float3 &handle_position : bezier_spline->handle_positions_start()) {
|
||||
handle_position = matrix * handle_position;
|
||||
}
|
||||
for (float3 &handle_position : bezier_spline->handle_positions_end()) {
|
||||
handle_position = matrix * handle_position;
|
||||
}
|
||||
}
|
||||
spline->mark_cache_invalid();
|
||||
}
|
||||
}
|
||||
|
||||
void SplineGroup::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
|
||||
{
|
||||
for (const SplinePtr &spline : this->splines) {
|
||||
spline->bounds_min_max(min, max, use_evaluated);
|
||||
}
|
||||
}
|
||||
|
||||
static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
|
||||
{
|
||||
switch (dna_handle_type) {
|
||||
case HD_FREE:
|
||||
return BezierSpline::Free;
|
||||
case HD_AUTO:
|
||||
return BezierSpline::Auto;
|
||||
case HD_VECT:
|
||||
return BezierSpline::Vector;
|
||||
case HD_ALIGN:
|
||||
return BezierSpline::Align;
|
||||
case HD_AUTO_ANIM:
|
||||
return BezierSpline::Auto;
|
||||
case HD_ALIGN_DOUBLESIDE:
|
||||
return BezierSpline::Align;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return BezierSpline::Auto;
|
||||
}
|
||||
|
||||
static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
|
||||
{
|
||||
switch (twist_mode) {
|
||||
case CU_TWIST_Z_UP:
|
||||
return Spline::NormalCalculationMode::ZUp;
|
||||
case CU_TWIST_MINIMUM:
|
||||
return Spline::NormalCalculationMode::Minimum;
|
||||
case CU_TWIST_TANGENT:
|
||||
return Spline::NormalCalculationMode::Tangent;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return Spline::NormalCalculationMode::Minimum;
|
||||
}
|
||||
|
||||
static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
|
||||
{
|
||||
switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
|
||||
case CU_NURB_ENDPOINT:
|
||||
return NURBSpline::KnotsMode::EndPoint;
|
||||
case CU_NURB_BEZIER:
|
||||
return NURBSpline::KnotsMode::Bezier;
|
||||
default:
|
||||
return NURBSpline::KnotsMode::Normal;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return NURBSpline::KnotsMode::Normal;
|
||||
}
|
||||
|
||||
SplineGroup *dcurve_from_dna_curve(const Curve &dna_curve)
|
||||
{
|
||||
SplineGroup *curve = new SplineGroup();
|
||||
|
||||
const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
|
||||
|
||||
curve->splines.reserve(BLI_listbase_count(nurbs));
|
||||
|
||||
LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
|
||||
switch (nurb->type) {
|
||||
case CU_BEZIER: {
|
||||
std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
|
||||
spline->set_resolution(nurb->resolu);
|
||||
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
|
||||
|
||||
/* TODO: Optimize by reserving the correct size. */
|
||||
for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
|
||||
spline->add_point(bezt.vec[1],
|
||||
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
|
||||
bezt.vec[0],
|
||||
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
|
||||
bezt.vec[2],
|
||||
bezt.radius,
|
||||
bezt.tilt);
|
||||
}
|
||||
|
||||
curve->splines.append(std::move(spline));
|
||||
break;
|
||||
}
|
||||
case CU_NURBS: {
|
||||
std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
|
||||
spline->set_resolution(nurb->resolu);
|
||||
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
|
||||
spline->set_order(nurb->orderu);
|
||||
spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
|
||||
|
||||
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
|
||||
spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
|
||||
}
|
||||
|
||||
curve->splines.append(std::move(spline));
|
||||
break;
|
||||
}
|
||||
case CU_POLY: {
|
||||
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
|
||||
spline->is_cyclic = nurb->flagu & CU_NURB_CYCLIC;
|
||||
|
||||
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
|
||||
spline->add_point(bp.vec, bp.radius, bp.tilt);
|
||||
}
|
||||
|
||||
curve->splines.append(std::move(spline));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: Normal mode is stored separately in each spline to facilitate combining splines
|
||||
* from multiple curve objects, where the value may be different. */
|
||||
const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
|
||||
dna_curve.twist_mode);
|
||||
for (SplinePtr &spline : curve->splines) {
|
||||
spline->normal_mode = normal_mode;
|
||||
}
|
||||
|
||||
return curve;
|
||||
}
|
||||
|
||||
/** \} */
|
444
source/blender/blenkernel/intern/spline_nurbs.cc
Normal file
444
source/blender/blenkernel/intern/spline_nurbs.cc
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::Array;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
SplinePtr NURBSpline::copy() const
|
||||
{
|
||||
SplinePtr new_spline = std::make_unique<NURBSpline>(*this);
|
||||
|
||||
return new_spline;
|
||||
}
|
||||
|
||||
int NURBSpline::size() const
|
||||
{
|
||||
const int size = this->positions_.size();
|
||||
BLI_assert(this->radii_.size() == size);
|
||||
BLI_assert(this->tilts_.size() == size);
|
||||
BLI_assert(this->weights_.size() == size);
|
||||
return size;
|
||||
}
|
||||
|
||||
int NURBSpline::resolution() const
|
||||
{
|
||||
return this->resolution_;
|
||||
}
|
||||
|
||||
void NURBSpline::set_resolution(const int value)
|
||||
{
|
||||
this->resolution_ = value;
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
uint8_t NURBSpline::order() const
|
||||
{
|
||||
return this->order_;
|
||||
}
|
||||
|
||||
void NURBSpline::set_order(const uint8_t value)
|
||||
{
|
||||
BLI_assert(value >= 2 && value <= 6);
|
||||
this->order_ = value;
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void NURBSpline::add_point(const float3 position,
|
||||
const float radius,
|
||||
const float tilt,
|
||||
const float weight)
|
||||
{
|
||||
this->positions_.append(position);
|
||||
this->radii_.append(radius);
|
||||
this->tilts_.append(tilt);
|
||||
this->weights_.append(weight);
|
||||
this->knots_dirty_ = true;
|
||||
}
|
||||
|
||||
void NURBSpline::drop_front(const int count)
|
||||
{
|
||||
BLI_assert(this->size() - count > 0);
|
||||
this->positions_.remove(0, count);
|
||||
this->radii_.remove(0, count);
|
||||
this->tilts_.remove(0, count);
|
||||
this->weights_.remove(0, count);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void NURBSpline::drop_back(const int count)
|
||||
{
|
||||
const int new_size = this->size() - count;
|
||||
BLI_assert(new_size > 0);
|
||||
this->positions_.resize(new_size);
|
||||
this->radii_.resize(new_size);
|
||||
this->tilts_.resize(new_size);
|
||||
this->weights_.resize(new_size);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
MutableSpan<float3> NURBSpline::positions()
|
||||
{
|
||||
return this->positions_;
|
||||
}
|
||||
Span<float3> NURBSpline::positions() const
|
||||
{
|
||||
return this->positions_;
|
||||
}
|
||||
MutableSpan<float> NURBSpline::radii()
|
||||
{
|
||||
return this->radii_;
|
||||
}
|
||||
Span<float> NURBSpline::radii() const
|
||||
{
|
||||
return this->radii_;
|
||||
}
|
||||
MutableSpan<float> NURBSpline::tilts()
|
||||
{
|
||||
return this->tilts_;
|
||||
}
|
||||
Span<float> NURBSpline::tilts() const
|
||||
{
|
||||
return this->tilts_;
|
||||
}
|
||||
MutableSpan<float> NURBSpline::weights()
|
||||
{
|
||||
return this->weights_;
|
||||
}
|
||||
Span<float> NURBSpline::weights() const
|
||||
{
|
||||
return this->weights_;
|
||||
}
|
||||
|
||||
void NURBSpline::mark_cache_invalid()
|
||||
{
|
||||
this->basis_cache_dirty_ = true;
|
||||
this->position_cache_dirty_ = true;
|
||||
this->tangent_cache_dirty_ = true;
|
||||
this->normal_cache_dirty_ = true;
|
||||
this->length_cache_dirty_ = true;
|
||||
}
|
||||
|
||||
int NURBSpline::evaluated_points_size() const
|
||||
{
|
||||
return this->resolution_ * this->segments_size();
|
||||
}
|
||||
|
||||
void NURBSpline::correct_end_tangents() const
|
||||
{
|
||||
}
|
||||
|
||||
bool NURBSpline::check_valid_size_and_order() const
|
||||
{
|
||||
if (this->size() < this->order_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->is_cyclic && this->knots_mode == KnotsMode::Bezier) {
|
||||
if (this->order_ == 4) {
|
||||
if (this->size() < 5) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (this->order_ != 3) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int NURBSpline::knots_size() const
|
||||
{
|
||||
const int size = this->size() + this->order_;
|
||||
return this->is_cyclic ? size + this->order_ - 1 : size;
|
||||
}
|
||||
|
||||
void NURBSpline::calculate_knots() const
|
||||
{
|
||||
const KnotsMode mode = this->knots_mode;
|
||||
const int length = this->size();
|
||||
const int order = this->order_;
|
||||
|
||||
this->knots_.resize(this->knots_size());
|
||||
|
||||
MutableSpan<float> knots = this->knots_;
|
||||
|
||||
if (mode == NURBSpline::KnotsMode::Normal || this->is_cyclic) {
|
||||
for (const int i : knots.index_range()) {
|
||||
knots[i] = static_cast<float>(i);
|
||||
}
|
||||
}
|
||||
else if (mode == NURBSpline::KnotsMode::EndPoint) {
|
||||
float k = 0.0f;
|
||||
for (const int i : IndexRange(1, knots.size())) {
|
||||
knots[i - 1] = k;
|
||||
if (i >= order && i <= length) {
|
||||
k += 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mode == NURBSpline::KnotsMode::Bezier) {
|
||||
BLI_assert(ELEM(order, 3, 4));
|
||||
if (order == 3) {
|
||||
float k = 0.6f;
|
||||
for (const int i : knots.index_range()) {
|
||||
if (i >= order && i <= length) {
|
||||
k += 0.5f;
|
||||
}
|
||||
knots[i] = std::floor(k);
|
||||
}
|
||||
}
|
||||
else {
|
||||
float k = 0.34f;
|
||||
for (const int i : knots.index_range()) {
|
||||
knots[i] = std::floor(k);
|
||||
k += 1.0f / 3.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->is_cyclic) {
|
||||
const int b = length + order - 1;
|
||||
if (order > 2) {
|
||||
for (const int i : IndexRange(1, order - 2)) {
|
||||
if (knots[b] != knots[b - i]) {
|
||||
if (i == order - 1) {
|
||||
knots[length + order - 2] += 1.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int c = order;
|
||||
for (int i = b; i < this->knots_size(); i++) {
|
||||
knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
|
||||
c--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Span<float> NURBSpline::knots() const
|
||||
{
|
||||
if (!this->knots_dirty_) {
|
||||
BLI_assert(this->knots_.size() == this->size() + this->order_);
|
||||
return this->knots_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->knots_mutex_};
|
||||
if (!this->knots_dirty_) {
|
||||
BLI_assert(this->knots_.size() == this->size() + this->order_);
|
||||
return this->knots_;
|
||||
}
|
||||
|
||||
this->calculate_knots();
|
||||
|
||||
this->knots_dirty_ = false;
|
||||
|
||||
return this->knots_;
|
||||
}
|
||||
|
||||
static void calculate_basis_for_point(const float parameter,
|
||||
const int points_len,
|
||||
const int order,
|
||||
Span<float> knots,
|
||||
MutableSpan<float> basis_buffer,
|
||||
NURBSpline::BasisCache &basis_cache)
|
||||
{
|
||||
/* Clamp parameter due to floating point inaccuracy. TODO: Look into using doubles. */
|
||||
const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (const int i : IndexRange(points_len + order - 1)) {
|
||||
const bool knots_equal = knots[i] == knots[i + 1];
|
||||
if (knots_equal || t < knots[i] || t > knots[i + 1]) {
|
||||
basis_buffer[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
basis_buffer[i] = 1.0f;
|
||||
start = std::max(i - order - 1, 0);
|
||||
end = i;
|
||||
basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
|
||||
break;
|
||||
}
|
||||
basis_buffer[points_len + order - 1] = 0.0f;
|
||||
|
||||
for (const int i_order : IndexRange(2, order - 1)) {
|
||||
if (end + i_order >= points_len + order) {
|
||||
end = points_len + order - 1 - i_order;
|
||||
}
|
||||
for (const int i : IndexRange(start, end - start + 1)) {
|
||||
float new_basis = 0.0f;
|
||||
if (basis_buffer[i] != 0.0f) {
|
||||
new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
|
||||
}
|
||||
|
||||
if (basis_buffer[i + 1] != 0.0f) {
|
||||
new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
|
||||
(knots[i + i_order] - knots[i + 1]);
|
||||
}
|
||||
|
||||
basis_buffer[i] = new_basis;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shrink the range of calculated values to avoid storing unecessary zeros. */
|
||||
while (basis_buffer[start] == 0.0f && start < end) {
|
||||
start++;
|
||||
}
|
||||
while (basis_buffer[end] == 0.0f && end > start) {
|
||||
end--;
|
||||
}
|
||||
|
||||
basis_cache.weights.clear();
|
||||
basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
|
||||
basis_cache.start_index = start;
|
||||
}
|
||||
|
||||
void NURBSpline::calculate_basis_cache() const
|
||||
{
|
||||
if (!this->basis_cache_dirty_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->basis_cache_mutex_};
|
||||
if (!this->basis_cache_dirty_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int points_len = this->size();
|
||||
const int evaluated_len = this->evaluated_points_size();
|
||||
this->basis_cache_.resize(evaluated_len);
|
||||
|
||||
const int order = this->order();
|
||||
Span<float> control_weights = this->weights();
|
||||
Span<float> knots = this->knots();
|
||||
|
||||
MutableSpan<BasisCache> basis_cache(this->basis_cache_);
|
||||
|
||||
/* This buffer is reused by each basis calculation to store temporary values.
|
||||
* Theoretically it could be optimized away in the future. */
|
||||
Array<float> basis_buffer(this->knots_size());
|
||||
|
||||
const float start = knots[order - 1];
|
||||
const float end = this->is_cyclic ? knots[points_len + order - 1] : knots[points_len];
|
||||
const float step = (end - start) / (evaluated_len - (this->is_cyclic ? 0 : 1));
|
||||
float parameter = start;
|
||||
for (const int i : IndexRange(evaluated_len)) {
|
||||
BasisCache &basis = basis_cache[i];
|
||||
calculate_basis_for_point(parameter,
|
||||
points_len + (this->is_cyclic ? order - 1 : 0),
|
||||
order,
|
||||
knots,
|
||||
basis_buffer,
|
||||
basis);
|
||||
BLI_assert(basis.weights.size() <= order);
|
||||
|
||||
for (const int j : basis.weights.index_range()) {
|
||||
const int point_index = (basis.start_index + j) % points_len;
|
||||
basis.weights[j] *= control_weights[point_index];
|
||||
}
|
||||
|
||||
parameter += step;
|
||||
}
|
||||
|
||||
this->basis_cache_dirty_ = false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
|
||||
const blender::VArray<T> &source_data,
|
||||
MutableSpan<T> result_data)
|
||||
{
|
||||
const int points_len = source_data.size();
|
||||
BLI_assert(result_data.size() == weights.size());
|
||||
blender::attribute_math::DefaultMixer<T> mixer(result_data);
|
||||
|
||||
for (const int i : result_data.index_range()) {
|
||||
Span<float> point_weights = weights[i].weights;
|
||||
const int start_index = weights[i].start_index;
|
||||
|
||||
for (const int j : point_weights.index_range()) {
|
||||
const int point_index = (start_index + j) % points_len;
|
||||
mixer.mix_in(i, source_data[point_index], point_weights[j]);
|
||||
}
|
||||
}
|
||||
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const
|
||||
{
|
||||
BLI_assert(source_data.size() == this->size());
|
||||
|
||||
this->calculate_basis_cache();
|
||||
Span<BasisCache> weights(this->basis_cache_);
|
||||
|
||||
blender::fn::GVArrayPtr new_varray;
|
||||
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(this->evaluated_points_size());
|
||||
interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
|
||||
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
|
||||
std::move(values));
|
||||
}
|
||||
});
|
||||
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
Span<float3> NURBSpline::evaluated_positions() const
|
||||
{
|
||||
if (!this->position_cache_dirty_) {
|
||||
return this->evaluated_position_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{this->position_cache_mutex_};
|
||||
if (!this->position_cache_dirty_) {
|
||||
return this->evaluated_position_cache_;
|
||||
}
|
||||
|
||||
const int total = this->evaluated_points_size();
|
||||
this->evaluated_position_cache_.resize(total);
|
||||
|
||||
blender::fn::GVArray_For_Span<float3> positions_varray(this->positions_.as_span());
|
||||
blender::fn::GVArrayPtr evaluated_positions_varray = this->interpolate_to_evaluated_points(
|
||||
positions_varray);
|
||||
|
||||
/* TODO: Avoid copying. */
|
||||
Span<float3> evaluated_positions =
|
||||
evaluated_positions_varray->typed<float3>()->get_internal_span();
|
||||
for (const int i : IndexRange(total)) {
|
||||
this->evaluated_position_cache_[i] = evaluated_positions[i];
|
||||
}
|
||||
|
||||
this->position_cache_dirty_ = false;
|
||||
return this->evaluated_position_cache_;
|
||||
}
|
144
source/blender/blenkernel/intern/spline_poly.cc
Normal file
144
source/blender/blenkernel/intern/spline_poly.cc
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::float3;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
SplinePtr PolySpline::copy() const
|
||||
{
|
||||
SplinePtr new_spline = std::make_unique<PolySpline>(*this);
|
||||
|
||||
return new_spline;
|
||||
}
|
||||
|
||||
int PolySpline::size() const
|
||||
{
|
||||
const int size = this->positions_.size();
|
||||
BLI_assert(this->radii_.size() == size);
|
||||
BLI_assert(this->tilts_.size() == size);
|
||||
return size;
|
||||
}
|
||||
|
||||
int PolySpline::resolution() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PolySpline::set_resolution(const int UNUSED(value))
|
||||
{
|
||||
/* Poly curve has no resolution, there is just one evaluated point per control point. */
|
||||
}
|
||||
|
||||
void PolySpline::add_point(const float3 position, const float radius, const float tilt)
|
||||
{
|
||||
this->positions_.append(position);
|
||||
this->radii_.append(radius);
|
||||
this->tilts_.append(tilt);
|
||||
}
|
||||
|
||||
void PolySpline::drop_front(const int count)
|
||||
{
|
||||
BLI_assert(this->size() - count > 0);
|
||||
this->positions_.remove(0, count);
|
||||
this->radii_.remove(0, count);
|
||||
this->tilts_.remove(0, count);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void PolySpline::drop_back(const int count)
|
||||
{
|
||||
const int new_size = this->size() - count;
|
||||
BLI_assert(new_size > 0);
|
||||
this->positions_.resize(new_size);
|
||||
this->radii_.resize(new_size);
|
||||
this->tilts_.resize(new_size);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
MutableSpan<float3> PolySpline::positions()
|
||||
{
|
||||
return this->positions_;
|
||||
}
|
||||
Span<float3> PolySpline::positions() const
|
||||
{
|
||||
return this->positions_;
|
||||
}
|
||||
MutableSpan<float> PolySpline::radii()
|
||||
{
|
||||
return this->radii_;
|
||||
}
|
||||
Span<float> PolySpline::radii() const
|
||||
{
|
||||
return this->radii_;
|
||||
}
|
||||
MutableSpan<float> PolySpline::tilts()
|
||||
{
|
||||
return this->tilts_;
|
||||
}
|
||||
Span<float> PolySpline::tilts() const
|
||||
{
|
||||
return this->tilts_;
|
||||
}
|
||||
|
||||
void PolySpline::mark_cache_invalid()
|
||||
{
|
||||
this->tangent_cache_dirty_ = true;
|
||||
this->normal_cache_dirty_ = true;
|
||||
this->length_cache_dirty_ = true;
|
||||
}
|
||||
|
||||
int PolySpline::evaluated_points_size() const
|
||||
{
|
||||
return this->size();
|
||||
}
|
||||
|
||||
void PolySpline::correct_end_tangents() const
|
||||
{
|
||||
}
|
||||
|
||||
Span<float3> PolySpline::evaluated_positions() const
|
||||
{
|
||||
return this->positions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Poly spline interpolation from control points to evaluated points is a special case, since the
|
||||
* result data is the same as the input data.
|
||||
*/
|
||||
blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const
|
||||
{
|
||||
BLI_assert(source_data.size() == this->size());
|
||||
|
||||
/* TODO: Must make sure this ownership idea works. */
|
||||
if (source_data.is_span()) {
|
||||
return std::make_unique<blender::fn::GVArray_For_GSpan>(source_data.get_internal_span());
|
||||
}
|
||||
// if (source_data.is_single()) {
|
||||
// BUFFER_FOR_CPP_TYPE_VALUE(source_data.type(), value);
|
||||
// source_data.get_internal_single(value);
|
||||
// return std::make_unique<blender::fn::GVArray_For_SingleValue>(
|
||||
// source_data.type(), source_data.size(), value);
|
||||
// }
|
||||
|
||||
return {};
|
||||
}
|
@@ -39,6 +39,7 @@
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
@@ -1003,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr
|
||||
#endif
|
||||
}
|
||||
|
||||
static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
Object *object,
|
||||
Volume *volume_input)
|
||||
static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
Object *object,
|
||||
GeometrySet &geometry_set)
|
||||
{
|
||||
Volume *volume = volume_input;
|
||||
|
||||
/* Modifier evaluation modes. */
|
||||
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
|
||||
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
|
||||
@@ -1030,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mti->modifyVolume) {
|
||||
/* Ensure we are not modifying the input. */
|
||||
if (volume == volume_input) {
|
||||
volume = BKE_volume_copy_for_eval(volume, true);
|
||||
}
|
||||
|
||||
Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
|
||||
|
||||
if (volume_next && volume_next != volume) {
|
||||
/* If the modifier returned a new volume, release the old one. */
|
||||
if (volume != volume_input) {
|
||||
BKE_id_free(nullptr, volume);
|
||||
}
|
||||
volume = volume_next;
|
||||
}
|
||||
if (mti->modifyGeometrySet) {
|
||||
mti->modifyGeometrySet(md, &mectx, &geometry_set);
|
||||
}
|
||||
}
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
|
||||
@@ -1072,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
|
||||
}
|
||||
}
|
||||
|
||||
static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
|
||||
{
|
||||
if (!geometry_set.has<VolumeComponent>()) {
|
||||
return nullptr;
|
||||
}
|
||||
VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
|
||||
Volume *volume = volume_component.release();
|
||||
if (volume != nullptr) {
|
||||
/* Add back, but only as read-only non-owning component. */
|
||||
volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
|
||||
}
|
||||
else {
|
||||
/* The component was empty, we can remove it. */
|
||||
geometry_set.remove<VolumeComponent>();
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
|
||||
{
|
||||
/* Free any evaluated data and restore original data. */
|
||||
@@ -1079,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
|
||||
|
||||
/* Evaluate modifiers. */
|
||||
Volume *volume = (Volume *)object->data;
|
||||
Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
|
||||
GeometrySet geometry_set;
|
||||
geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly);
|
||||
volume_evaluate_modifiers(depsgraph, scene, object, geometry_set);
|
||||
|
||||
Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
|
||||
|
||||
/* If the geometry set did not contain a volume, we still create an empty one. */
|
||||
if (volume_eval == nullptr) {
|
||||
volume_eval = BKE_volume_new_for_eval(volume);
|
||||
}
|
||||
|
||||
/* Assign evaluated object. */
|
||||
const bool is_owned = (volume != volume_eval);
|
||||
BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
|
||||
const bool eval_is_owned = (volume != volume_eval);
|
||||
BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
|
||||
object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
|
||||
}
|
||||
|
||||
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
|
||||
|
@@ -98,3 +98,10 @@
|
||||
#else
|
||||
# define ATTR_ALIGN(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
|
||||
/* Alignment directive */
|
||||
#ifdef _WIN64
|
||||
# define ALIGN_STRUCT __declspec(align(64))
|
||||
#else
|
||||
# define ALIGN_STRUCT
|
||||
#endif
|
||||
|
@@ -245,6 +245,13 @@ struct float3 {
|
||||
return result;
|
||||
}
|
||||
|
||||
static float3 cross(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
cross_v3_v3v3(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float3 project(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
|
@@ -45,6 +45,37 @@ struct float4x4 {
|
||||
return mat;
|
||||
}
|
||||
|
||||
static float4x4 from_normalized_axis_data(const float3 location,
|
||||
const float3 forward,
|
||||
const float3 up)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(forward);
|
||||
BLI_ASSERT_UNIT_V3(up);
|
||||
float4x4 matrix;
|
||||
const float3 cross = float3::cross(forward, up);
|
||||
matrix.values[0][0] = forward.x;
|
||||
matrix.values[1][0] = cross.x;
|
||||
matrix.values[2][0] = up.x;
|
||||
matrix.values[3][0] = location.x;
|
||||
|
||||
matrix.values[0][1] = forward.y;
|
||||
matrix.values[1][1] = cross.y;
|
||||
matrix.values[2][1] = up.y;
|
||||
matrix.values[3][1] = location.y;
|
||||
|
||||
matrix.values[0][2] = forward.z;
|
||||
matrix.values[1][2] = cross.z;
|
||||
matrix.values[2][2] = up.z;
|
||||
matrix.values[3][2] = location.z;
|
||||
|
||||
matrix.values[0][3] = 0.0f;
|
||||
matrix.values[1][3] = 0.0f;
|
||||
matrix.values[2][3] = 0.0f;
|
||||
matrix.values[3][3] = 1.0f;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
static float4x4 identity()
|
||||
{
|
||||
float4x4 mat;
|
||||
@@ -116,6 +147,19 @@ struct float4x4 {
|
||||
return scale;
|
||||
}
|
||||
|
||||
void apply_scale(const float scale)
|
||||
{
|
||||
values[0][0] *= scale;
|
||||
values[0][1] *= scale;
|
||||
values[0][2] *= scale;
|
||||
values[1][0] *= scale;
|
||||
values[1][1] *= scale;
|
||||
values[1][2] *= scale;
|
||||
values[2][0] *= scale;
|
||||
values[2][1] *= scale;
|
||||
values[2][2] *= scale;
|
||||
}
|
||||
|
||||
float4x4 inverted() const
|
||||
{
|
||||
float4x4 result;
|
||||
|
@@ -247,11 +247,11 @@ class Map {
|
||||
{
|
||||
this->add_new_as(std::move(key), std::move(value));
|
||||
}
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
void add_new_as(ForwardKey &&key, ForwardValue &&value)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
void add_new_as(ForwardKey &&key, ForwardValue &&... value)
|
||||
{
|
||||
this->add_new__impl(
|
||||
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
|
||||
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,11 +277,11 @@ class Map {
|
||||
{
|
||||
return this->add_as(std::move(key), std::move(value));
|
||||
}
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
bool add_as(ForwardKey &&key, ForwardValue &&value)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
bool add_as(ForwardKey &&key, ForwardValue &&... value)
|
||||
{
|
||||
return this->add__impl(
|
||||
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
|
||||
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,11 +307,11 @@ class Map {
|
||||
{
|
||||
return this->add_overwrite_as(std::move(key), std::move(value));
|
||||
}
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value)
|
||||
{
|
||||
return this->add_overwrite__impl(
|
||||
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
|
||||
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,12 +413,12 @@ class Map {
|
||||
{
|
||||
return this->pop_default_as(key, std::move(default_value));
|
||||
}
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value)
|
||||
{
|
||||
Slot *slot = this->lookup_slot_ptr(key, hash_(key));
|
||||
if (slot == nullptr) {
|
||||
return std::forward<ForwardValue>(default_value);
|
||||
return Value(std::forward<ForwardValue>(default_value)...);
|
||||
}
|
||||
Value value = std::move(*slot->value());
|
||||
slot->remove();
|
||||
@@ -525,15 +525,15 @@ class Map {
|
||||
{
|
||||
return this->lookup_default_as(key, default_value);
|
||||
}
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const
|
||||
{
|
||||
const Value *ptr = this->lookup_ptr_as(key);
|
||||
if (ptr != nullptr) {
|
||||
return *ptr;
|
||||
}
|
||||
else {
|
||||
return std::forward<ForwardValue>(default_value);
|
||||
return Value(std::forward<ForwardValue>(default_value)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,11 +557,11 @@ class Map {
|
||||
{
|
||||
return this->lookup_or_add_as(std::move(key), std::move(value));
|
||||
}
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value)
|
||||
{
|
||||
return this->lookup_or_add__impl(
|
||||
std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
|
||||
std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -982,7 +982,7 @@ class Map {
|
||||
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
|
||||
Slot &slot = new_slots[slot_index];
|
||||
if (slot.is_empty()) {
|
||||
slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash);
|
||||
slot.occupy(std::move(*old_slot.key()), hash, std::move(*old_slot.value()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -996,8 +996,8 @@ class Map {
|
||||
new (this) Map(NoExceptConstructor(), allocator);
|
||||
}
|
||||
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
|
||||
{
|
||||
BLI_assert(!this->contains_as(key));
|
||||
|
||||
@@ -1005,7 +1005,7 @@ class Map {
|
||||
|
||||
MAP_SLOT_PROBING_BEGIN (hash, slot) {
|
||||
if (slot.is_empty()) {
|
||||
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
|
||||
slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
|
||||
occupied_and_removed_slots_++;
|
||||
return;
|
||||
}
|
||||
@@ -1013,14 +1013,14 @@ class Map {
|
||||
MAP_SLOT_PROBING_END();
|
||||
}
|
||||
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
|
||||
{
|
||||
this->ensure_can_add();
|
||||
|
||||
MAP_SLOT_PROBING_BEGIN (hash, slot) {
|
||||
if (slot.is_empty()) {
|
||||
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
|
||||
slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
|
||||
occupied_and_removed_slots_++;
|
||||
return true;
|
||||
}
|
||||
@@ -1075,7 +1075,7 @@ class Map {
|
||||
|
||||
MAP_SLOT_PROBING_BEGIN (hash, slot) {
|
||||
if (slot.is_empty()) {
|
||||
slot.occupy(std::forward<ForwardKey>(key), create_value(), hash);
|
||||
slot.occupy(std::forward<ForwardKey>(key), hash, create_value());
|
||||
occupied_and_removed_slots_++;
|
||||
return *slot.value();
|
||||
}
|
||||
@@ -1086,14 +1086,14 @@ class Map {
|
||||
MAP_SLOT_PROBING_END();
|
||||
}
|
||||
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
|
||||
{
|
||||
this->ensure_can_add();
|
||||
|
||||
MAP_SLOT_PROBING_BEGIN (hash, slot) {
|
||||
if (slot.is_empty()) {
|
||||
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
|
||||
slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
|
||||
occupied_and_removed_slots_++;
|
||||
return *slot.value();
|
||||
}
|
||||
@@ -1104,15 +1104,15 @@ class Map {
|
||||
MAP_SLOT_PROBING_END();
|
||||
}
|
||||
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
|
||||
{
|
||||
auto create_func = [&](Value *ptr) {
|
||||
new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value));
|
||||
new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...);
|
||||
return true;
|
||||
};
|
||||
auto modify_func = [&](Value *ptr) {
|
||||
*ptr = std::forward<ForwardValue>(value);
|
||||
*ptr = Value(std::forward<ForwardValue>(value)...);
|
||||
return false;
|
||||
};
|
||||
return this->add_or_modify__impl(
|
||||
@@ -1221,16 +1221,18 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper {
|
||||
map_.reserve(n);
|
||||
}
|
||||
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
void add_new(ForwardKey &&key, ForwardValue &&value)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
void add_new(ForwardKey &&key, ForwardValue &&... value)
|
||||
{
|
||||
map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)});
|
||||
map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)});
|
||||
}
|
||||
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
bool add(ForwardKey &&key, ForwardValue &&value)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
bool add(ForwardKey &&key, ForwardValue &&... value)
|
||||
{
|
||||
return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second;
|
||||
return map_
|
||||
.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)})
|
||||
.second;
|
||||
}
|
||||
|
||||
bool contains(const Key &key) const
|
||||
|
@@ -195,11 +195,11 @@ template<typename Key, typename Value> class SimpleMapSlot {
|
||||
* Change the state of this slot from empty/removed to occupied. The key/value has to be
|
||||
* constructed by calling the constructor with the given key/value as parameter.
|
||||
*/
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
|
||||
{
|
||||
BLI_assert(!this->is_occupied());
|
||||
new (&value_buffer_) Value(std::forward<ForwardValue>(value));
|
||||
new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
|
||||
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
|
||||
state_ = Occupied;
|
||||
}
|
||||
@@ -315,12 +315,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
|
||||
return is_equal(key, key_);
|
||||
}
|
||||
|
||||
template<typename ForwardKey, typename ForwardValue>
|
||||
void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
|
||||
template<typename ForwardKey, typename... ForwardValue>
|
||||
void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
|
||||
{
|
||||
BLI_assert(!this->is_occupied());
|
||||
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
|
||||
new (&value_buffer_) Value(std::forward<ForwardValue>(value));
|
||||
new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
|
||||
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
|
||||
}
|
||||
|
||||
|
@@ -94,7 +94,7 @@ template<typename T> class Span {
|
||||
using iterator = const T *;
|
||||
using size_type = int64_t;
|
||||
|
||||
private:
|
||||
protected:
|
||||
const T *data_ = nullptr;
|
||||
int64_t size_ = 0;
|
||||
|
||||
@@ -477,7 +477,7 @@ template<typename T> class MutableSpan {
|
||||
using iterator = T *;
|
||||
using size_type = int64_t;
|
||||
|
||||
private:
|
||||
protected:
|
||||
T *data_;
|
||||
int64_t size_;
|
||||
|
||||
@@ -661,6 +661,16 @@ template<typename T> class MutableSpan {
|
||||
return IndexRange(size_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to the first element in the array. This invokes undefined behavior when the
|
||||
* array is empty.
|
||||
*/
|
||||
constexpr T &first() const
|
||||
{
|
||||
BLI_assert(size_ > 0);
|
||||
return data_[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the last element. This invokes undefined behavior when the array is
|
||||
* empty.
|
||||
|
@@ -232,13 +232,14 @@ class Stack {
|
||||
{
|
||||
this->push_as(std::move(value));
|
||||
}
|
||||
template<typename ForwardT> void push_as(ForwardT &&value)
|
||||
/* This is similar to `std::stack::emplace`. */
|
||||
template<typename... ForwardT> void push_as(ForwardT &&... value)
|
||||
{
|
||||
if (top_ == top_chunk_->capacity_end) {
|
||||
this->activate_next_chunk(1);
|
||||
}
|
||||
try {
|
||||
new (top_) T(std::forward<ForwardT>(value));
|
||||
new (top_) T(std::forward<ForwardT>(value)...);
|
||||
top_++;
|
||||
size_++;
|
||||
}
|
||||
|
@@ -68,6 +68,11 @@ char *BLI_str_replaceN(const char *__restrict str,
|
||||
|
||||
void BLI_str_replace_char(char *string, char src, char dst) ATTR_NONNULL();
|
||||
|
||||
bool BLI_str_replace_table_exact(char *string,
|
||||
const size_t string_len,
|
||||
const char *replace_table[][2],
|
||||
int replace_table_len);
|
||||
|
||||
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
|
||||
ATTR_NONNULL(1, 3) ATTR_PRINTF_FORMAT(3, 4);
|
||||
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
|
||||
|
@@ -437,13 +437,17 @@ class Vector {
|
||||
*/
|
||||
void append(const T &value)
|
||||
{
|
||||
this->ensure_space_for_one();
|
||||
this->append_unchecked(value);
|
||||
this->append_as(value);
|
||||
}
|
||||
void append(T &&value)
|
||||
{
|
||||
this->append_as(std::move(value));
|
||||
}
|
||||
/* This is similar to `std::vector::emplace_back`. */
|
||||
template<typename... ForwardValue> void append_as(ForwardValue &&...value)
|
||||
{
|
||||
this->ensure_space_for_one();
|
||||
this->append_unchecked(std::move(value));
|
||||
this->append_unchecked_as(std::forward<ForwardValue>(value)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -474,10 +478,18 @@ class Vector {
|
||||
* behavior when not enough capacity has been reserved beforehand. Only use this in performance
|
||||
* critical code.
|
||||
*/
|
||||
template<typename ForwardT> void append_unchecked(ForwardT &&value)
|
||||
void append_unchecked(const T &value)
|
||||
{
|
||||
this->append_unchecked_as(value);
|
||||
}
|
||||
void append_unchecked(T &&value)
|
||||
{
|
||||
this->append_unchecked_as(std::move(value));
|
||||
}
|
||||
template<typename... ForwardT> void append_unchecked_as(ForwardT &&...value)
|
||||
{
|
||||
BLI_assert(end_ < capacity_end_);
|
||||
new (end_) T(std::forward<ForwardT>(value));
|
||||
new (end_) T(std::forward<ForwardT>(value)...);
|
||||
end_++;
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
}
|
||||
@@ -656,6 +668,21 @@ class Vector {
|
||||
return *(end_ - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to the first element in the vector.
|
||||
* This invokes undefined behavior when the vector is empty.
|
||||
*/
|
||||
const T &first() const
|
||||
{
|
||||
BLI_assert(this->size() > 0);
|
||||
return *begin_;
|
||||
}
|
||||
T &first()
|
||||
{
|
||||
BLI_assert(this->size() > 0);
|
||||
return *begin_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how many values are currently stored in the vector.
|
||||
*/
|
||||
|
@@ -37,6 +37,7 @@
|
||||
* see of the increased compile time and binary size is worth it.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
@@ -71,6 +72,11 @@ template<typename T> class VArray {
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
IndexRange index_range() const
|
||||
{
|
||||
return IndexRange(size_);
|
||||
}
|
||||
|
||||
/* Returns true when the virtual array is stored as a span internally. */
|
||||
bool is_span() const
|
||||
{
|
||||
@@ -82,13 +88,13 @@ template<typename T> class VArray {
|
||||
|
||||
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
|
||||
* virtual array is not stored as a span internally. */
|
||||
Span<T> get_span() const
|
||||
Span<T> get_internal_span() const
|
||||
{
|
||||
BLI_assert(this->is_span());
|
||||
if (size_ == 0) {
|
||||
return {};
|
||||
}
|
||||
return this->get_span_impl();
|
||||
return this->get_internal_span_impl();
|
||||
}
|
||||
|
||||
/* Returns true when the virtual array returns the same value for every index. */
|
||||
@@ -102,20 +108,35 @@ template<typename T> class VArray {
|
||||
|
||||
/* Returns the value that is returned for every index. This invokes undefined behavior if the
|
||||
* virtual array would not return the same value for every index. */
|
||||
T get_single() const
|
||||
T get_internal_single() const
|
||||
{
|
||||
BLI_assert(this->is_single());
|
||||
if (size_ == 1) {
|
||||
return this->get(0);
|
||||
}
|
||||
return this->get_single_impl();
|
||||
return this->get_internal_single_impl();
|
||||
}
|
||||
|
||||
/* Get the element at a specific index. Note that this operator cannot be used to assign values
|
||||
* to an index, because the return value is not a reference. */
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
return this->get(index);
|
||||
}
|
||||
|
||||
/* Copy the entire virtual array into a span. */
|
||||
void materialize(MutableSpan<T> r_span) const
|
||||
{
|
||||
BLI_assert(size_ == r_span.size());
|
||||
this->materialize_impl(r_span);
|
||||
}
|
||||
|
||||
void materialize_to_uninitialized(MutableSpan<T> r_span) const
|
||||
{
|
||||
BLI_assert(size_ == r_span.size());
|
||||
this->materialize_to_uninitialized_impl(r_span);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual T get_impl(const int64_t index) const = 0;
|
||||
|
||||
@@ -124,7 +145,7 @@ template<typename T> class VArray {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual Span<T> get_span_impl() const
|
||||
virtual Span<T> get_internal_span_impl() const
|
||||
{
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
@@ -135,56 +156,201 @@ template<typename T> class VArray {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual T get_single_impl() const
|
||||
virtual T get_internal_single_impl() const
|
||||
{
|
||||
/* Provide a default implementation, so that subclasses don't have to provide it. This method
|
||||
* should never be called because `is_single_impl` returns false by default. */
|
||||
BLI_assert_unreachable();
|
||||
return T();
|
||||
}
|
||||
|
||||
virtual void materialize_impl(MutableSpan<T> r_span) const
|
||||
{
|
||||
if (this->is_span()) {
|
||||
const Span<T> span = this->get_internal_span();
|
||||
initialized_copy_n(span.data(), size_, r_span.data());
|
||||
}
|
||||
else if (this->is_single()) {
|
||||
const T single = this->get_internal_single();
|
||||
initialized_fill_n(r_span.data(), size_, single);
|
||||
}
|
||||
else {
|
||||
const int64_t size = size_;
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
r_span[i] = this->get(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void materialize_to_uninitialized_impl(MutableSpan<T> r_span) const
|
||||
{
|
||||
if (this->is_span()) {
|
||||
const Span<T> span = this->get_internal_span();
|
||||
uninitialized_copy_n(span.data(), size_, r_span.data());
|
||||
}
|
||||
else if (this->is_single()) {
|
||||
const T single = this->get_internal_single();
|
||||
uninitialized_fill_n(r_span.data(), size_, single);
|
||||
}
|
||||
else {
|
||||
const int64_t size = size_;
|
||||
T *dst = r_span.data();
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
new (dst + i) T(this->get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Similar to VArray, but the elements are mutable. */
|
||||
template<typename T> class VMutableArray : public VArray<T> {
|
||||
public:
|
||||
VMutableArray(const int64_t size) : VArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
void set(const int64_t index, T value)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size_);
|
||||
this->set_impl(index, std::move(value));
|
||||
}
|
||||
|
||||
/* Copy the values from the source span to all elements in the virtual array. */
|
||||
void set_all(Span<T> src)
|
||||
{
|
||||
BLI_assert(src.size() == this->size_);
|
||||
this->set_all_impl(src);
|
||||
}
|
||||
|
||||
MutableSpan<T> get_internal_span()
|
||||
{
|
||||
BLI_assert(this->is_span());
|
||||
Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span();
|
||||
return MutableSpan<T>(const_cast<T *>(span.data()), span.size());
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_impl(const int64_t index, T value) = 0;
|
||||
|
||||
virtual void set_all_impl(Span<T> src)
|
||||
{
|
||||
if (this->is_span()) {
|
||||
const MutableSpan<T> span = this->get_internal_span();
|
||||
initialized_copy_n(src.data(), this->size_, span.data());
|
||||
}
|
||||
else {
|
||||
const int64_t size = this->size_;
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
this->set(i, src[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
|
||||
template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
|
||||
|
||||
/**
|
||||
* A virtual array implementation for a span. This class is final so that it can be devirtualized
|
||||
* by the compiler in some cases (e.g. when #devirtualize_varray is used).
|
||||
* A virtual array implementation for a span. Methods in this class are final so that it can be
|
||||
* devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used).
|
||||
*/
|
||||
template<typename T> class VArrayForSpan final : public VArray<T> {
|
||||
private:
|
||||
const T *data_;
|
||||
template<typename T> class VArray_For_Span : public VArray<T> {
|
||||
protected:
|
||||
const T *data_ = nullptr;
|
||||
|
||||
public:
|
||||
VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
|
||||
VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
T get_impl(const int64_t index) const override
|
||||
VArray_For_Span(const int64_t size) : VArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
T get_impl(const int64_t index) const final
|
||||
{
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
bool is_span_impl() const final
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Span<T> get_internal_span_impl() const final
|
||||
{
|
||||
return Span<T>(data_, this->size_);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> {
|
||||
protected:
|
||||
T *data_ = nullptr;
|
||||
|
||||
public:
|
||||
VMutableArray_For_MutableSpan(const MutableSpan<T> data)
|
||||
: VMutableArray<T>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size)
|
||||
{
|
||||
}
|
||||
|
||||
T get_impl(const int64_t index) const final
|
||||
{
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
void set_impl(const int64_t index, T value) final
|
||||
{
|
||||
data_[index] = value;
|
||||
}
|
||||
|
||||
bool is_span_impl() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Span<T> get_span_impl() const override
|
||||
Span<T> get_internal_span_impl() const override
|
||||
{
|
||||
return Span<T>(data_, this->size_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A variant of `VArray_For_Span` that owns the underlying data.
|
||||
* The `Container` type has to implement a `size()` and `data()` method.
|
||||
* The `data()` method has to return a pointer to the first element in the continuous array of
|
||||
* elements.
|
||||
*/
|
||||
template<typename Container, typename T = typename Container::value_type>
|
||||
class VArray_For_ArrayContainer : public VArray_For_Span<T> {
|
||||
private:
|
||||
Container container_;
|
||||
|
||||
public:
|
||||
VArray_For_ArrayContainer(Container container)
|
||||
: VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container))
|
||||
{
|
||||
this->data_ = container_.data();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A virtual array implementation that returns the same value for every index. This class is final
|
||||
* so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is
|
||||
* used).
|
||||
*/
|
||||
template<typename T> class VArrayForSingle final : public VArray<T> {
|
||||
template<typename T> class VArray_For_Single final : public VArray<T> {
|
||||
private:
|
||||
T value_;
|
||||
|
||||
public:
|
||||
VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
|
||||
VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -199,7 +365,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
|
||||
return this->size_ == 1;
|
||||
}
|
||||
|
||||
Span<T> get_span_impl() const override
|
||||
Span<T> get_internal_span_impl() const override
|
||||
{
|
||||
return Span<T>(&value_, 1);
|
||||
}
|
||||
@@ -209,12 +375,170 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
T get_single_impl() const override
|
||||
T get_internal_single_impl() const override
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* In many cases a virtual array is a span internally. In those cases, access to individual could
|
||||
* be much more efficient than calling a virtual method. When the underlying virtual array is not a
|
||||
* span, this class allocates a new array and copies the values over.
|
||||
*
|
||||
* This should be used in those cases:
|
||||
* - All elements in the virtual array are accessed multiple times.
|
||||
* - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
|
||||
* from faster access.
|
||||
* - An API is called, that does not accept virtual arrays, but only spans.
|
||||
*/
|
||||
template<typename T> class VArray_Span final : public Span<T> {
|
||||
private:
|
||||
const VArray<T> &varray_;
|
||||
Array<T> owned_data_;
|
||||
|
||||
public:
|
||||
VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray)
|
||||
{
|
||||
this->size_ = varray_.size();
|
||||
if (varray_.is_span()) {
|
||||
this->data_ = varray_.get_internal_span().data();
|
||||
}
|
||||
else {
|
||||
owned_data_.~Array();
|
||||
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
|
||||
varray_.materialize_to_uninitialized(owned_data_);
|
||||
this->data_ = owned_data_.data();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as VArray_Span, but for a mutable span.
|
||||
* The important thing to note is that when changing this span, the results might not be
|
||||
* immediately reflected in the underlying virtual array (only when the virtual array is a span
|
||||
* internally). The #save method can be used to write all changes to the underlying virtual array,
|
||||
* if necessary.
|
||||
*/
|
||||
template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
|
||||
private:
|
||||
VMutableArray<T> &varray_;
|
||||
Array<T> owned_data_;
|
||||
bool save_has_been_called_ = false;
|
||||
bool show_not_saved_warning_ = true;
|
||||
|
||||
public:
|
||||
/* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If
|
||||
* not, a new array has to be allocated as a wrapper for the underlying virtual array. */
|
||||
VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true)
|
||||
: MutableSpan<T>(), varray_(varray)
|
||||
{
|
||||
this->size_ = varray_.size();
|
||||
if (varray_.is_span()) {
|
||||
this->data_ = varray_.get_internal_span().data();
|
||||
}
|
||||
else {
|
||||
if (copy_values_to_span) {
|
||||
owned_data_.~Array();
|
||||
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
|
||||
varray_.materialize_to_uninitialized(owned_data_);
|
||||
}
|
||||
else {
|
||||
owned_data_.reinitialize(varray_.size());
|
||||
}
|
||||
this->data_ = owned_data_.data();
|
||||
}
|
||||
}
|
||||
|
||||
~VMutableArray_Span()
|
||||
{
|
||||
if (show_not_saved_warning_) {
|
||||
if (!save_has_been_called_) {
|
||||
std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write back all values from a temporary allocated array to the underlying virtual array. */
|
||||
void save()
|
||||
{
|
||||
save_has_been_called_ = true;
|
||||
if (this->data_ != owned_data_.data()) {
|
||||
return;
|
||||
}
|
||||
varray_.set_all(owned_data_);
|
||||
}
|
||||
|
||||
void disable_not_applied_warning()
|
||||
{
|
||||
show_not_saved_warning_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class makes it easy to create a virtual array for an existing function or lambda. The
|
||||
* `GetFunc` should take a single `index` argument and return the value at that index.
|
||||
*/
|
||||
template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> {
|
||||
private:
|
||||
GetFunc get_func_;
|
||||
|
||||
public:
|
||||
VArray_For_Func(const int64_t size, GetFunc get_func)
|
||||
: VArray<T>(size), get_func_(std::move(get_func))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
T get_impl(const int64_t index) const override
|
||||
{
|
||||
return get_func_(index);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
class VArray_For_DerivedSpan : public VArray<ElemT> {
|
||||
private:
|
||||
const StructT *data_;
|
||||
|
||||
public:
|
||||
VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ElemT get_impl(const int64_t index) const override
|
||||
{
|
||||
return GetFunc(data_[index]);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, ElemT)>
|
||||
class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> {
|
||||
private:
|
||||
StructT *data_;
|
||||
|
||||
public:
|
||||
VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
|
||||
: VMutableArray<ElemT>(data.size()), data_(data.data())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ElemT get_impl(const int64_t index) const override
|
||||
{
|
||||
return GetFunc(data_[index]);
|
||||
}
|
||||
|
||||
void set_impl(const int64_t index, ElemT value) override
|
||||
{
|
||||
SetFunc(data_[index], std::move(value));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate multiple versions of the given function optimized for different virtual arrays.
|
||||
* One has to be careful with nesting multiple devirtualizations, because that results in an
|
||||
@@ -229,14 +553,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool
|
||||
/* Support disabling the devirtualization to simplify benchmarking. */
|
||||
if (enable) {
|
||||
if (varray.is_single()) {
|
||||
/* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()};
|
||||
/* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()};
|
||||
func(varray_single);
|
||||
return;
|
||||
}
|
||||
if (varray.is_span()) {
|
||||
/* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArrayForSpan<T> varray_span{varray.get_span()};
|
||||
/* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */
|
||||
const VArray_For_Span<T> varray_span{varray.get_internal_span()};
|
||||
func(varray_span);
|
||||
return;
|
||||
}
|
||||
@@ -262,26 +586,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
|
||||
const bool is_single1 = varray1.is_single();
|
||||
const bool is_single2 = varray2.is_single();
|
||||
if (is_span1 && is_span2) {
|
||||
const VArrayForSpan<T1> varray1_span{varray1.get_span()};
|
||||
const VArrayForSpan<T2> varray2_span{varray2.get_span()};
|
||||
const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
|
||||
const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
|
||||
func(varray1_span, varray2_span);
|
||||
return;
|
||||
}
|
||||
if (is_span1 && is_single2) {
|
||||
const VArrayForSpan<T1> varray1_span{varray1.get_span()};
|
||||
const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
|
||||
const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
|
||||
const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
|
||||
func(varray1_span, varray2_single);
|
||||
return;
|
||||
}
|
||||
if (is_single1 && is_span2) {
|
||||
const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
|
||||
const VArrayForSpan<T2> varray2_span{varray2.get_span()};
|
||||
const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
|
||||
const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
|
||||
func(varray1_single, varray2_span);
|
||||
return;
|
||||
}
|
||||
if (is_single1 && is_single2) {
|
||||
const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
|
||||
const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
|
||||
const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
|
||||
const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
|
||||
func(varray1_single, varray2_single);
|
||||
return;
|
||||
}
|
||||
|
@@ -2691,8 +2691,12 @@ static IMesh raycast_patches_boolean(const IMesh &tm,
|
||||
* each edge in the output triangles either matches the original edge
|
||||
* for the (identical) edge of f, or else is -1. So diagonals added
|
||||
* for triangulation can later be identified by having #NO_INDEX for original.
|
||||
*
|
||||
* This code uses Blenlib's BLI_polyfill_calc to do triangulation, and is therefore quite fast.
|
||||
* Unfortunately, it can product degenerate triangles that mesh_intersect will remove, leaving
|
||||
* the mesh non-PWN.
|
||||
*/
|
||||
static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
|
||||
static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
|
||||
{
|
||||
/* Similar to loop body in BM_mesh_calc_tesselation. */
|
||||
int flen = f->size();
|
||||
@@ -2746,6 +2750,113 @@ static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
|
||||
return ans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Which CDT output edge index is for an edge between output verts
|
||||
* v1 and v2 (in either order)?
|
||||
* \return -1 if none.
|
||||
*/
|
||||
static int find_cdt_edge(const CDT_result<mpq_class> &cdt_out, int v1, int v2)
|
||||
{
|
||||
for (int e : cdt_out.edge.index_range()) {
|
||||
const std::pair<int, int> &edge = cdt_out.edge[e];
|
||||
if ((edge.first == v1 && edge.second == v2) || (edge.first == v2 && edge.second == v1)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tessellate face f into triangles and return an array of `const Face *`
|
||||
* giving that triangulation.
|
||||
* Care is taken so that the original edge index associated with
|
||||
* each edge in the output triangles either matches the original edge
|
||||
* for the (identical) edge of f, or else is -1. So diagonals added
|
||||
* for triangulation can later be identified by having #NO_INDEX for original.
|
||||
*
|
||||
* The method used is to use the CDT triangulation. Usually that triangulation
|
||||
* will only use the existing vertices. However, if the face self-intersects
|
||||
* then the CDT triangulation will include the intersection points.
|
||||
* If this happens, we use the polyfill triangulator instead. We don't
|
||||
* use the polyfill triangulator by default because it can create degenerate
|
||||
* triangles (which we can handle but they'll create non-manifold meshes).
|
||||
*/
|
||||
static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
|
||||
{
|
||||
int flen = f->size();
|
||||
CDT_input<mpq_class> cdt_in;
|
||||
cdt_in.vert = Array<mpq2>(flen);
|
||||
cdt_in.face = Array<Vector<int>>(1);
|
||||
cdt_in.face[0].reserve(flen);
|
||||
for (int i : f->index_range()) {
|
||||
cdt_in.face[0].append(i);
|
||||
}
|
||||
/* Project poly along dominant axis of normal to get 2d coords. */
|
||||
if (!f->plane_populated()) {
|
||||
f->populate_plane(false);
|
||||
}
|
||||
const double3 &poly_normal = f->plane->norm;
|
||||
int axis = double3::dominant_axis(poly_normal);
|
||||
/* If project down y axis as opposed to x or z, the orientation
|
||||
* of the polygon will be reversed.
|
||||
* Yet another reversal happens if the poly normal in the dominant
|
||||
* direction is opposite that of the positive dominant axis. */
|
||||
bool rev1 = (axis == 1);
|
||||
bool rev2 = poly_normal[axis] < 0;
|
||||
bool rev = rev1 ^ rev2;
|
||||
for (int i = 0; i < flen; ++i) {
|
||||
int ii = rev ? flen - i - 1 : i;
|
||||
mpq2 &p2d = cdt_in.vert[ii];
|
||||
int k = 0;
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
if (j != axis) {
|
||||
p2d[k++] = (*f)[ii]->co_exact[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE);
|
||||
int n_tris = cdt_out.face.size();
|
||||
Array<Face *> ans(n_tris);
|
||||
for (int t = 0; t < n_tris; ++t) {
|
||||
int i_v_out[3];
|
||||
const Vert *v[3];
|
||||
int eo[3];
|
||||
bool needs_steiner = false;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
i_v_out[i] = cdt_out.face[t][i];
|
||||
if (cdt_out.vert_orig[i_v_out[i]].size() == 0) {
|
||||
needs_steiner = true;
|
||||
break;
|
||||
}
|
||||
v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]];
|
||||
}
|
||||
if (needs_steiner) {
|
||||
/* Fall back on the polyfill triangulator. */
|
||||
return polyfill_triangulate_poly(f, arena);
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
int e_out = find_cdt_edge(cdt_out, i_v_out[i], i_v_out[(i + 1) % 3]);
|
||||
BLI_assert(e_out != -1);
|
||||
eo[i] = NO_INDEX;
|
||||
for (int orig : cdt_out.edge_orig[e_out]) {
|
||||
if (orig != NO_INDEX) {
|
||||
eo[i] = orig;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rev) {
|
||||
ans[t] = arena->add_face(
|
||||
{v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false});
|
||||
}
|
||||
else {
|
||||
ans[t] = arena->add_face(
|
||||
{v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an #IMesh that is a triangulation of a mesh with general
|
||||
* polygonal faces, #IMesh.
|
||||
|
@@ -539,6 +539,29 @@ void BLI_str_replace_char(char *str, char src, char dst)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple exact-match string replacement.
|
||||
*
|
||||
* \param replace_table: Array of source, destination pairs.
|
||||
*
|
||||
* \note Larger tables should use a hash table.
|
||||
*/
|
||||
bool BLI_str_replace_table_exact(char *string,
|
||||
const size_t string_len,
|
||||
const char *replace_table[][2],
|
||||
int replace_table_len)
|
||||
{
|
||||
for (int i = 0; i < replace_table_len; i++) {
|
||||
if (STREQ(string, replace_table[i][0])) {
|
||||
BLI_strncpy(string, replace_table[i][1], string_len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/**
|
||||
* Compare two strings without regard to case.
|
||||
*
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user