Compare commits
508 Commits
geometry-n
...
temp-exper
Author | SHA1 | Date | |
---|---|---|---|
b3d34a6690 | |||
3c112c5e46 | |||
e44e0e4e78 | |||
ac290bfbe4 | |||
b271475a9e | |||
bf90fcda47 | |||
1a26d15763 | |||
2f9073adb1 | |||
9e1ec5033d | |||
98268e5c68 | |||
28f99dda6c | |||
beb5863ed4 | |||
c083398921 | |||
7b6e55b933 | |||
2a297366f0 | |||
98262bb8cf | |||
3e251da3aa | |||
fe53b6a7fb | |||
a37164e11b | |||
![]() |
f0ae0f490e | ||
6e7716f32e | |||
445ebcaa30 | |||
54f8a5dd73 | |||
63cea2b793 | |||
d095411002 | |||
4c44a5309c | |||
618f7df7fc | |||
9fb32b2b29 | |||
64929398fb | |||
a83c67c183 | |||
b01e62e399 | |||
16aefea9fa | |||
50104b11a1 | |||
ab5986cf3a | |||
af88d23ffa | |||
d4330ae70b | |||
ad15e764dd | |||
08e44b5e3e | |||
9db3d1951d | |||
1d3b92bdea | |||
30310a4fc8 | |||
5b90ed6c06 | |||
![]() |
544e371908 | ||
e45630a99f | |||
9441413cb2 | |||
6154aa1529 | |||
fa82a15676 | |||
e718004edf | |||
246efd7286 | |||
645298be29 | |||
1bb530745f | |||
9c07454816 | |||
69a22a70ac | |||
7fd19c20e0 | |||
3c1fcec652 | |||
bc788929aa | |||
2d3f96cace | |||
c549d736cf | |||
c66f00dc26 | |||
e3ae7d1f2f | |||
c889ec916c | |||
c44a17ec4b | |||
1698678231 | |||
03f1d8acab | |||
4a0b8c9427 | |||
8877e294df | |||
662b7c3edf | |||
ceaed47bf9 | |||
f448ff2afe | |||
09ea339a6c | |||
15bb8f9f93 | |||
61f1faac3f | |||
5530d6f86f | |||
95c63babbb | |||
d259e7dcfb | |||
3db2bc82aa | |||
![]() |
64277e8f3a | ||
![]() |
e3d9241a05 | ||
3db975f30d | |||
4c0fc60105 | |||
044dd42a05 | |||
987e9e2145 | |||
c55b578c9e | |||
c26f46cb95 | |||
0d042c8cca | |||
4a771263fd | |||
![]() |
dd0df3c5d2 | ||
2dfd117ab5 | |||
a9dea9cfaa | |||
f35a38fba7 | |||
27426c05b1 | |||
b138c8f5b3 | |||
a9c607e60e | |||
133bdac306 | |||
211e161d76 | |||
6ea01dc509 | |||
3028de9527 | |||
5cdf279ef4 | |||
6672cbeb23 | |||
a584aef470 | |||
691c021679 | |||
f0071dfa10 | |||
0e4f8ed90e | |||
eb1ff4b3a4 | |||
1c72a2f47d | |||
967cf303f3 | |||
bc3e38ca3a | |||
4e23f08807 | |||
290b6d7ef3 | |||
4b56c18290 | |||
947dc92083 | |||
![]() |
5424b4821d | ||
2ed6055209 | |||
c20e482714 | |||
4ade409a87 | |||
3373d14b1b | |||
491a9e9ec4 | |||
90a26f900c | |||
166c0db3f9 | |||
da9d471e1d | |||
583006d0ef | |||
39f99fd05c | |||
9dbea1db66 | |||
357e519575 | |||
cffa39358f | |||
36ae6e66c1 | |||
![]() |
1f41bdc6f3 | ||
7cd6f667e3 | |||
f41de6dc46 | |||
fab772860d | |||
acfa7b102b | |||
1f6846fa4e | |||
e4884d224c | |||
105d385e4b | |||
7241104877 | |||
c0a8dd943f | |||
dc4014c676 | |||
7d152bedc5 | |||
08f00f4f6e | |||
4150facd61 | |||
5370a7dd40 | |||
d3c62b682d | |||
a0db43cd7b | |||
6990b6ed3b | |||
f359102589 | |||
9b17e71c23 | |||
5a6dfb7571 | |||
9316cb33d8 | |||
102eff0bd4 | |||
ec90dda318 | |||
![]() |
5a498e6a7b | ||
54ee410914 | |||
8c80299fc4 | |||
5aab7f66a0 | |||
09c1cb8a17 | |||
c6e5b3f42d | |||
c716b9862a | |||
cb2517016b | |||
a3fcbb54f4 | |||
02d1f1482a | |||
5e38384034 | |||
c7085be6c6 | |||
d11a87b88c | |||
17be2149a8 | |||
b8e536f381 | |||
a6285339ba | |||
![]() |
80e720ee4a | ||
14ee48d898 | |||
f43e8cceb3 | |||
f17184bfeb | |||
3254a63218 | |||
dcdc0f177a | |||
cd29d76ab6 | |||
2008c24123 | |||
82bbe257ee | |||
a5081896bc | |||
b5c2a75d26 | |||
22895d0f32 | |||
b34eded930 | |||
9ff4012711 | |||
fea1026bb8 | |||
dfbda59a66 | |||
f03c25bda8 | |||
54f89e8704 | |||
9b10b3930b | |||
c61c7d9926 | |||
613c568df2 | |||
12409b2069 | |||
01f38c10cb | |||
1e8f266591 | |||
c7e92e379d | |||
![]() |
e1b8af9ba7 | ||
0330b0552b | |||
057a8afb87 | |||
0da1fc2fc4 | |||
cda8979005 | |||
8c135fa9a8 | |||
85951f8fde | |||
e5e57fe0a2 | |||
8373f497bd | |||
![]() |
2d86175bac | ||
2be2165dd6 | |||
f910342c8d | |||
761ef45a24 | |||
c1740e9ad0 | |||
48e0e0e2ac | |||
ae82410329 | |||
e22a36e80a | |||
5428ad40e8 | |||
81c57c9471 | |||
1db27af38f | |||
![]() |
6fbeb6e2e0 | ||
7f3601e70d | |||
ad63d2f60e | |||
b1d8aeeab2 | |||
26c34a2a3a | |||
823f0da702 | |||
e6bb2cdec7 | |||
e0b6c8f777 | |||
e590d2d167 | |||
57fe65b17e | |||
aa64fd69e7 | |||
960a0b892c | |||
e56fe47748 | |||
7916c59304 | |||
8491e4ab86 | |||
![]() |
565ea3df60 | ||
![]() |
af316d2761 | ||
![]() |
2917f550ca | ||
fe440a92e9 | |||
29cd99e7fd | |||
c4f8aedbc2 | |||
2221389d6e | |||
0b4fae7a51 | |||
58c697a9ec | |||
83ad35cb9c | |||
c9efb54240 | |||
d8dc4c5b32 | |||
8a9dedf829 | |||
d29a720c45 | |||
9d04fa39d1 | |||
635694c0ff | |||
5cfda8e7f7 | |||
21cb288029 | |||
27fcaa6173 | |||
78d2ce19c4 | |||
9460051c1a | |||
b4b888fd2a | |||
8d3d4c8840 | |||
4b46afae09 | |||
ac82904c49 | |||
00ff3dfb23 | |||
b2edc716c1 | |||
dad5aded0c | |||
2601501fce | |||
512a23c3d6 | |||
0e85d701c6 | |||
9f34f2b20d | |||
ffacce5be4 | |||
![]() |
2d6e6d035b | ||
1e2c028a3a | |||
36b6ea6cb1 | |||
be71bd8fcc | |||
![]() |
985528c9b9 | ||
d2239b685c | |||
626a9aae6d | |||
5fe24ce295 | |||
c34ba26856 | |||
c9c3bf9833 | |||
42e2dd2178 | |||
720b2c6655 | |||
![]() |
fdb7623e09 | ||
![]() |
84cc00f3b6 | ||
c229d9876b | |||
d283a093d6 | |||
![]() |
478ba53270 | ||
16527ebaba | |||
![]() |
5a69f70751 | ||
![]() |
864c806863 | ||
38b77ef8b2 | |||
c4ff91aab7 | |||
![]() |
0144b70948 | ||
![]() |
6538f1e600 | ||
09be4a0917 | |||
a5a302bd18 | |||
046ca0749a | |||
6942dd9f49 | |||
002722bb80 | |||
c106b07e23 | |||
6367bc716a | |||
e298339289 | |||
6be9747b96 | |||
2250b5cefe | |||
7bee1489c1 | |||
3deb21055a | |||
1be465cccb | |||
f3b50380f0 | |||
![]() |
95afc53604 | ||
e5764013b0 | |||
c5569ba140 | |||
79d6bd9a22 | |||
f43561eae6 | |||
b342e089c3 | |||
e5528904f1 | |||
fe5d2448c6 | |||
ffb6648a97 | |||
4f9e21bdc9 | |||
095b693614 | |||
9dbc014af7 | |||
7d25139eaf | |||
7cbcfb7f49 | |||
f880fe2d66 | |||
![]() |
ef17fb2715 | ||
23233fcf05 | |||
b10d8e330e | |||
f193b1afb3 | |||
48ddb94a26 | |||
![]() |
e7b698327c | ||
fed995ced5 | |||
3fc07d1e74 | |||
c9f8f7915f | |||
a9edf2c869 | |||
9258b70453 | |||
cf2ebaf27c | |||
0eedba328d | |||
2945c1e3e5 | |||
6203a3ee68 | |||
7e535499d5 | |||
8df6589585 | |||
d11b219d40 | |||
3a1d1aaa86 | |||
9f588432e9 | |||
07c4615431 | |||
247b10e6a2 | |||
![]() |
ad7682ffdb | ||
5713626422 | |||
9d15226383 | |||
a8da70f70a | |||
c484b54453 | |||
c06c5d617a | |||
ebd8a703cc | |||
a7628ec22a | |||
![]() |
d23894d3ef | ||
f3ab123e33 | |||
5c5550f7b8 | |||
7ed69bd672 | |||
4463087223 | |||
684c771263 | |||
25543e6983 | |||
8d590e4b86 | |||
8da62a9a86 | |||
245450b144 | |||
![]() |
69c3f4a46d | ||
29f923b27c | |||
4c26dd430d | |||
985d673374 | |||
![]() |
4f128269b2 | ||
3fc9fc1cb4 | |||
58d818f8be | |||
c7a500e3a0 | |||
19fc30c15f | |||
0ae15e68c7 | |||
055ef5df61 | |||
![]() |
975ca91939 | ||
9cbfcc4af5 | |||
![]() |
1a375d6ece | ||
d23a5b1d88 | |||
e671c548e6 | |||
![]() |
f34ca933a8 | ||
a869a61c88 | |||
b347c4e9ca | |||
588f107f11 | |||
87fdb71438 | |||
977bd7937a | |||
![]() |
fad80a95fd | ||
![]() |
f44dea0558 | ||
![]() |
dd9d12bf45 | ||
![]() |
151e847b87 | ||
![]() |
5535b0b887 | ||
9e4a4c2e99 | |||
7e8f6985d8 | |||
1e799dd26e | |||
c9b4d75336 | |||
288f2efbd4 | |||
c9de65679b | |||
26e9c2147e | |||
d59e87acf0 | |||
8df167873a | |||
3834dc2f7b | |||
ffe63b0440 | |||
b24712a9ca | |||
c6692014cf | |||
990406e1ff | |||
7dc8db7cd1 | |||
9caeb9dfc7 | |||
68e4a90240 | |||
303aceb917 | |||
2d6a69ae4e | |||
70474e1a7c | |||
e413c80371 | |||
b5d778a7d4 | |||
9363132c86 | |||
c25e031049 | |||
b71eb3a105 | |||
82645ff739 | |||
0d58eabee6 | |||
d8db5cb600 | |||
ef2151d73d | |||
1b130f17c9 | |||
35368e8bfc | |||
12792ee70c | |||
e035d7301c | |||
a05d98884a | |||
612598acd7 | |||
001f2c5d50 | |||
e995296acb | |||
525364be31 | |||
15f2f69694 | |||
1e38e32cf6 | |||
7470c10601 | |||
![]() |
6074636387 | ||
![]() |
7f6ffe9195 | ||
![]() |
fa6bf8f5b6 | ||
![]() |
04ca93ef9b | ||
20bc1ab275 | |||
49ec3cef69 | |||
010f44b855 | |||
f4df036bc4 | |||
07ce9910f7 | |||
9ee7270e0a | |||
6714b800d1 | |||
e3068f38c8 | |||
![]() |
68d5ad9983 | ||
4b0396695c | |||
732d0b458b | |||
551856ed32 | |||
fdc9350a9f | |||
6da609fcb3 | |||
812ea91842 | |||
6f7ced77e3 | |||
![]() |
f5a019ed43 | ||
8e1b63d4bd | |||
5d570c875e | |||
53ed96641c | |||
45bf470ee9 | |||
067046c26a | |||
088df2bb03 | |||
b8ae90263a | |||
7d4536cacc | |||
6327771bc9 | |||
8f3a401975 | |||
fddbcb5757 | |||
260fca5d08 | |||
![]() |
977ef04746 | ||
dd0520b93b | |||
![]() |
a2693ba43e | ||
![]() |
afeaac8b18 | ||
![]() |
2e5d9a73f7 | ||
![]() |
bec583951d | ||
9c0df8e275 | |||
c6075118d5 | |||
4797c13e8f | |||
57f900e4ef | |||
3eb6649453 | |||
ae94a390a7 | |||
0c1d476923 | |||
af008f5532 | |||
ba83ad226d | |||
0dbbcaf1e6 | |||
bbd7f94d8a | |||
cada56b1f7 | |||
87b19b3aba | |||
92ab76c38f | |||
![]() |
93c67b0b8e | ||
97651f428b | |||
b73ed882c0 | |||
cef5d0923b | |||
561d9169fe | |||
85cb238820 | |||
f5dc34ec9c | |||
5ced167336 | |||
4885fbc07b | |||
f762d37790 | |||
c6626a2f8a | |||
![]() |
f7069d71aa | ||
![]() |
badbf816b8 | ||
273aca964e | |||
6de85a0cc0 | |||
c8a3d1a1fa | |||
727d5013a7 | |||
7c8e01341f | |||
bfb6fce659 | |||
d72ec16e70 | |||
150a1d158a | |||
f5c7246717 | |||
f7e5f96f56 | |||
b62a9ef66f | |||
![]() |
6e4fccd9fa | ||
219dba8506 | |||
392a8e2907 | |||
8cc951d2ae | |||
ff6d7e9072 | |||
![]() |
a4a42f3171 | ||
caed4849d0 | |||
a7fcca1062 | |||
8bdd996cd0 | |||
3d25312617 | |||
1d447dcd19 |
@@ -347,16 +347,21 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
option(WITH_PYTHON_INSTALL "Copy system python into the blender install folder" ON)
|
||||
|
||||
if((WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE) OR WITH_MOD_FLUID)
|
||||
option(WITH_PYTHON_NUMPY "Include NumPy in Blender (used by Audaspace and Mantaflow)" ON)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR APPLE)
|
||||
# Windows and macOS have this bundled with Python libraries.
|
||||
elseif(WITH_PYTHON_INSTALL OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE))
|
||||
elseif(WITH_PYTHON_INSTALL OR WITH_PYTHON_NUMPY)
|
||||
set(PYTHON_NUMPY_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'numpy' module")
|
||||
mark_as_advanced(PYTHON_NUMPY_PATH)
|
||||
set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module")
|
||||
set(PYTHON_NUMPY_INCLUDE_DIRS "" CACHE PATH "Path to the include directory of the NumPy module")
|
||||
mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS)
|
||||
endif()
|
||||
if(WITH_PYTHON_INSTALL)
|
||||
option(WITH_PYTHON_INSTALL_NUMPY "Copy system numpy into the blender install folder" ON)
|
||||
option(WITH_PYTHON_INSTALL_NUMPY "Copy system NumPy into the blender install folder" ON)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
option(WITH_PYTHON_INSTALL_REQUESTS "Copy system requests into the blender install folder" ON)
|
||||
@@ -605,6 +610,11 @@ if(WIN32)
|
||||
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
# See WITH_WINDOWS_SCCACHE for Windows.
|
||||
option(WITH_COMPILER_CCACHE "Use ccache to improve rebuild times (Works with Ninja, Makefiles and Xcode)" OFF)
|
||||
endif()
|
||||
|
||||
# The following only works with the Ninja generator in CMake >= 3.0.
|
||||
if("${CMAKE_GENERATOR}" MATCHES "Ninja")
|
||||
option(WITH_NINJA_POOL_JOBS
|
||||
@@ -1621,19 +1631,16 @@ if(WITH_PYTHON)
|
||||
|
||||
if(WIN32 OR APPLE)
|
||||
# Windows and macOS have this bundled with Python libraries.
|
||||
elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE))
|
||||
elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR WITH_PYTHON_NUMPY)
|
||||
if(("${PYTHON_NUMPY_PATH}" STREQUAL "") OR (${PYTHON_NUMPY_PATH} MATCHES NOTFOUND))
|
||||
find_python_package(numpy)
|
||||
unset(PYTHON_NUMPY_INCLUDE_DIRS CACHE)
|
||||
set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module")
|
||||
mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS)
|
||||
find_python_package(numpy "core/include")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32 OR APPLE)
|
||||
# pass, we have this in lib/python/site-packages
|
||||
elseif(WITH_PYTHON_INSTALL_REQUESTS)
|
||||
find_python_package(requests)
|
||||
find_python_package(requests "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1766,6 +1773,10 @@ elseif(WITH_CYCLES_STANDALONE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Testing
|
||||
add_subdirectory(tests)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Blender Application
|
||||
if(WITH_BLENDER)
|
||||
@@ -1773,11 +1784,6 @@ if(WITH_BLENDER)
|
||||
endif()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Testing
|
||||
add_subdirectory(tests)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Define 'heavy' submodules (for Ninja builder when using pools).
|
||||
setup_heavy_lib_pool()
|
||||
|
@@ -41,6 +41,7 @@ Convenience Targets
|
||||
* developer: Enable faster builds, error checking and tests, recommended for developers.
|
||||
* config: Run cmake configuration tool to set build options.
|
||||
* ninja: Use ninja build tool for faster builds.
|
||||
* ccache: Use ccache for faster rebuilds.
|
||||
|
||||
Note: passing the argument 'BUILD_DIR=path' when calling make will override the default build dir.
|
||||
Note: passing the argument 'BUILD_CMAKE_ARGS=args' lets you add cmake arguments.
|
||||
@@ -241,6 +242,10 @@ ifneq "$(findstring developer, $(MAKECMDGOALS))" ""
|
||||
CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/blender_developer.cmake" $(CMAKE_CONFIG_ARGS)
|
||||
endif
|
||||
|
||||
ifneq "$(findstring ccache, $(MAKECMDGOALS))" ""
|
||||
CMAKE_CONFIG_ARGS:=-DWITH_COMPILER_CCACHE=YES $(CMAKE_CONFIG_ARGS)
|
||||
endif
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# build tool
|
||||
|
||||
@@ -340,6 +345,7 @@ headless: all
|
||||
bpy: all
|
||||
developer: all
|
||||
ninja: all
|
||||
ccache: all
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build dependencies
|
||||
|
@@ -2086,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=OFF"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=ON"
|
||||
cmake_d="$cmake_d -D TXT2MAN="
|
||||
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
|
||||
@@ -4072,7 +4072,7 @@ install_DEB() {
|
||||
else
|
||||
check_package_version_ge_lt_DEB libopenimageio-dev $OIIO_VERSION_MIN $OIIO_VERSION_MAX
|
||||
if [ $? -eq 0 -a "$_with_built_openexr" = false ]; then
|
||||
install_packages_DEB libopenimageio-dev
|
||||
install_packages_DEB libopenimageio-dev openimageio-tools
|
||||
clean_OIIO
|
||||
else
|
||||
compile_OIIO
|
||||
@@ -4714,13 +4714,13 @@ install_RPM() {
|
||||
INFO "Forced OpenImageIO building, as requested..."
|
||||
compile_OIIO
|
||||
else
|
||||
#check_package_version_ge_lt_RPM OpenImageIO-devel $OIIO_VERSION_MIN $OIIO_VERSION_MAX
|
||||
#if [ $? -eq 0 -a $_with_built_openexr == false ]; then
|
||||
# install_packages_RPM OpenImageIO-devel
|
||||
# clean_OIIO
|
||||
#else
|
||||
check_package_version_ge_lt_RPM OpenImageIO-devel $OIIO_VERSION_MIN $OIIO_VERSION_MAX
|
||||
if [ $? -eq 0 -a $_with_built_openexr == false ]; then
|
||||
install_packages_RPM OpenImageIO-devel OpenImageIO-utils
|
||||
clean_OIIO
|
||||
else
|
||||
compile_OIIO
|
||||
#fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
@@ -330,6 +330,9 @@ function(gtest_add_tests)
|
||||
set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*")
|
||||
set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)")
|
||||
|
||||
# This will get a filter for each test suite.
|
||||
set(test_filters "")
|
||||
|
||||
foreach(source IN LISTS ARGS_SOURCES)
|
||||
if(NOT ARGS_SKIP_DEPENDENCY)
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${source})
|
||||
@@ -376,175 +379,32 @@ function(gtest_add_tests)
|
||||
list(APPEND testList ${ctest_test_name})
|
||||
endif()
|
||||
else()
|
||||
set(ctest_test_name ${ARGS_TEST_PREFIX}${gtest_test_name}${ARGS_TEST_SUFFIX})
|
||||
add_test(NAME ${ctest_test_name}
|
||||
${workDir}
|
||||
COMMAND ${ARGS_TARGET}
|
||||
--gtest_filter=${gtest_test_name}
|
||||
${ARGS_EXTRA_ARGS}
|
||||
)
|
||||
list(APPEND testList ${ctest_test_name})
|
||||
# BLENDER: collect tests named "suite.testcase" as list of "suite.*" filters.
|
||||
string(REGEX REPLACE "\\..*$" "" gtest_suite_name ${gtest_test_name})
|
||||
list(APPEND test_filters "${gtest_suite_name}.*")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# Join all found GTest suite names into one big filter.
|
||||
list(REMOVE_DUPLICATES test_filters)
|
||||
list(JOIN test_filters ":" gtest_filter)
|
||||
add_test(NAME ${ARGS_TEST_PREFIX}
|
||||
${workDir}
|
||||
COMMAND ${ARGS_TARGET}
|
||||
--gtest_filter=${gtest_filter}
|
||||
${ARGS_EXTRA_ARGS}
|
||||
)
|
||||
list(APPEND testList ${ARGS_TEST_PREFIX})
|
||||
|
||||
if(ARGS_TEST_LIST)
|
||||
set(${ARGS_TEST_LIST} ${testList} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
function(gtest_discover_tests TARGET)
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
"NO_PRETTY_TYPES;NO_PRETTY_VALUES"
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE"
|
||||
"EXTRA_ARGS;PROPERTIES"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if(NOT _WORKING_DIRECTORY)
|
||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
if(NOT _DISCOVERY_TIMEOUT)
|
||||
set(_DISCOVERY_TIMEOUT 5)
|
||||
endif()
|
||||
if(NOT _DISCOVERY_MODE)
|
||||
if(NOT CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE)
|
||||
set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
|
||||
endif()
|
||||
set(_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE})
|
||||
endif()
|
||||
|
||||
get_property(
|
||||
has_counter
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CTEST_DISCOVERED_TEST_COUNTER
|
||||
SET
|
||||
)
|
||||
if(has_counter)
|
||||
get_property(
|
||||
counter
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CTEST_DISCOVERED_TEST_COUNTER
|
||||
)
|
||||
math(EXPR counter "${counter} + 1")
|
||||
else()
|
||||
set(counter 1)
|
||||
endif()
|
||||
set_property(
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CTEST_DISCOVERED_TEST_COUNTER
|
||||
${counter}
|
||||
)
|
||||
|
||||
# Define rule to generate test list for aforementioned test executable
|
||||
# Blender: use _ instead of [] to avoid problems with zsh regex.
|
||||
set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_${counter}_")
|
||||
set(ctest_include_file "${ctest_file_base}_include.cmake")
|
||||
set(ctest_tests_file "${ctest_file_base}_tests.cmake")
|
||||
get_property(crosscompiling_emulator
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CROSSCOMPILING_EMULATOR
|
||||
)
|
||||
|
||||
if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-D "TEST_TARGET=${TARGET}"
|
||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
|
||||
-D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "CTEST_FILE=${ctest_tests_file}"
|
||||
-D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}"
|
||||
-D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}"
|
||||
-P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
file(WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||
" include(\"${ctest_tests_file}\")\n"
|
||||
"else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n"
|
||||
"endif()\n"
|
||||
)
|
||||
elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL
|
||||
PROPERTY GENERATOR_IS_MULTI_CONFIG
|
||||
)
|
||||
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake")
|
||||
endif()
|
||||
|
||||
string(CONCAT ctest_include_content
|
||||
"if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n"
|
||||
" if(\"$<TARGET_FILE:${TARGET}>\" IS_NEWER_THAN \"${ctest_tests_file}\")" "\n"
|
||||
" include(\"${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}\")" "\n"
|
||||
" gtest_discover_tests_impl(" "\n"
|
||||
" TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n"
|
||||
" TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n"
|
||||
" TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n"
|
||||
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
|
||||
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
|
||||
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
|
||||
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
|
||||
" NO_PRETTY_TYPES" " [==[" "${_NO_PRETTY_TYPES}" "]==]" "\n"
|
||||
" NO_PRETTY_VALUES" " [==[" "${_NO_PRETTY_VALUES}" "]==]" "\n"
|
||||
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
|
||||
" CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n"
|
||||
" TEST_DISCOVERY_TIMEOUT" " [==[" "${_DISCOVERY_TIMEOUT}" "]==]" "\n"
|
||||
" TEST_XML_OUTPUT_DIR" " [==[" "${_XML_OUTPUT_DIR}" "]==]" "\n"
|
||||
" )" "\n"
|
||||
" endif()" "\n"
|
||||
" include(\"${ctest_tests_file}\")" "\n"
|
||||
"else()" "\n"
|
||||
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n"
|
||||
"endif()" "\n"
|
||||
)
|
||||
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||
file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>)
|
||||
endforeach()
|
||||
file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")")
|
||||
else()
|
||||
file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}")
|
||||
file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
|
||||
endif()
|
||||
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(DIRECTORY
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||
)
|
||||
|
||||
endfunction()
|
||||
|
||||
###############################################################################
|
||||
|
||||
set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_CURRENT_LIST_DIR}/GTestAddTests.cmake
|
||||
)
|
||||
# BLENDER: remove the discovery function gtest_discover_tests(). It's not used,
|
||||
# as it generates too many test invocations.
|
||||
|
||||
# Restore project's policies
|
||||
cmake_policy(POP)
|
||||
|
@@ -1,194 +0,0 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License,
|
||||
# see accompanying file BSD-3-Clause-license.txt for details.
|
||||
|
||||
# Changes made to this script have been marked with "BLENDER".
|
||||
|
||||
|
||||
# BLENDER: disable ASAN leak detection when trying to discover tests.
|
||||
set(ENV{ASAN_OPTIONS} "detect_leaks=0")
|
||||
|
||||
cmake_minimum_required(VERSION ${CMAKE_VERSION})
|
||||
|
||||
# Overwrite possibly existing ${_CTEST_FILE} with empty file
|
||||
set(flush_tests_MODE WRITE)
|
||||
|
||||
# Flushes script to ${_CTEST_FILE}
|
||||
macro(flush_script)
|
||||
file(${flush_tests_MODE} "${_CTEST_FILE}" "${script}")
|
||||
set(flush_tests_MODE APPEND)
|
||||
|
||||
set(script "")
|
||||
endmacro()
|
||||
|
||||
# Flushes tests_buffer to tests
|
||||
macro(flush_tests_buffer)
|
||||
list(APPEND tests "${tests_buffer}")
|
||||
set(tests_buffer "")
|
||||
endmacro()
|
||||
|
||||
macro(add_command NAME)
|
||||
set(_args "")
|
||||
foreach(_arg ${ARGN})
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
string(APPEND _args " [==[${_arg}]==]")
|
||||
else()
|
||||
string(APPEND _args " ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
string(APPEND script "${NAME}(${_args})\n")
|
||||
string(LENGTH "${script}" _script_len)
|
||||
if(${_script_len} GREATER "50000")
|
||||
flush_script()
|
||||
endif()
|
||||
# Unsets macro local variables to prevent leakage outside of this macro.
|
||||
unset(_args)
|
||||
unset(_script_len)
|
||||
endmacro()
|
||||
|
||||
function(gtest_discover_tests_impl)
|
||||
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_EXECUTOR;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR"
|
||||
"TEST_EXTRA_ARGS;TEST_PROPERTIES"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
set(prefix "${_TEST_PREFIX}")
|
||||
set(suffix "${_TEST_SUFFIX}")
|
||||
set(extra_args ${_TEST_EXTRA_ARGS})
|
||||
set(properties ${_TEST_PROPERTIES})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
set(tests_buffer)
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${_TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable does not exist.\n"
|
||||
" Path: '${_TEST_EXECUTABLE}'"
|
||||
)
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
string(REPLACE "\n" "\n " output "${output}")
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable.\n"
|
||||
" Path: '${_TEST_EXECUTABLE}'\n"
|
||||
" Result: ${result}\n"
|
||||
" Output:\n"
|
||||
" ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Preserve semicolon in test-parameters
|
||||
string(REPLACE [[;]] [[\;]] output "${output}")
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
# Skip header
|
||||
if(NOT line MATCHES "gtest_main\\.cc")
|
||||
# Do we have a module name or a test name?
|
||||
if(NOT line MATCHES "^ ")
|
||||
# Module; remove trailing '.' to get just the name...
|
||||
string(REGEX REPLACE "\\.( *#.*)?" "" suite "${line}")
|
||||
if(line MATCHES "#" AND NOT _NO_PRETTY_TYPES)
|
||||
string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}")
|
||||
else()
|
||||
set(pretty_suite "${suite}")
|
||||
endif()
|
||||
string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
|
||||
else()
|
||||
# Test name; strip spaces and comments to get just the name...
|
||||
string(REGEX REPLACE " +" "" test "${line}")
|
||||
if(test MATCHES "#" AND NOT _NO_PRETTY_VALUES)
|
||||
string(REGEX REPLACE "/[0-9]+#GetParam..=" "/" pretty_test "${test}")
|
||||
else()
|
||||
string(REGEX REPLACE "#.*" "" pretty_test "${test}")
|
||||
endif()
|
||||
string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}")
|
||||
string(REGEX REPLACE "#.*" "" test "${test}")
|
||||
if(NOT "${_TEST_XML_OUTPUT_DIR}" STREQUAL "")
|
||||
set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml")
|
||||
else()
|
||||
unset(TEST_XML_OUTPUT_PARAM)
|
||||
endif()
|
||||
|
||||
# sanitize test name for further processing downstream
|
||||
set(testname "${prefix}${pretty_suite}.${pretty_test}${suffix}")
|
||||
# escape \
|
||||
string(REPLACE [[\]] [[\\]] testname "${testname}")
|
||||
# escape ;
|
||||
string(REPLACE [[;]] [[\;]] testname "${testname}")
|
||||
# escape $
|
||||
string(REPLACE [[$]] [[\$]] testname "${testname}")
|
||||
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${testname}"
|
||||
${_TEST_EXECUTOR}
|
||||
"${_TEST_EXECUTABLE}"
|
||||
"--gtest_filter=${suite}.${test}"
|
||||
"--gtest_also_run_disabled_tests"
|
||||
${TEST_XML_OUTPUT_PARAM}
|
||||
${extra_args}
|
||||
)
|
||||
if(suite MATCHES "^DISABLED" OR test MATCHES "^DISABLED")
|
||||
add_command(set_tests_properties
|
||||
"${testname}"
|
||||
PROPERTIES DISABLED TRUE
|
||||
)
|
||||
endif()
|
||||
add_command(set_tests_properties
|
||||
"${testname}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
SKIP_REGULAR_EXPRESSION "\\\\[ SKIPPED \\\\]"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests_buffer "${testname}")
|
||||
list(LENGTH tests_buffer tests_buffer_length)
|
||||
if(${tests_buffer_length} GREATER "250")
|
||||
flush_tests_buffer()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
flush_tests_buffer()
|
||||
add_command(set ${_TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
flush_script()
|
||||
|
||||
endfunction()
|
||||
|
||||
if(CMAKE_SCRIPT_MODE_FILE)
|
||||
gtest_discover_tests_impl(
|
||||
NO_PRETTY_TYPES ${NO_PRETTY_TYPES}
|
||||
NO_PRETTY_VALUES ${NO_PRETTY_VALUES}
|
||||
TEST_EXECUTABLE ${TEST_EXECUTABLE}
|
||||
TEST_EXECUTOR ${TEST_EXECUTOR}
|
||||
TEST_WORKING_DIR ${TEST_WORKING_DIR}
|
||||
TEST_PREFIX ${TEST_PREFIX}
|
||||
TEST_SUFFIX ${TEST_SUFFIX}
|
||||
TEST_LIST ${TEST_LIST}
|
||||
CTEST_FILE ${CTEST_FILE}
|
||||
TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
|
||||
TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR}
|
||||
TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
|
||||
TEST_PROPERTIES ${TEST_PROPERTIES}
|
||||
)
|
||||
endif()
|
@@ -8,6 +8,17 @@
|
||||
#
|
||||
#=============================================================================
|
||||
|
||||
function(GET_BLENDER_TEST_INSTALL_DIR VARIABLE_NAME)
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
string(REPLACE "\${BUILD_TYPE}" "$<CONFIG>" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
else()
|
||||
string(REPLACE "\${BUILD_TYPE}" "" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
endif()
|
||||
set(${VARIABLE_NAME} "${TEST_INSTALL_DIR}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
macro(BLENDER_SRC_GTEST_EX)
|
||||
if(WITH_GTESTS)
|
||||
set(options SKIP_ADD_TEST)
|
||||
@@ -75,13 +86,7 @@ macro(BLENDER_SRC_GTEST_EX)
|
||||
target_link_libraries(${TARGET_NAME} ${GMP_LIBRARIES})
|
||||
endif()
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
string(REPLACE "\${BUILD_TYPE}" "$<CONFIG>" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
else()
|
||||
string(REPLACE "\${BUILD_TYPE}" "" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
endif()
|
||||
|
||||
GET_BLENDER_TEST_INSTALL_DIR(TEST_INSTALL_DIR)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${TESTS_OUTPUT_DIR}"
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${TESTS_OUTPUT_DIR}"
|
||||
|
@@ -13,7 +13,7 @@ Invocation:
|
||||
export CLANG_BIND_DIR="/dsk/src/llvm/tools/clang/bindings/python"
|
||||
export CLANG_LIB_DIR="/opt/llvm/lib"
|
||||
|
||||
python2 clang_array_check.py somefile.c -DSOME_DEFINE -I/some/include
|
||||
python clang_array_check.py somefile.c -DSOME_DEFINE -I/some/include
|
||||
|
||||
... defines and includes are optional
|
||||
|
||||
@@ -76,6 +76,32 @@ defs_precalc = {
|
||||
"glNormal3bv": {0: 3},
|
||||
"glNormal3iv": {0: 3},
|
||||
"glNormal3sv": {0: 3},
|
||||
|
||||
# GPU immediate mode.
|
||||
"immVertex2iv": {1: 2},
|
||||
|
||||
"immVertex2fv": {1: 2},
|
||||
"immVertex3fv": {1: 3},
|
||||
|
||||
"immAttr2fv": {1: 2},
|
||||
"immAttr3fv": {1: 3},
|
||||
"immAttr4fv": {1: 4},
|
||||
|
||||
"immAttr3ubv": {1: 3},
|
||||
"immAttr4ubv": {1: 4},
|
||||
|
||||
"immUniform2fv": {1: 2},
|
||||
"immUniform3fv": {1: 3},
|
||||
"immUniform4fv": {1: 4},
|
||||
|
||||
"immUniformColor3fv": {0: 3},
|
||||
"immUniformColor4fv": {0: 4},
|
||||
|
||||
"immUniformColor3ubv": {1: 3},
|
||||
"immUniformColor4ubv": {1: 4},
|
||||
|
||||
"immUniformColor3fvAlpha": {0: 3},
|
||||
"immUniformColor4fvAlpha": {0: 4},
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -100,7 +126,8 @@ else:
|
||||
if CLANG_LIB_DIR is None:
|
||||
print("$CLANG_LIB_DIR clang lib dir not set")
|
||||
|
||||
sys.path.append(CLANG_BIND_DIR)
|
||||
if CLANG_BIND_DIR:
|
||||
sys.path.append(CLANG_BIND_DIR)
|
||||
|
||||
import clang
|
||||
import clang.cindex
|
||||
@@ -108,7 +135,8 @@ from clang.cindex import (CursorKind,
|
||||
TypeKind,
|
||||
TokenKind)
|
||||
|
||||
clang.cindex.Config.set_library_path(CLANG_LIB_DIR)
|
||||
if CLANG_LIB_DIR:
|
||||
clang.cindex.Config.set_library_path(CLANG_LIB_DIR)
|
||||
|
||||
index = clang.cindex.Index.create()
|
||||
|
||||
|
@@ -32,7 +32,7 @@ CHECKER_IGNORE_PREFIX = [
|
||||
"intern/moto",
|
||||
]
|
||||
|
||||
CHECKER_BIN = "python2"
|
||||
CHECKER_BIN = "python3"
|
||||
|
||||
CHECKER_ARGS = [
|
||||
os.path.join(os.path.dirname(__file__), "clang_array_check.py"),
|
||||
|
@@ -388,6 +388,43 @@ function(blender_add_lib
|
||||
set_property(GLOBAL APPEND PROPERTY BLENDER_LINK_LIBS ${name})
|
||||
endfunction()
|
||||
|
||||
function(blender_add_test_suite)
|
||||
if (ARGC LESS 1)
|
||||
message(FATAL_ERROR "No arguments supplied to blender_add_test_suite()")
|
||||
endif()
|
||||
|
||||
# Parse the arguments
|
||||
set(oneValueArgs TARGET SUITE_NAME)
|
||||
set(multiValueArgs SOURCES)
|
||||
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Figure out the release dir, as some tests need files from there.
|
||||
GET_BLENDER_TEST_INSTALL_DIR(TEST_INSTALL_DIR)
|
||||
if(APPLE)
|
||||
set(_test_release_dir ${TEST_INSTALL_DIR}/Blender.app/Contents/Resources/${BLENDER_VERSION})
|
||||
else()
|
||||
if(WIN32 OR WITH_INSTALL_PORTABLE)
|
||||
set(_test_release_dir ${TEST_INSTALL_DIR}/${BLENDER_VERSION})
|
||||
else()
|
||||
set(_test_release_dir ${TEST_INSTALL_DIR}/share/blender/${BLENDER_VERSION})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Define a test case with our custom gtest_add_tests() command.
|
||||
include(GTest)
|
||||
gtest_add_tests(
|
||||
TARGET ${ARGS_TARGET}
|
||||
SOURCES "${ARGS_SOURCES}"
|
||||
TEST_PREFIX ${ARGS_SUITE_NAME}
|
||||
WORKING_DIRECTORY "${TEST_INSTALL_DIR}"
|
||||
EXTRA_ARGS
|
||||
--test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests"
|
||||
--test-release-dir "${_test_release_dir}"
|
||||
)
|
||||
|
||||
unset(_test_release_dir)
|
||||
endfunction()
|
||||
|
||||
# Add tests for a Blender library, to be called in tandem with blender_add_lib().
|
||||
# The tests will be part of the blender_test executable (see tests/gtests/runner).
|
||||
function(blender_add_test_lib
|
||||
@@ -421,6 +458,12 @@ function(blender_add_test_lib
|
||||
blender_add_lib__impl(${name} "${sources}" "${includes}" "${includes_sys}" "${library_deps}")
|
||||
|
||||
set_property(GLOBAL APPEND PROPERTY BLENDER_TEST_LIBS ${name})
|
||||
|
||||
blender_add_test_suite(
|
||||
TARGET blender_test
|
||||
SUITE_NAME ${name}
|
||||
SOURCES "${sources}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
|
||||
@@ -454,14 +497,10 @@ function(blender_add_test_executable
|
||||
SKIP_ADD_TEST
|
||||
)
|
||||
|
||||
include(GTest)
|
||||
set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_SOURCE_DIR}/build_files/cmake/Modules/GTestAddTests.cmake
|
||||
)
|
||||
|
||||
gtest_discover_tests(${name}_test
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
WORKING_DIRECTORY "${TEST_INSTALL_DIR}"
|
||||
blender_add_test_suite(
|
||||
TARGET ${name}_test
|
||||
SUITE_NAME ${name}
|
||||
SOURCES "${sources}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
@@ -1139,6 +1178,7 @@ endfunction()
|
||||
|
||||
function(find_python_package
|
||||
package
|
||||
relative_include_dir
|
||||
)
|
||||
|
||||
string(TOUPPER ${package} _upper_package)
|
||||
@@ -1170,7 +1210,10 @@ function(find_python_package
|
||||
dist-packages
|
||||
vendor-packages
|
||||
NO_DEFAULT_PATH
|
||||
DOC
|
||||
"Path to python site-packages or dist-packages containing '${package}' module"
|
||||
)
|
||||
mark_as_advanced(PYTHON_${_upper_package}_PATH)
|
||||
|
||||
if(NOT EXISTS "${PYTHON_${_upper_package}_PATH}")
|
||||
message(WARNING
|
||||
@@ -1188,6 +1231,50 @@ function(find_python_package
|
||||
set(WITH_PYTHON_INSTALL_${_upper_package} OFF PARENT_SCOPE)
|
||||
else()
|
||||
message(STATUS "${package} found at '${PYTHON_${_upper_package}_PATH}'")
|
||||
|
||||
if(NOT "${relative_include_dir}" STREQUAL "")
|
||||
set(_relative_include_dir "${package}/${relative_include_dir}")
|
||||
unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE)
|
||||
find_path(PYTHON_${_upper_package}_INCLUDE_DIRS
|
||||
NAMES
|
||||
"${_relative_include_dir}"
|
||||
HINTS
|
||||
"${PYTHON_LIBPATH}/"
|
||||
"${PYTHON_LIBPATH}/python${PYTHON_VERSION}/"
|
||||
"${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/"
|
||||
PATH_SUFFIXES
|
||||
"site-packages/"
|
||||
"dist-packages/"
|
||||
"vendor-packages/"
|
||||
NO_DEFAULT_PATH
|
||||
DOC
|
||||
"Path to python site-packages or dist-packages containing '${package}' module header files"
|
||||
)
|
||||
mark_as_advanced(PYTHON_${_upper_package}_INCLUDE_DIRS)
|
||||
|
||||
if(NOT EXISTS "${PYTHON_${_upper_package}_INCLUDE_DIRS}")
|
||||
message(WARNING
|
||||
"Python package '${package}' include dir path could not be found in:\n"
|
||||
"'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/site-packages/${_relative_include_dir}', "
|
||||
"'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/site-packages/${_relative_include_dir}', "
|
||||
"'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/dist-packages/${_relative_include_dir}', "
|
||||
"'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/dist-packages/${_relative_include_dir}', "
|
||||
"'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/vendor-packages/${_relative_include_dir}', "
|
||||
"'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/vendor-packages/${_relative_include_dir}', "
|
||||
"\n"
|
||||
"The 'WITH_PYTHON_${_upper_package}' option will be disabled.\n"
|
||||
"The build will be usable, only add-ons that depend on this package won't be functional."
|
||||
)
|
||||
set(WITH_PYTHON_${_upper_package} OFF PARENT_SCOPE)
|
||||
else()
|
||||
set(_temp "${PYTHON_${_upper_package}_INCLUDE_DIRS}/${package}/${relative_include_dir}")
|
||||
unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE)
|
||||
set(PYTHON_${_upper_package}_INCLUDE_DIRS "${_temp}"
|
||||
CACHE PATH "Path to the include directory of the ${package} module")
|
||||
|
||||
message(STATUS "${package} include files found at '${PYTHON_${_upper_package}_INCLUDE_DIRS}'")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
@@ -470,3 +470,17 @@ 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>")
|
||||
|
||||
if(WITH_COMPILER_CCACHE)
|
||||
if(NOT CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
# Makefiles and ninja
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE STRING "" FORCE)
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE STRING "" FORCE)
|
||||
else()
|
||||
message(WARNING "Ccache NOT found, disabling WITH_COMPILER_CCACHE")
|
||||
set(WITH_COMPILER_CCACHE OFF)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
@@ -154,3 +154,32 @@ if(NOT ${CMAKE_GENERATOR} MATCHES "Xcode")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||
add_definitions("-DMACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||
endif()
|
||||
|
||||
if(WITH_COMPILER_CCACHE)
|
||||
if(CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
get_filename_component(ccompiler "${CMAKE_C_COMPILER}" NAME)
|
||||
get_filename_component(cxxcompiler "${CMAKE_CXX_COMPILER}" NAME)
|
||||
# Ccache can figure out which compiler to use if it's invoked from
|
||||
# a symlink with the name of the compiler.
|
||||
# https://ccache.dev/manual/4.1.html#_run_modes
|
||||
set(_fake_compiler_dir "${CMAKE_BINARY_DIR}/ccache")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${_fake_compiler_dir})
|
||||
set(_fake_C_COMPILER "${_fake_compiler_dir}/${ccompiler}")
|
||||
set(_fake_CXX_COMPILER "${_fake_compiler_dir}/${cxxcompiler}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${CCACHE_PROGRAM}" ${_fake_C_COMPILER})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${CCACHE_PROGRAM}" ${_fake_CXX_COMPILER})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CC ${_fake_C_COMPILER} CACHE STRING "" FORCE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CXX ${_fake_CXX_COMPILER} CACHE STRING "" FORCE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LD ${_fake_C_COMPILER} CACHE STRING "" FORCE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS ${_fake_CXX_COMPILER} CACHE STRING "" FORCE)
|
||||
unset(_fake_compiler_dir)
|
||||
unset(_fake_C_COMPILER)
|
||||
unset(_fake_CXX_COMPILER)
|
||||
else()
|
||||
message(WARNING "Ccache NOT found, disabling WITH_COMPILER_CCACHE")
|
||||
set(WITH_COMPILER_CCACHE OFF)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
@@ -684,3 +684,15 @@ set(PLATFORM_LINKFLAGS
|
||||
if(WITH_INSTALL_PORTABLE)
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -no-pie")
|
||||
endif()
|
||||
|
||||
if(WITH_COMPILER_CCACHE)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
# Makefiles and ninja
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE STRING "" FORCE)
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE STRING "" FORCE)
|
||||
else()
|
||||
message(WARNING "Ccache NOT found, disabling WITH_COMPILER_CCACHE")
|
||||
set(WITH_COMPILER_CCACHE OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
@@ -739,7 +739,7 @@ if(WINDOWS_PYTHON_DEBUG)
|
||||
string(REPLACE "/" "\\" _group_path "${_source_path}")
|
||||
source_group("${_group_path}" FILES "${_source}")
|
||||
endforeach()
|
||||
|
||||
|
||||
# If the user scripts env var is set, include scripts from there otherwise
|
||||
# include user scripts in the profile folder.
|
||||
if(DEFINED ENV{BLENDER_USER_SCRIPTS})
|
||||
@@ -750,7 +750,7 @@ if(WINDOWS_PYTHON_DEBUG)
|
||||
# Include the user scripts from the profile folder in the blender_python_user_scripts project.
|
||||
set(USER_SCRIPTS_ROOT "$ENV{appdata}/blender foundation/blender/${BLENDER_VERSION}/scripts")
|
||||
endif()
|
||||
|
||||
|
||||
file(TO_CMAKE_PATH ${USER_SCRIPTS_ROOT} USER_SCRIPTS_ROOT)
|
||||
FILE(GLOB_RECURSE inFiles "${USER_SCRIPTS_ROOT}/*.*" )
|
||||
ADD_CUSTOM_TARGET(blender_python_user_scripts SOURCES ${inFiles})
|
||||
|
@@ -121,6 +121,10 @@
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
/** \defgroup edasset asset
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
/** \defgroup edcurve curve
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
@@ -163,13 +163,13 @@ Now in the button's context menu select *Copy Data Path*, then paste the result
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.active_object.modifiers["Subsurf"].levels
|
||||
bpy.context.active_object.modifiers["Subdivision"].levels
|
||||
|
||||
Press :kbd:`Return` and you'll get the current value of 1. Now try changing the value to 2:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.active_object.modifiers["Subsurf"].levels = 2
|
||||
bpy.context.active_object.modifiers["Subdivision"].levels = 2
|
||||
|
||||
You can see the value update in the Subdivision Surface modifier's UI as well as the cube.
|
||||
|
||||
@@ -185,43 +185,31 @@ For example, if you want to access the texture of a brush via Python to adjust i
|
||||
#. Start in the default scene and enable Sculpt Mode from the 3D Viewport header.
|
||||
#. From the Sidebar expand the Brush Settings panel's *Texture* subpanel and add a new texture.
|
||||
*Notice the texture data-block menu itself doesn't have very useful links (you can check the tooltips).*
|
||||
#. The contrast setting isn't exposed in the Sidebar, so view the texture in the properties editor:
|
||||
|
||||
- In the properties editor select the Texture tab.
|
||||
- Select brush texture.
|
||||
- Expand the *Colors* panel to locate the *Contrast* number field.
|
||||
#. The contrast setting isn't exposed in the Sidebar, so view the texture in the
|
||||
:ref:`Properties Editor <blender_manual:bpy.types.Texture.contrast`
|
||||
#. Open the context menu of the contrast field and select *Online Python Reference*.
|
||||
This takes you to ``bpy.types.Texture.contrast``. Now you can see that ``contrast`` is a property of texture.
|
||||
#. To find out how to access the texture from the brush check on the references at the bottom of the page.
|
||||
Sometimes there are many references, and it may take some guesswork to find the right one,
|
||||
but in this case it's ``Brush.texture``.
|
||||
|
||||
but in this case it's ``tool_settings.sculpt.brush.texture``.
|
||||
#. Now you know that the texture can be accessed from ``bpy.data.brushes["BrushName"].texture``
|
||||
but normally you *won't* want to access the brush by name, instead you want to access the active brush.
|
||||
So the next step is to check on where brushes are accessed from via the references.
|
||||
In this case there it is simply ``bpy.context.brush``.
|
||||
|
||||
Now you can use the Python console to form the nested properties needed to access brush textures contrast:
|
||||
*Context -> Brush -> Texture -> Contrast*.
|
||||
:menuselection:`Context --> Tool Settings --> Sculpt --> Brush --> Texture --> Contrast`.
|
||||
|
||||
Since the attribute for each is given along the way you can compose the data path in the Python console:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.brush.texture.contrast
|
||||
|
||||
There can be multiple ways to access the same data, which you choose often depends on the task.
|
||||
An alternate path to access the same setting is:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.context.sculpt.brush.texture.contrast
|
||||
bpy.context.tool_settings.sculpt.brush.texture.contrast
|
||||
|
||||
Or access the brush directly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
bpy.data.brushes["BrushName"].texture.contrast
|
||||
bpy.data.textures["Texture"].contrast
|
||||
|
||||
|
||||
If you are writing a user tool normally you want to use the :mod:`bpy.context` since the user normally expects
|
||||
|
@@ -35,12 +35,13 @@ but not to fully cover each topic.
|
||||
|
||||
A quick list of helpful things to know before starting:
|
||||
|
||||
- Blender uses Python 3.x; some online documentation still assumes version 2.x.
|
||||
- The interactive console is great for testing one-liners.
|
||||
It also has autocompletion so you can inspect the API quickly.
|
||||
- Button tooltips show Python attributes and operator names.
|
||||
- The context menu of buttons directly links to this API documentation.
|
||||
- More operator examples can be found in the text editor's template menu.
|
||||
- Enable :ref:`Developer Extra <blender_manual:prefs-interface-dev-extras`
|
||||
and :ref:`Python Tooltips <blender_manual:prefs-interface-tooltips-python>`.
|
||||
- The :ref:`Python Console <blender_manual:bpy.types.SpaceConsole>`
|
||||
is great for testing one-liners; it has autocompletion so you can inspect the API quickly.
|
||||
- Button tooltips show Python attributes and operator names (when enabled see above).
|
||||
- The context menu of buttons directly links to this API documentation (when enabled see above).
|
||||
- Many python examples can be found in the text editor's template menu.
|
||||
- To examine further scripts distributed with Blender, see:
|
||||
|
||||
- ``scripts/startup/bl_ui`` for the user interface.
|
||||
@@ -237,7 +238,7 @@ Examples:
|
||||
{'FINISHED'}
|
||||
>>> bpy.ops.mesh.hide(unselected=False)
|
||||
{'FINISHED'}
|
||||
>>> bpy.ops.object.scale_apply()
|
||||
>>> bpy.ops.object.transform_apply()
|
||||
{'FINISHED'}
|
||||
|
||||
.. tip::
|
||||
|
@@ -24,10 +24,9 @@ The three main use cases for the terminal are:
|
||||
- If the script runs for too long or you accidentally enter an infinite loop,
|
||||
:kbd:`Ctrl-C` in the terminal (:kbd:`Ctrl-Break` on Windows) will quit the script early.
|
||||
|
||||
.. note::
|
||||
.. seealso::
|
||||
|
||||
For Linux and macOS users this means starting the terminal first, then running Blender from within it.
|
||||
On Windows the terminal can be enabled from the Help menu.
|
||||
:ref:`blender_manual:command_line-launch-index`.
|
||||
|
||||
|
||||
Interface Tricks
|
||||
|
@@ -1,7 +1,5 @@
|
||||
/* Prevent Long enum lists */
|
||||
.field-body {
|
||||
display: block;
|
||||
width: 100%;
|
||||
/* T76453: Prevent Long enum lists */
|
||||
.field-list li {
|
||||
max-height: 245px;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
4
extern/README
vendored
Normal file
4
extern/README
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
When updating a library remember to:
|
||||
|
||||
* Update the README.blender with the corresponding version.
|
||||
* Update the THIRD-PARTY-LICENSE.txt document
|
2
extern/audaspace/blender_config.cmake
vendored
2
extern/audaspace/blender_config.cmake
vendored
@@ -24,6 +24,6 @@ set(JACK_FOUND ${WITH_JACK})
|
||||
set(LIBSNDFILE_FOUND ${WITH_CODEC_SNDFILE})
|
||||
set(OPENAL_FOUND ${WITH_OPENAL})
|
||||
set(PYTHONLIBS_FOUND TRUE)
|
||||
set(NUMPY_FOUND TRUE)
|
||||
set(NUMPY_FOUND ${WITH_PYTHON_NUMPY})
|
||||
set(NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_INCLUDE_DIRS})
|
||||
set(SDL_FOUND ${WITH_SDL})
|
||||
|
@@ -72,6 +72,9 @@ protected:
|
||||
/// The channel mapper reader in between.
|
||||
std::shared_ptr<ChannelMapperReader> m_mapper;
|
||||
|
||||
/// Whether the source is being read for the first time.
|
||||
bool m_first_reading;
|
||||
|
||||
/// Whether to keep the source if end of it is reached.
|
||||
bool m_keep;
|
||||
|
||||
|
19
extern/audaspace/src/devices/SoftwareDevice.cpp
vendored
19
extern/audaspace/src/devices/SoftwareDevice.cpp
vendored
@@ -78,7 +78,7 @@ bool SoftwareDevice::SoftwareHandle::pause(bool keep)
|
||||
}
|
||||
|
||||
SoftwareDevice::SoftwareHandle::SoftwareHandle(SoftwareDevice* device, std::shared_ptr<IReader> reader, std::shared_ptr<PitchReader> pitch, std::shared_ptr<ResampleReader> resampler, std::shared_ptr<ChannelMapperReader> mapper, bool keep) :
|
||||
m_reader(reader), m_pitch(pitch), m_resampler(resampler), m_mapper(mapper), m_keep(keep), m_user_pitch(1.0f), m_user_volume(1.0f), m_user_pan(0.0f), m_volume(0.0f), m_old_volume(0.0f), m_loopcount(0),
|
||||
m_reader(reader), m_pitch(pitch), m_resampler(resampler), m_mapper(mapper), m_first_reading(true), m_keep(keep), m_user_pitch(1.0f), m_user_volume(1.0f), m_user_pan(0.0f), m_volume(0.0f), m_old_volume(0.0f), m_loopcount(0),
|
||||
m_relative(true), m_volume_max(1.0f), m_volume_min(0), m_distance_max(std::numeric_limits<float>::max()),
|
||||
m_distance_reference(1.0f), m_attenuation(1.0f), m_cone_angle_outer(M_PI), m_cone_angle_inner(M_PI), m_cone_volume_outer(0),
|
||||
m_flags(RENDER_CONE), m_stop(nullptr), m_stop_data(nullptr), m_status(STATUS_PLAYING), m_device(device)
|
||||
@@ -106,6 +106,14 @@ void SoftwareDevice::SoftwareHandle::update()
|
||||
if(m_pitch->getSpecs().channels != CHANNELS_MONO)
|
||||
{
|
||||
m_volume = m_user_volume;
|
||||
|
||||
// we don't know a previous volume if this source has never been read before
|
||||
if(m_first_reading)
|
||||
{
|
||||
m_old_volume = m_volume;
|
||||
m_first_reading = false;
|
||||
}
|
||||
|
||||
m_pitch->setPitch(m_user_pitch);
|
||||
return;
|
||||
}
|
||||
@@ -214,6 +222,13 @@ void SoftwareDevice::SoftwareHandle::update()
|
||||
m_volume *= m_user_volume;
|
||||
}
|
||||
|
||||
// we don't know a previous volume if this source has never been read before
|
||||
if(m_first_reading)
|
||||
{
|
||||
m_old_volume = m_volume;
|
||||
m_first_reading = false;
|
||||
}
|
||||
|
||||
// 3D Cue
|
||||
|
||||
Quaternion orientation;
|
||||
@@ -754,6 +769,8 @@ void SoftwareDevice::mix(data_t* buffer, int length)
|
||||
{
|
||||
m_mixer->mix(buf, pos, len, sound->m_volume, sound->m_old_volume);
|
||||
|
||||
sound->m_old_volume = sound->m_volume;
|
||||
|
||||
pos += len;
|
||||
|
||||
if(sound->m_loopcount > 0)
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#define KEEP_TIME 10
|
||||
#define POSITION_EPSILON (1.0 / static_cast<double>(RATE_48000))
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
@@ -64,7 +65,7 @@ bool SequenceHandle::updatePosition(double position)
|
||||
if(m_handle.get())
|
||||
{
|
||||
// we currently have a handle, let's check where we are
|
||||
if(position >= m_entry->m_end)
|
||||
if(position - POSITION_EPSILON >= m_entry->m_end)
|
||||
{
|
||||
if(position >= m_entry->m_end + KEEP_TIME)
|
||||
// far end, stopping
|
||||
@@ -76,7 +77,7 @@ bool SequenceHandle::updatePosition(double position)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(position >= m_entry->m_begin)
|
||||
else if(position + POSITION_EPSILON >= m_entry->m_begin)
|
||||
{
|
||||
// inside, resuming
|
||||
m_handle->resume();
|
||||
@@ -98,7 +99,7 @@ bool SequenceHandle::updatePosition(double position)
|
||||
else
|
||||
{
|
||||
// we don't have a handle, let's start if we should be playing
|
||||
if(position >= m_entry->m_begin && position <= m_entry->m_end)
|
||||
if(position + POSITION_EPSILON >= m_entry->m_begin && position - POSITION_EPSILON <= m_entry->m_end)
|
||||
{
|
||||
start();
|
||||
return m_valid;
|
||||
|
1
extern/draco/draco/CMakeLists.txt
vendored
1
extern/draco/draco/CMakeLists.txt
vendored
@@ -268,4 +268,3 @@ set(INC
|
||||
)
|
||||
|
||||
blender_add_lib(draco "${SRC}" "${INC}" "" "${LIB}")
|
||||
|
||||
|
8
extern/mantaflow/CMakeLists.txt
vendored
8
extern/mantaflow/CMakeLists.txt
vendored
@@ -85,7 +85,7 @@ if(WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES)
|
||||
endif()
|
||||
|
||||
if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
|
||||
if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
|
||||
add_definitions(-DNUMPY=1)
|
||||
endif()
|
||||
|
||||
@@ -109,7 +109,7 @@ set(INC_SYS
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
|
||||
if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
|
||||
list(APPEND INC_SYS
|
||||
${PYTHON_NUMPY_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -200,6 +200,7 @@ set(SRC
|
||||
${MANTA_PP}/plugin/ptsplugins.cpp
|
||||
${MANTA_PP}/plugin/secondaryparticles.cpp
|
||||
${MANTA_PP}/plugin/surfaceturbulence.cpp
|
||||
${MANTA_PP}/plugin/viscosity.cpp
|
||||
${MANTA_PP}/plugin/vortexplugins.cpp
|
||||
${MANTA_PP}/plugin/waveletturbulence.cpp
|
||||
${MANTA_PP}/plugin/waves.cpp
|
||||
@@ -255,8 +256,7 @@ if(WITH_MANTA_DEPENDENCIES)
|
||||
${MANTA_DEP}/cnpy/cnpy.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
|
||||
if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
|
||||
list(APPEND SRC
|
||||
${MANTA_PP}/plugin/numpyconvert.cpp
|
||||
${MANTA_PP}/plugin/tfplugins.cpp
|
||||
|
2
extern/mantaflow/helper/util/rcmatrix.h
vendored
2
extern/mantaflow/helper/util/rcmatrix.h
vendored
@@ -1035,7 +1035,7 @@ template<class N, class T> struct RCFixedMatrix {
|
||||
typedef RCMatrix<int, Real> Matrix;
|
||||
typedef RCFixedMatrix<int, Real> FixedMatrix;
|
||||
|
||||
} // namespace Manta
|
||||
}
|
||||
|
||||
#undef parallel_for
|
||||
#undef parallel_end
|
||||
|
94
extern/mantaflow/preprocessed/conjugategrad.cpp
vendored
94
extern/mantaflow/preprocessed/conjugategrad.cpp
vendored
@@ -397,7 +397,7 @@ struct UpdateSearchVec : public KernelBase {
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// CG class
|
||||
// CG class
|
||||
|
||||
template<class APPLYMAT>
|
||||
GridCg<APPLYMAT>::GridCg(Grid<Real> &dst,
|
||||
@@ -406,10 +406,8 @@ GridCg<APPLYMAT>::GridCg(Grid<Real> &dst,
|
||||
Grid<Real> &search,
|
||||
const FlagGrid &flags,
|
||||
Grid<Real> &tmp,
|
||||
Grid<Real> *pA0,
|
||||
Grid<Real> *pAi,
|
||||
Grid<Real> *pAj,
|
||||
Grid<Real> *pAk)
|
||||
std::vector<Grid<Real> *> matrixAVec,
|
||||
std::vector<Grid<Real> *> rhsVec)
|
||||
: GridCgInterface(),
|
||||
mInited(false),
|
||||
mIterations(0),
|
||||
@@ -419,10 +417,8 @@ GridCg<APPLYMAT>::GridCg(Grid<Real> &dst,
|
||||
mSearch(search),
|
||||
mFlags(flags),
|
||||
mTmp(tmp),
|
||||
mpA0(pA0),
|
||||
mpAi(pAi),
|
||||
mpAj(pAj),
|
||||
mpAk(pAk),
|
||||
mMatrixA(matrixAVec),
|
||||
mVecRhs(rhsVec),
|
||||
mPcMethod(PC_None),
|
||||
mpPCA0(nullptr),
|
||||
mpPCAi(nullptr),
|
||||
@@ -445,19 +441,37 @@ template<class APPLYMAT> void GridCg<APPLYMAT>::doInit()
|
||||
|
||||
if (mPcMethod == PC_ICP) {
|
||||
assertMsg(mDst.is3D(), "ICP only supports 3D grids so far");
|
||||
InitPreconditionIncompCholesky(
|
||||
mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk);
|
||||
ApplyPreconditionIncompCholesky(
|
||||
mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk);
|
||||
InitPreconditionIncompCholesky(mFlags,
|
||||
*mpPCA0,
|
||||
*mpPCAi,
|
||||
*mpPCAj,
|
||||
*mpPCAk,
|
||||
*mMatrixA[0],
|
||||
*mMatrixA[1],
|
||||
*mMatrixA[2],
|
||||
*mMatrixA[3]);
|
||||
ApplyPreconditionIncompCholesky(mTmp,
|
||||
mResidual,
|
||||
mFlags,
|
||||
*mpPCA0,
|
||||
*mpPCAi,
|
||||
*mpPCAj,
|
||||
*mpPCAk,
|
||||
*mMatrixA[0],
|
||||
*mMatrixA[1],
|
||||
*mMatrixA[2],
|
||||
*mMatrixA[3]);
|
||||
}
|
||||
else if (mPcMethod == PC_mICP) {
|
||||
assertMsg(mDst.is3D(), "mICP only supports 3D grids so far");
|
||||
InitPreconditionModifiedIncompCholesky2(mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk);
|
||||
InitPreconditionModifiedIncompCholesky2(
|
||||
mFlags, *mpPCA0, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3]);
|
||||
ApplyPreconditionModifiedIncompCholesky2(
|
||||
mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk);
|
||||
mTmp, mResidual, mFlags, *mpPCA0, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3]);
|
||||
}
|
||||
else if (mPcMethod == PC_MGP) {
|
||||
InitPreconditionMultigrid(mMG, *mpA0, *mpAi, *mpAj, *mpAk, mAccuracy);
|
||||
InitPreconditionMultigrid(
|
||||
mMG, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3], mAccuracy);
|
||||
ApplyPreconditionMultigrid(mMG, mTmp, mResidual);
|
||||
}
|
||||
else {
|
||||
@@ -465,7 +479,6 @@ template<class APPLYMAT> void GridCg<APPLYMAT>::doInit()
|
||||
}
|
||||
|
||||
mSearch.copyFrom(mTmp);
|
||||
|
||||
mSigma = GridDotProduct(mTmp, mResidual);
|
||||
}
|
||||
|
||||
@@ -480,7 +493,7 @@ template<class APPLYMAT> bool GridCg<APPLYMAT>::iterate()
|
||||
// this could reinterpret the mpA pointers (not so clean right now)
|
||||
// tmp = applyMat(search)
|
||||
|
||||
APPLYMAT(mFlags, mTmp, mSearch, *mpA0, *mpAi, *mpAj, *mpAk);
|
||||
APPLYMAT(mFlags, mTmp, mSearch, mMatrixA, mVecRhs);
|
||||
|
||||
// alpha = sigma/dot(tmp, search)
|
||||
Real dp = GridDotProduct(mTmp, mSearch);
|
||||
@@ -492,11 +505,20 @@ template<class APPLYMAT> bool GridCg<APPLYMAT>::iterate()
|
||||
gridScaledAdd<Real, Real>(mResidual, mTmp, -alpha); // residual += tmp * -alpha
|
||||
|
||||
if (mPcMethod == PC_ICP)
|
||||
ApplyPreconditionIncompCholesky(
|
||||
mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk);
|
||||
ApplyPreconditionIncompCholesky(mTmp,
|
||||
mResidual,
|
||||
mFlags,
|
||||
*mpPCA0,
|
||||
*mpPCAi,
|
||||
*mpPCAj,
|
||||
*mpPCAk,
|
||||
*mMatrixA[0],
|
||||
*mMatrixA[1],
|
||||
*mMatrixA[2],
|
||||
*mMatrixA[3]);
|
||||
else if (mPcMethod == PC_mICP)
|
||||
ApplyPreconditionModifiedIncompCholesky2(
|
||||
mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk);
|
||||
mTmp, mResidual, mFlags, *mpPCA0, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3]);
|
||||
else if (mPcMethod == PC_MGP)
|
||||
ApplyPreconditionMultigrid(mMG, mTmp, mResidual);
|
||||
else
|
||||
@@ -584,13 +606,15 @@ void GridCg<APPLYMAT>::setMGPreconditioner(PreconditionType method, GridMg *MG)
|
||||
assertMsg(method == PC_MGP, "GridCg<APPLYMAT>::setMGPreconditioner: Invalid method specified.");
|
||||
|
||||
mPcMethod = method;
|
||||
|
||||
mMG = MG;
|
||||
}
|
||||
|
||||
// explicit instantiation
|
||||
template class GridCg<ApplyMatrix>;
|
||||
template class GridCg<ApplyMatrix2D>;
|
||||
template class GridCg<ApplyMatrixViscosityU>;
|
||||
template class GridCg<ApplyMatrixViscosityV>;
|
||||
template class GridCg<ApplyMatrixViscosityW>;
|
||||
|
||||
//*****************************************************************************
|
||||
// diffusion for real and vec grids, e.g. for viscosity
|
||||
@@ -638,10 +662,15 @@ void cgSolveDiffusion(const FlagGrid &flags,
|
||||
if (grid.getType() & GridBase::TypeReal) {
|
||||
Grid<Real> &u = ((Grid<Real> &)grid);
|
||||
rhs.copyFrom(u);
|
||||
if (flags.is3D())
|
||||
gcg = new GridCg<ApplyMatrix>(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
else
|
||||
gcg = new GridCg<ApplyMatrix2D>(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
vector<Grid<Real> *> matA{&A0, &Ai, &Aj};
|
||||
|
||||
if (flags.is3D()) {
|
||||
matA.push_back(&Ak);
|
||||
gcg = new GridCg<ApplyMatrix>(u, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
else {
|
||||
gcg = new GridCg<ApplyMatrix2D>(u, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
|
||||
gcg->setAccuracy(cgAccuracy);
|
||||
gcg->solve(maxIter);
|
||||
@@ -653,12 +682,17 @@ void cgSolveDiffusion(const FlagGrid &flags,
|
||||
else if ((grid.getType() & GridBase::TypeVec3) || (grid.getType() & GridBase::TypeMAC)) {
|
||||
Grid<Vec3> &vec = ((Grid<Vec3> &)grid);
|
||||
Grid<Real> u(parent);
|
||||
vector<Grid<Real> *> matA{&A0, &Ai, &Aj};
|
||||
|
||||
// core solve is same as for a regular real grid
|
||||
if (flags.is3D())
|
||||
gcg = new GridCg<ApplyMatrix>(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
else
|
||||
gcg = new GridCg<ApplyMatrix2D>(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
if (flags.is3D()) {
|
||||
matA.push_back(&Ak);
|
||||
gcg = new GridCg<ApplyMatrix>(u, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
else {
|
||||
gcg = new GridCg<ApplyMatrix2D>(u, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
|
||||
gcg->setAccuracy(cgAccuracy);
|
||||
|
||||
// diffuse every component separately
|
||||
|
468
extern/mantaflow/preprocessed/conjugategrad.h
vendored
468
extern/mantaflow/preprocessed/conjugategrad.h
vendored
@@ -78,13 +78,9 @@ template<class APPLYMAT> class GridCg : public GridCgInterface {
|
||||
Grid<Real> &search,
|
||||
const FlagGrid &flags,
|
||||
Grid<Real> &tmp,
|
||||
Grid<Real> *A0,
|
||||
Grid<Real> *pAi,
|
||||
Grid<Real> *pAj,
|
||||
Grid<Real> *pAk);
|
||||
~GridCg()
|
||||
{
|
||||
}
|
||||
std::vector<Grid<Real> *> matrixAVec,
|
||||
std::vector<Grid<Real> *> rhsVec = {});
|
||||
~GridCg(){};
|
||||
|
||||
void doInit();
|
||||
bool iterate();
|
||||
@@ -133,7 +129,10 @@ template<class APPLYMAT> class GridCg : public GridCgInterface {
|
||||
const FlagGrid &mFlags;
|
||||
Grid<Real> &mTmp;
|
||||
|
||||
Grid<Real> *mpA0, *mpAi, *mpAj, *mpAk;
|
||||
//! shape of A matrix defined here (e.g. diagonal, positive neighbor cells, etc)
|
||||
std::vector<Grid<Real> *> mMatrixA;
|
||||
//! shape of rhs vector defined here (e.g. 1 rhs for regular fluids solve, 3 rhs for viscosity)
|
||||
std::vector<Grid<Real> *> mVecRhs;
|
||||
|
||||
PreconditionType mPcMethod;
|
||||
//! preconditioning grids
|
||||
@@ -154,11 +153,9 @@ struct ApplyMatrix : public KernelBase {
|
||||
ApplyMatrix(const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
Grid<Real> &A0,
|
||||
Grid<Real> &Ai,
|
||||
Grid<Real> &Aj,
|
||||
Grid<Real> &Ak)
|
||||
: KernelBase(&flags, 0), flags(flags), dst(dst), src(src), A0(A0), Ai(Ai), Aj(Aj), Ak(Ak)
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs)
|
||||
: KernelBase(&flags, 0), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs)
|
||||
{
|
||||
runMessage();
|
||||
run();
|
||||
@@ -167,11 +164,18 @@ struct ApplyMatrix : public KernelBase {
|
||||
const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
Grid<Real> &A0,
|
||||
Grid<Real> &Ai,
|
||||
Grid<Real> &Aj,
|
||||
Grid<Real> &Ak) const
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs) const
|
||||
{
|
||||
unusedParameter(vecRhs); // Not needed in this matrix application
|
||||
|
||||
if (matrixA.size() != 4)
|
||||
errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step");
|
||||
Grid<Real> &A0 = *matrixA[0];
|
||||
Grid<Real> &Ai = *matrixA[1];
|
||||
Grid<Real> &Aj = *matrixA[2];
|
||||
Grid<Real> &Ak = *matrixA[3];
|
||||
|
||||
if (!flags.isFluid(idx)) {
|
||||
dst[idx] = src[idx];
|
||||
return;
|
||||
@@ -196,26 +200,16 @@ struct ApplyMatrix : public KernelBase {
|
||||
return src;
|
||||
}
|
||||
typedef Grid<Real> type2;
|
||||
inline Grid<Real> &getArg3()
|
||||
inline const std::vector<Grid<Real> *> &getArg3()
|
||||
{
|
||||
return A0;
|
||||
return matrixA;
|
||||
}
|
||||
typedef Grid<Real> type3;
|
||||
inline Grid<Real> &getArg4()
|
||||
typedef std::vector<Grid<Real> *> type3;
|
||||
inline const std::vector<Grid<Real> *> &getArg4()
|
||||
{
|
||||
return Ai;
|
||||
return vecRhs;
|
||||
}
|
||||
typedef Grid<Real> type4;
|
||||
inline Grid<Real> &getArg5()
|
||||
{
|
||||
return Aj;
|
||||
}
|
||||
typedef Grid<Real> type5;
|
||||
inline Grid<Real> &getArg6()
|
||||
{
|
||||
return Ak;
|
||||
}
|
||||
typedef Grid<Real> type6;
|
||||
typedef std::vector<Grid<Real> *> type4;
|
||||
void runMessage()
|
||||
{
|
||||
debMsg("Executing kernel ApplyMatrix ", 3);
|
||||
@@ -226,7 +220,7 @@ struct ApplyMatrix : public KernelBase {
|
||||
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
||||
{
|
||||
for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
|
||||
op(idx, flags, dst, src, A0, Ai, Aj, Ak);
|
||||
op(idx, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
void run()
|
||||
{
|
||||
@@ -235,10 +229,8 @@ struct ApplyMatrix : public KernelBase {
|
||||
const FlagGrid &flags;
|
||||
Grid<Real> &dst;
|
||||
const Grid<Real> &src;
|
||||
Grid<Real> &A0;
|
||||
Grid<Real> &Ai;
|
||||
Grid<Real> &Aj;
|
||||
Grid<Real> &Ak;
|
||||
const std::vector<Grid<Real> *> matrixA;
|
||||
const std::vector<Grid<Real> *> vecRhs;
|
||||
};
|
||||
|
||||
//! Kernel: Apply symmetric stored Matrix. 2D version
|
||||
@@ -247,11 +239,9 @@ struct ApplyMatrix2D : public KernelBase {
|
||||
ApplyMatrix2D(const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
Grid<Real> &A0,
|
||||
Grid<Real> &Ai,
|
||||
Grid<Real> &Aj,
|
||||
Grid<Real> &Ak)
|
||||
: KernelBase(&flags, 0), flags(flags), dst(dst), src(src), A0(A0), Ai(Ai), Aj(Aj), Ak(Ak)
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs)
|
||||
: KernelBase(&flags, 0), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs)
|
||||
{
|
||||
runMessage();
|
||||
run();
|
||||
@@ -260,12 +250,16 @@ struct ApplyMatrix2D : public KernelBase {
|
||||
const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
Grid<Real> &A0,
|
||||
Grid<Real> &Ai,
|
||||
Grid<Real> &Aj,
|
||||
Grid<Real> &Ak) const
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs) const
|
||||
{
|
||||
unusedParameter(Ak); // only there for parameter compatibility with ApplyMatrix
|
||||
unusedParameter(vecRhs); // Not needed in this matrix application
|
||||
|
||||
if (matrixA.size() != 3)
|
||||
errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step");
|
||||
Grid<Real> &A0 = *matrixA[0];
|
||||
Grid<Real> &Ai = *matrixA[1];
|
||||
Grid<Real> &Aj = *matrixA[2];
|
||||
|
||||
if (!flags.isFluid(idx)) {
|
||||
dst[idx] = src[idx];
|
||||
@@ -290,26 +284,16 @@ struct ApplyMatrix2D : public KernelBase {
|
||||
return src;
|
||||
}
|
||||
typedef Grid<Real> type2;
|
||||
inline Grid<Real> &getArg3()
|
||||
inline const std::vector<Grid<Real> *> &getArg3()
|
||||
{
|
||||
return A0;
|
||||
return matrixA;
|
||||
}
|
||||
typedef Grid<Real> type3;
|
||||
inline Grid<Real> &getArg4()
|
||||
typedef std::vector<Grid<Real> *> type3;
|
||||
inline const std::vector<Grid<Real> *> &getArg4()
|
||||
{
|
||||
return Ai;
|
||||
return vecRhs;
|
||||
}
|
||||
typedef Grid<Real> type4;
|
||||
inline Grid<Real> &getArg5()
|
||||
{
|
||||
return Aj;
|
||||
}
|
||||
typedef Grid<Real> type5;
|
||||
inline Grid<Real> &getArg6()
|
||||
{
|
||||
return Ak;
|
||||
}
|
||||
typedef Grid<Real> type6;
|
||||
typedef std::vector<Grid<Real> *> type4;
|
||||
void runMessage()
|
||||
{
|
||||
debMsg("Executing kernel ApplyMatrix2D ", 3);
|
||||
@@ -320,7 +304,7 @@ struct ApplyMatrix2D : public KernelBase {
|
||||
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
||||
{
|
||||
for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
|
||||
op(idx, flags, dst, src, A0, Ai, Aj, Ak);
|
||||
op(idx, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
void run()
|
||||
{
|
||||
@@ -329,12 +313,358 @@ struct ApplyMatrix2D : public KernelBase {
|
||||
const FlagGrid &flags;
|
||||
Grid<Real> &dst;
|
||||
const Grid<Real> &src;
|
||||
Grid<Real> &A0;
|
||||
Grid<Real> &Ai;
|
||||
Grid<Real> &Aj;
|
||||
Grid<Real> &Ak;
|
||||
const std::vector<Grid<Real> *> matrixA;
|
||||
const std::vector<Grid<Real> *> vecRhs;
|
||||
};
|
||||
|
||||
struct ApplyMatrixViscosityU : public KernelBase {
|
||||
ApplyMatrixViscosityU(const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs)
|
||||
: KernelBase(&flags, 1), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs)
|
||||
{
|
||||
runMessage();
|
||||
run();
|
||||
}
|
||||
inline void op(int i,
|
||||
int j,
|
||||
int k,
|
||||
const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs) const
|
||||
{
|
||||
if (matrixA.size() != 15)
|
||||
errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step");
|
||||
Grid<Real> &A0 = *matrixA[0];
|
||||
Grid<Real> &Aplusi = *matrixA[1];
|
||||
Grid<Real> &Aplusj = *matrixA[2];
|
||||
Grid<Real> &Aplusk = *matrixA[3];
|
||||
Grid<Real> &Aminusi = *matrixA[4];
|
||||
Grid<Real> &Aminusj = *matrixA[5];
|
||||
Grid<Real> &Aminusk = *matrixA[6];
|
||||
|
||||
if (vecRhs.size() != 2)
|
||||
errMsg("ConjugatedGrad: Invalid rhs vector in apply matrix step");
|
||||
Grid<Real> &srcV = *vecRhs[0];
|
||||
Grid<Real> &srcW = *vecRhs[1];
|
||||
|
||||
dst(i, j, k) = src(i, j, k) * A0(i, j, k) + src(i + 1, j, k) * Aplusi(i, j, k) +
|
||||
src(i, j + 1, k) * Aplusj(i, j, k) + src(i, j, k + 1) * Aplusk(i, j, k) +
|
||||
src(i - 1, j, k) * Aminusi(i, j, k) + src(i, j - 1, k) * Aminusj(i, j, k) +
|
||||
src(i, j, k - 1) * Aminusk(i, j, k);
|
||||
|
||||
dst(i, j, k) += srcV(i, j + 1, k) * (*matrixA[7])(i, j, k) +
|
||||
srcV(i - 1, j + 1, k) * (*matrixA[8])(i, j, k) +
|
||||
srcV(i, j, k) * (*matrixA[9])(i, j, k) +
|
||||
srcV(i - 1, j, k) * (*matrixA[10])(i, j, k) +
|
||||
srcW(i, j, k + 1) * (*matrixA[11])(i, j, k) +
|
||||
srcW(i - 1, j, k + 1) * (*matrixA[12])(i, j, k) +
|
||||
srcW(i, j, k) * (*matrixA[13])(i, j, k) +
|
||||
srcW(i - 1, j, k) * (*matrixA[14])(i, j, k);
|
||||
}
|
||||
inline const FlagGrid &getArg0()
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
typedef FlagGrid type0;
|
||||
inline Grid<Real> &getArg1()
|
||||
{
|
||||
return dst;
|
||||
}
|
||||
typedef Grid<Real> type1;
|
||||
inline const Grid<Real> &getArg2()
|
||||
{
|
||||
return src;
|
||||
}
|
||||
typedef Grid<Real> type2;
|
||||
inline const std::vector<Grid<Real> *> &getArg3()
|
||||
{
|
||||
return matrixA;
|
||||
}
|
||||
typedef std::vector<Grid<Real> *> type3;
|
||||
inline const std::vector<Grid<Real> *> &getArg4()
|
||||
{
|
||||
return vecRhs;
|
||||
}
|
||||
typedef std::vector<Grid<Real> *> type4;
|
||||
void runMessage()
|
||||
{
|
||||
debMsg("Executing kernel ApplyMatrixViscosityU ", 3);
|
||||
debMsg("Kernel range"
|
||||
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
||||
4);
|
||||
};
|
||||
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
||||
{
|
||||
const int _maxX = maxX;
|
||||
const int _maxY = maxY;
|
||||
if (maxZ > 1) {
|
||||
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
||||
for (int j = 1; j < _maxY; j++)
|
||||
for (int i = 1; i < _maxX; i++)
|
||||
op(i, j, k, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
else {
|
||||
const int k = 0;
|
||||
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
||||
for (int i = 1; i < _maxX; i++)
|
||||
op(i, j, k, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
}
|
||||
void run()
|
||||
{
|
||||
if (maxZ > 1)
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
||||
else
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
||||
}
|
||||
const FlagGrid &flags;
|
||||
Grid<Real> &dst;
|
||||
const Grid<Real> &src;
|
||||
const std::vector<Grid<Real> *> matrixA;
|
||||
const std::vector<Grid<Real> *> vecRhs;
|
||||
};
|
||||
|
||||
struct ApplyMatrixViscosityV : public KernelBase {
|
||||
ApplyMatrixViscosityV(const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs)
|
||||
: KernelBase(&flags, 1), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs)
|
||||
{
|
||||
runMessage();
|
||||
run();
|
||||
}
|
||||
inline void op(int i,
|
||||
int j,
|
||||
int k,
|
||||
const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs) const
|
||||
{
|
||||
if (matrixA.size() != 15)
|
||||
errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step");
|
||||
Grid<Real> &A0 = *matrixA[0];
|
||||
Grid<Real> &Aplusi = *matrixA[1];
|
||||
Grid<Real> &Aplusj = *matrixA[2];
|
||||
Grid<Real> &Aplusk = *matrixA[3];
|
||||
Grid<Real> &Aminusi = *matrixA[4];
|
||||
Grid<Real> &Aminusj = *matrixA[5];
|
||||
Grid<Real> &Aminusk = *matrixA[6];
|
||||
|
||||
if (vecRhs.size() != 2)
|
||||
errMsg("ConjugatedGrad: Invalid rhs vector in apply matrix step");
|
||||
Grid<Real> &srcU = *vecRhs[0];
|
||||
Grid<Real> &srcW = *vecRhs[1];
|
||||
|
||||
dst(i, j, k) = src(i, j, k) * A0(i, j, k) + src(i + 1, j, k) * Aplusi(i, j, k) +
|
||||
src(i, j + 1, k) * Aplusj(i, j, k) + src(i, j, k + 1) * Aplusk(i, j, k) +
|
||||
src(i - 1, j, k) * Aminusi(i, j, k) + src(i, j - 1, k) * Aminusj(i, j, k) +
|
||||
src(i, j, k - 1) * Aminusk(i, j, k);
|
||||
|
||||
dst(i, j, k) += srcU(i + 1, j, k) * (*matrixA[7])(i, j, k) +
|
||||
srcU(i + 1, j - 1, k) * (*matrixA[8])(i, j, k) +
|
||||
srcU(i, j, k) * (*matrixA[9])(i, j, k) +
|
||||
srcU(i, j - 1, k) * (*matrixA[10])(i, j, k) +
|
||||
srcW(i, j, k + 1) * (*matrixA[11])(i, j, k) +
|
||||
srcW(i, j - 1, k + 1) * (*matrixA[12])(i, j, k) +
|
||||
srcW(i, j, k) * (*matrixA[13])(i, j, k) +
|
||||
srcW(i, j - 1, k) * (*matrixA[14])(i, j, k);
|
||||
}
|
||||
inline const FlagGrid &getArg0()
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
typedef FlagGrid type0;
|
||||
inline Grid<Real> &getArg1()
|
||||
{
|
||||
return dst;
|
||||
}
|
||||
typedef Grid<Real> type1;
|
||||
inline const Grid<Real> &getArg2()
|
||||
{
|
||||
return src;
|
||||
}
|
||||
typedef Grid<Real> type2;
|
||||
inline const std::vector<Grid<Real> *> &getArg3()
|
||||
{
|
||||
return matrixA;
|
||||
}
|
||||
typedef std::vector<Grid<Real> *> type3;
|
||||
inline const std::vector<Grid<Real> *> &getArg4()
|
||||
{
|
||||
return vecRhs;
|
||||
}
|
||||
typedef std::vector<Grid<Real> *> type4;
|
||||
void runMessage()
|
||||
{
|
||||
debMsg("Executing kernel ApplyMatrixViscosityV ", 3);
|
||||
debMsg("Kernel range"
|
||||
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
||||
4);
|
||||
};
|
||||
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
||||
{
|
||||
const int _maxX = maxX;
|
||||
const int _maxY = maxY;
|
||||
if (maxZ > 1) {
|
||||
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
||||
for (int j = 1; j < _maxY; j++)
|
||||
for (int i = 1; i < _maxX; i++)
|
||||
op(i, j, k, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
else {
|
||||
const int k = 0;
|
||||
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
||||
for (int i = 1; i < _maxX; i++)
|
||||
op(i, j, k, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
}
|
||||
void run()
|
||||
{
|
||||
if (maxZ > 1)
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
||||
else
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
||||
}
|
||||
const FlagGrid &flags;
|
||||
Grid<Real> &dst;
|
||||
const Grid<Real> &src;
|
||||
const std::vector<Grid<Real> *> matrixA;
|
||||
const std::vector<Grid<Real> *> vecRhs;
|
||||
};
|
||||
|
||||
struct ApplyMatrixViscosityW : public KernelBase {
|
||||
ApplyMatrixViscosityW(const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs)
|
||||
: KernelBase(&flags, 1), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs)
|
||||
{
|
||||
runMessage();
|
||||
run();
|
||||
}
|
||||
inline void op(int i,
|
||||
int j,
|
||||
int k,
|
||||
const FlagGrid &flags,
|
||||
Grid<Real> &dst,
|
||||
const Grid<Real> &src,
|
||||
const std::vector<Grid<Real> *> matrixA,
|
||||
const std::vector<Grid<Real> *> vecRhs) const
|
||||
{
|
||||
if (matrixA.size() != 15)
|
||||
errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step");
|
||||
Grid<Real> &A0 = *matrixA[0];
|
||||
Grid<Real> &Aplusi = *matrixA[1];
|
||||
Grid<Real> &Aplusj = *matrixA[2];
|
||||
Grid<Real> &Aplusk = *matrixA[3];
|
||||
Grid<Real> &Aminusi = *matrixA[4];
|
||||
Grid<Real> &Aminusj = *matrixA[5];
|
||||
Grid<Real> &Aminusk = *matrixA[6];
|
||||
|
||||
if (vecRhs.size() != 2)
|
||||
errMsg("ConjugatedGrad: Invalid rhs vector in apply matrix step");
|
||||
Grid<Real> &srcU = *vecRhs[0];
|
||||
Grid<Real> &srcV = *vecRhs[1];
|
||||
|
||||
dst(i, j, k) = src(i, j, k) * A0(i, j, k) + src(i + 1, j, k) * Aplusi(i, j, k) +
|
||||
src(i, j + 1, k) * Aplusj(i, j, k) + src(i, j, k + 1) * Aplusk(i, j, k) +
|
||||
src(i - 1, j, k) * Aminusi(i, j, k) + src(i, j - 1, k) * Aminusj(i, j, k) +
|
||||
src(i, j, k - 1) * Aminusk(i, j, k);
|
||||
|
||||
dst(i, j, k) += srcU(i + 1, j, k) * (*matrixA[7])(i, j, k) +
|
||||
srcU(i + 1, j, k - 1) * (*matrixA[8])(i, j, k) +
|
||||
srcU(i, j, k) * (*matrixA[9])(i, j, k) +
|
||||
srcU(i, j, k - 1) * (*matrixA[10])(i, j, k) +
|
||||
srcV(i, j + 1, k) * (*matrixA[11])(i, j, k) +
|
||||
srcV(i, j + 1, k - 1) * (*matrixA[12])(i, j, k) +
|
||||
srcV(i, j, k) * (*matrixA[13])(i, j, k) +
|
||||
srcV(i, j, k - 1) * (*matrixA[14])(i, j, k);
|
||||
}
|
||||
inline const FlagGrid &getArg0()
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
typedef FlagGrid type0;
|
||||
inline Grid<Real> &getArg1()
|
||||
{
|
||||
return dst;
|
||||
}
|
||||
typedef Grid<Real> type1;
|
||||
inline const Grid<Real> &getArg2()
|
||||
{
|
||||
return src;
|
||||
}
|
||||
typedef Grid<Real> type2;
|
||||
inline const std::vector<Grid<Real> *> &getArg3()
|
||||
{
|
||||
return matrixA;
|
||||
}
|
||||
typedef std::vector<Grid<Real> *> type3;
|
||||
inline const std::vector<Grid<Real> *> &getArg4()
|
||||
{
|
||||
return vecRhs;
|
||||
}
|
||||
typedef std::vector<Grid<Real> *> type4;
|
||||
void runMessage()
|
||||
{
|
||||
debMsg("Executing kernel ApplyMatrixViscosityW ", 3);
|
||||
debMsg("Kernel range"
|
||||
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
||||
4);
|
||||
};
|
||||
void operator()(const tbb::blocked_range<IndexInt> &__r) const
|
||||
{
|
||||
const int _maxX = maxX;
|
||||
const int _maxY = maxY;
|
||||
if (maxZ > 1) {
|
||||
for (int k = __r.begin(); k != (int)__r.end(); k++)
|
||||
for (int j = 1; j < _maxY; j++)
|
||||
for (int i = 1; i < _maxX; i++)
|
||||
op(i, j, k, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
else {
|
||||
const int k = 0;
|
||||
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
||||
for (int i = 1; i < _maxX; i++)
|
||||
op(i, j, k, flags, dst, src, matrixA, vecRhs);
|
||||
}
|
||||
}
|
||||
void run()
|
||||
{
|
||||
if (maxZ > 1)
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
||||
else
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(1, maxY), *this);
|
||||
}
|
||||
const FlagGrid &flags;
|
||||
Grid<Real> &dst;
|
||||
const Grid<Real> &src;
|
||||
const std::vector<Grid<Real> *> matrixA;
|
||||
const std::vector<Grid<Real> *> vecRhs;
|
||||
};
|
||||
|
||||
/* NOTE: Use this template for new matrix application kernels
|
||||
|
||||
//! Template for matrix application kernels
|
||||
KERNEL()
|
||||
void ApplyMatrixTemplate (const FlagGrid& flags, Grid<Real>& dst, const Grid<Real>& src,
|
||||
const std::vector<Grid<Real> *> matrixA, const std::vector<Grid<Real> *> vecRhs)
|
||||
{
|
||||
// The kernel must define how to use the grids from the matrixA and vecRhs lists
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//! Kernel: Construct the matrix for the poisson equation
|
||||
|
||||
struct MakeLaplaceMatrix : public KernelBase {
|
||||
|
19
extern/mantaflow/preprocessed/fileio/iovdb.cpp
vendored
19
extern/mantaflow/preprocessed/fileio/iovdb.cpp
vendored
@@ -423,13 +423,14 @@ int writeObjectsVDB(const string &filename,
|
||||
|
||||
if (GridBase *mantaGrid = dynamic_cast<GridBase *>(*iter)) {
|
||||
|
||||
if (clipGrid) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size");
|
||||
}
|
||||
if (mantaGrid->getType() & GridBase::TypeInt) {
|
||||
debMsg("Writing int grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
|
||||
Grid<int> *mantaIntGrid = (Grid<int> *)mantaGrid;
|
||||
if (clipGrid && mantaIntGrid->saveSparse()) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size "
|
||||
<< clipGrid->getSize() << " vs " << mantaGrid->getSize());
|
||||
}
|
||||
vdbGrid = exportVDB<int, openvdb::Int32Grid>(mantaIntGrid, clip, vdbClipGrid);
|
||||
gridsVDB.push_back(vdbGrid);
|
||||
}
|
||||
@@ -440,6 +441,11 @@ int writeObjectsVDB(const string &filename,
|
||||
Grid<Real> *mantaRealGrid = (Grid<Real> *)mantaGrid;
|
||||
// Only supply clip grid if real grid is not equal to the clip grid
|
||||
openvdb::FloatGrid::Ptr tmpClipGrid = (mantaRealGrid == clipGrid) ? nullptr : vdbClipGrid;
|
||||
if (clipGrid && mantaRealGrid->saveSparse()) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size "
|
||||
<< clipGrid->getSize() << " vs " << mantaGrid->getSize());
|
||||
}
|
||||
vdbGrid = exportVDB<Real, openvdb::FloatGrid>(mantaRealGrid, clip, tmpClipGrid);
|
||||
gridsVDB.push_back(vdbGrid);
|
||||
}
|
||||
@@ -448,6 +454,11 @@ int writeObjectsVDB(const string &filename,
|
||||
gClass = (mantaGrid->getType() & GridBase::TypeMAC) ? openvdb::GRID_STAGGERED :
|
||||
openvdb::GRID_UNKNOWN;
|
||||
Grid<Vec3> *mantaVec3Grid = (Grid<Vec3> *)mantaGrid;
|
||||
if (clipGrid && mantaVec3Grid->saveSparse()) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size "
|
||||
<< clipGrid->getSize() << " vs " << mantaGrid->getSize());
|
||||
}
|
||||
vdbGrid = exportVDB<Vec3, openvdb::Vec3SGrid>(mantaVec3Grid, clip, vdbClipGrid);
|
||||
gridsVDB.push_back(vdbGrid);
|
||||
}
|
||||
|
35
extern/mantaflow/preprocessed/general.h
vendored
35
extern/mantaflow/preprocessed/general.h
vendored
@@ -42,7 +42,7 @@ inline void updateQtGui(bool full, int frame, float time, const std::string &cur
|
||||
# ifdef _DEBUG
|
||||
# define DEBUG 1
|
||||
# endif // _DEBUG
|
||||
#endif // DEBUG
|
||||
#endif // DEBUG
|
||||
|
||||
// Standard exception
|
||||
class Error : public std::exception {
|
||||
@@ -242,6 +242,39 @@ inline bool c_isnan(float c)
|
||||
return d != d;
|
||||
}
|
||||
|
||||
//! Swap so that a<b
|
||||
template<class T> inline void sort(T &a, T &b)
|
||||
{
|
||||
if (a > b)
|
||||
std::swap(a, b);
|
||||
}
|
||||
|
||||
//! Swap so that a<b<c
|
||||
template<class T> inline void sort(T &a, T &b, T &c)
|
||||
{
|
||||
if (a > b)
|
||||
std::swap(a, b);
|
||||
if (a > c)
|
||||
std::swap(a, c);
|
||||
if (b > c)
|
||||
std::swap(b, c);
|
||||
}
|
||||
|
||||
//! Swap so that a<b<c<d
|
||||
template<class T> inline void sort(T &a, T &b, T &c, T &d)
|
||||
{
|
||||
if (a > b)
|
||||
std::swap(a, b);
|
||||
if (c > d)
|
||||
std::swap(c, d);
|
||||
if (a > c)
|
||||
std::swap(a, c);
|
||||
if (b > d)
|
||||
std::swap(b, d);
|
||||
if (b > c)
|
||||
std::swap(b, c);
|
||||
}
|
||||
|
||||
} // namespace Manta
|
||||
|
||||
#endif
|
||||
|
2
extern/mantaflow/preprocessed/gitinfo.h
vendored
2
extern/mantaflow/preprocessed/gitinfo.h
vendored
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
#define MANTA_GIT_VERSION "commit 327917cd59b03bef3a953b5f58fc1637b3a83e01"
|
||||
#define MANTA_GIT_VERSION "commit e2285cb9bc492987f728123be6cfc1fe11fe73d6"
|
||||
|
@@ -1135,26 +1135,27 @@ struct KnAddForceIfLower : public KernelBase {
|
||||
if (!curFluid && !curEmpty)
|
||||
return;
|
||||
|
||||
Real minVal, maxVal, sum;
|
||||
if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k))) {
|
||||
Real forceMACX = 0.5 * (force(i - 1, j, k).x + force(i, j, k).x);
|
||||
Real min = std::min(vel(i, j, k).x, forceMACX);
|
||||
Real max = std::max(vel(i, j, k).x, forceMACX);
|
||||
Real sum = vel(i, j, k).x + forceMACX;
|
||||
vel(i, j, k).x = (forceMACX > 0) ? std::min(sum, max) : std::max(sum, min);
|
||||
minVal = min(vel(i, j, k).x, forceMACX);
|
||||
maxVal = max(vel(i, j, k).x, forceMACX);
|
||||
sum = vel(i, j, k).x + forceMACX;
|
||||
vel(i, j, k).x = (forceMACX > 0) ? min(sum, maxVal) : max(sum, minVal);
|
||||
}
|
||||
if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k))) {
|
||||
Real forceMACY = 0.5 * (force(i, j - 1, k).y + force(i, j, k).y);
|
||||
Real min = std::min(vel(i, j, k).y, forceMACY);
|
||||
Real max = std::max(vel(i, j, k).y, forceMACY);
|
||||
Real sum = vel(i, j, k).y + forceMACY;
|
||||
vel(i, j, k).y = (forceMACY > 0) ? std::min(sum, max) : std::max(sum, min);
|
||||
minVal = min(vel(i, j, k).y, forceMACY);
|
||||
maxVal = max(vel(i, j, k).y, forceMACY);
|
||||
sum = vel(i, j, k).y + forceMACY;
|
||||
vel(i, j, k).y = (forceMACY > 0) ? min(sum, maxVal) : max(sum, minVal);
|
||||
}
|
||||
if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1)))) {
|
||||
Real forceMACZ = 0.5 * (force(i, j, k - 1).z + force(i, j, k).z);
|
||||
Real min = std::min(vel(i, j, k).z, forceMACZ);
|
||||
Real max = std::max(vel(i, j, k).z, forceMACZ);
|
||||
Real sum = vel(i, j, k).z + forceMACZ;
|
||||
vel(i, j, k).z = (forceMACZ > 0) ? std::min(sum, max) : std::max(sum, min);
|
||||
minVal = min(vel(i, j, k).z, forceMACZ);
|
||||
maxVal = max(vel(i, j, k).z, forceMACZ);
|
||||
sum = vel(i, j, k).z + forceMACZ;
|
||||
vel(i, j, k).z = (forceMACZ > 0) ? min(sum, maxVal) : max(sum, minVal);
|
||||
}
|
||||
}
|
||||
inline const FlagGrid &getArg0()
|
||||
|
@@ -1138,11 +1138,15 @@ void solvePressureSystem(Grid<Real> &rhs,
|
||||
// note: the last factor increases the max iterations for 2d, which right now can't use a
|
||||
// preconditioner
|
||||
GridCgInterface *gcg;
|
||||
if (vel.is3D())
|
||||
gcg = new GridCg<ApplyMatrix>(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
else
|
||||
gcg = new GridCg<ApplyMatrix2D>(
|
||||
pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
vector<Grid<Real> *> matA{&A0, &Ai, &Aj};
|
||||
|
||||
if (vel.is3D()) {
|
||||
matA.push_back(&Ak);
|
||||
gcg = new GridCg<ApplyMatrix>(pressure, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
else {
|
||||
gcg = new GridCg<ApplyMatrix2D>(pressure, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
|
||||
gcg->setAccuracy(cgAccuracy);
|
||||
gcg->setUseL2Norm(useL2Norm);
|
||||
|
1430
extern/mantaflow/preprocessed/plugin/viscosity.cpp
vendored
Normal file
1430
extern/mantaflow/preprocessed/plugin/viscosity.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -576,8 +576,10 @@ void VICintegration(VortexSheetMesh &mesh,
|
||||
|
||||
// prepare CG solver
|
||||
const int maxIter = (int)(cgMaxIterFac * vel.getSize().max());
|
||||
vector<Grid<Real> *> matA{&A0, &Ai, &Aj, &Ak};
|
||||
|
||||
GridCgInterface *gcg = new GridCg<ApplyMatrix>(
|
||||
solution, rhs, residual, search, flags, temp1, &A0, &Ai, &Aj, &Ak);
|
||||
solution, rhs, residual, search, flags, temp1, matA);
|
||||
gcg->setAccuracy(cgAccuracy);
|
||||
gcg->setUseL2Norm(true);
|
||||
gcg->setICPreconditioner(
|
||||
|
13
extern/mantaflow/preprocessed/plugin/waves.cpp
vendored
13
extern/mantaflow/preprocessed/plugin/waves.cpp
vendored
@@ -423,10 +423,15 @@ void cgSolveWE(const FlagGrid &flags,
|
||||
|
||||
const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4);
|
||||
GridCgInterface *gcg;
|
||||
if (flags.is3D())
|
||||
gcg = new GridCg<ApplyMatrix>(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
else
|
||||
gcg = new GridCg<ApplyMatrix2D>(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak);
|
||||
vector<Grid<Real> *> matA{&A0, &Ai, &Aj};
|
||||
|
||||
if (flags.is3D()) {
|
||||
matA.push_back(&Ak);
|
||||
gcg = new GridCg<ApplyMatrix>(out, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
else {
|
||||
gcg = new GridCg<ApplyMatrix2D>(out, rhs, residual, search, flags, tmp, matA);
|
||||
}
|
||||
|
||||
gcg->setAccuracy(cgAccuracy);
|
||||
|
||||
|
@@ -145,6 +145,7 @@ extern void PbRegister_flipComputeSurfaceNormals();
|
||||
extern void PbRegister_flipUpdateNeighborRatio();
|
||||
extern void PbRegister_particleSurfaceTurbulence();
|
||||
extern void PbRegister_debugCheckParts();
|
||||
extern void PbRegister_applyViscosity();
|
||||
extern void PbRegister_markAsFixed();
|
||||
extern void PbRegister_texcoordInflow();
|
||||
extern void PbRegister_meshSmokeInflow();
|
||||
@@ -342,6 +343,7 @@ void MantaEnsureRegistration()
|
||||
PbRegister_flipUpdateNeighborRatio();
|
||||
PbRegister_particleSurfaceTurbulence();
|
||||
PbRegister_debugCheckParts();
|
||||
PbRegister_applyViscosity();
|
||||
PbRegister_markAsFixed();
|
||||
PbRegister_texcoordInflow();
|
||||
PbRegister_meshSmokeInflow();
|
||||
|
@@ -282,7 +282,7 @@ def list_render_passes(scene, srl):
|
||||
yield ("CryptoAsset" + '{:02d}'.format(i), "RGBA", 'COLOR')
|
||||
|
||||
# Denoising passes.
|
||||
if crl.use_denoising or crl.denoising_store_passes:
|
||||
if (scene.cycles.use_denoising and crl.use_denoising) or crl.denoising_store_passes:
|
||||
yield ("Noisy Image", "RGBA", 'COLOR')
|
||||
if crl.denoising_store_passes:
|
||||
yield ("Denoising Normal", "XYZ", 'VECTOR')
|
||||
|
@@ -1570,7 +1570,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
||||
elif entry.type == 'CPU':
|
||||
cpu_devices.append(entry)
|
||||
# Extend all GPU devices with CPU.
|
||||
if compute_device_type in {'CUDA', 'OPENCL'}:
|
||||
if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}:
|
||||
devices.extend(cpu_devices)
|
||||
return devices
|
||||
|
||||
|
@@ -1148,7 +1148,7 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
|
||||
split = layout.split(factor=0.65)
|
||||
|
||||
if ob:
|
||||
split.template_ID(ob, "active_material", new="material.new")
|
||||
split.template_ID(ob, "active_material", new="material.new", duplicate="material.duplicate")
|
||||
row = split.row()
|
||||
|
||||
if slot:
|
||||
@@ -1443,6 +1443,7 @@ class CYCLES_LIGHT_PT_nodes(CyclesButtonsPanel, Panel):
|
||||
|
||||
class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Spot Shape"
|
||||
bl_parent_id = "CYCLES_LIGHT_PT_light"
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
@@ -1454,7 +1455,6 @@ class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel):
|
||||
layout = self.layout
|
||||
light = context.light
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column()
|
||||
col.prop(light, "spot_size", text="Size")
|
||||
@@ -1969,9 +1969,11 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel):
|
||||
if rd.bake_type == 'DISPLACEMENT':
|
||||
layout.prop(rd, "use_bake_lores_mesh")
|
||||
else:
|
||||
layout.prop(cbk, "target")
|
||||
|
||||
layout.prop(cbk, "margin")
|
||||
layout.prop(cbk, "use_clear", text="Clear Image")
|
||||
if cbk.target == 'IMAGE_TEXTURES':
|
||||
layout.prop(cbk, "margin")
|
||||
layout.prop(cbk, "use_clear", text="Clear Image")
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel):
|
||||
|
@@ -43,42 +43,41 @@ int blender_device_threads(BL::Scene &b_scene)
|
||||
|
||||
DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scene, bool background)
|
||||
{
|
||||
if (BlenderSession::device_override != DEVICE_MASK_ALL) {
|
||||
vector<DeviceInfo> devices = Device::available_devices(BlenderSession::device_override);
|
||||
|
||||
if (devices.empty()) {
|
||||
return Device::dummy_device("Found no Cycles device of the specified type");
|
||||
}
|
||||
|
||||
int threads = blender_device_threads(b_scene);
|
||||
return Device::get_multi_device(devices, threads, background);
|
||||
}
|
||||
|
||||
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
|
||||
|
||||
/* Find cycles preferences. */
|
||||
PointerRNA cpreferences;
|
||||
BL::Preferences::addons_iterator b_addon_iter;
|
||||
for (b_preferences.addons.begin(b_addon_iter); b_addon_iter != b_preferences.addons.end();
|
||||
++b_addon_iter) {
|
||||
if (b_addon_iter->module() == "cycles") {
|
||||
cpreferences = b_addon_iter->preferences().ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default to CPU device. */
|
||||
DeviceInfo device = Device::available_devices(DEVICE_MASK_CPU).front();
|
||||
|
||||
if (get_enum(cscene, "device") == 2) {
|
||||
if (BlenderSession::device_override != DEVICE_MASK_ALL) {
|
||||
vector<DeviceInfo> devices = Device::available_devices(BlenderSession::device_override);
|
||||
|
||||
if (devices.empty()) {
|
||||
device = Device::dummy_device("Found no Cycles device of the specified type");
|
||||
}
|
||||
else {
|
||||
int threads = blender_device_threads(b_scene);
|
||||
device = Device::get_multi_device(devices, threads, background);
|
||||
}
|
||||
}
|
||||
else if (get_enum(cscene, "device") == 2) {
|
||||
/* Find network device. */
|
||||
vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK_NETWORK);
|
||||
if (!devices.empty()) {
|
||||
return devices.front();
|
||||
device = devices.front();
|
||||
}
|
||||
}
|
||||
else if (get_enum(cscene, "device") == 1) {
|
||||
/* Find cycles preferences. */
|
||||
PointerRNA cpreferences;
|
||||
|
||||
BL::Preferences::addons_iterator b_addon_iter;
|
||||
for (b_preferences.addons.begin(b_addon_iter); b_addon_iter != b_preferences.addons.end();
|
||||
++b_addon_iter) {
|
||||
if (b_addon_iter->module() == "cycles") {
|
||||
cpreferences = b_addon_iter->preferences().ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test if we are using GPU devices. */
|
||||
ComputeDevice compute_device = (ComputeDevice)get_enum(
|
||||
cpreferences, "compute_device_type", COMPUTE_DEVICE_NUM, COMPUTE_DEVICE_CPU);
|
||||
@@ -90,8 +89,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
|
||||
mask |= DEVICE_MASK_CUDA;
|
||||
}
|
||||
else if (compute_device == COMPUTE_DEVICE_OPTIX) {
|
||||
/* Cannot use CPU and OptiX device at the same time right now, so replace mask. */
|
||||
mask = DEVICE_MASK_OPTIX;
|
||||
mask |= DEVICE_MASK_OPTIX;
|
||||
}
|
||||
else if (compute_device == COMPUTE_DEVICE_OPENCL) {
|
||||
mask |= DEVICE_MASK_OPENCL;
|
||||
@@ -118,13 +116,13 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
|
||||
device = Device::get_multi_device(used_devices, threads, background);
|
||||
}
|
||||
/* Else keep using the CPU device that was set before. */
|
||||
|
||||
if (!get_boolean(cpreferences, "peer_memory")) {
|
||||
device.has_peer_memory = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!get_boolean(cpreferences, "peer_memory")) {
|
||||
device.has_peer_memory = false;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
|
@@ -351,6 +351,10 @@ static bool lookup_property(BL::ID b_id, const string &name, float4 *r_value)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prop == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyType type = RNA_property_type(prop);
|
||||
int arraylen = RNA_property_array_length(&ptr, prop);
|
||||
|
||||
|
@@ -25,6 +25,7 @@ set(SRC
|
||||
bvh_binning.cpp
|
||||
bvh_build.cpp
|
||||
bvh_embree.cpp
|
||||
bvh_multi.cpp
|
||||
bvh_node.cpp
|
||||
bvh_optix.cpp
|
||||
bvh_sort.cpp
|
||||
@@ -38,6 +39,7 @@ set(SRC_HEADERS
|
||||
bvh_binning.h
|
||||
bvh_build.h
|
||||
bvh_embree.h
|
||||
bvh_multi.h
|
||||
bvh_node.h
|
||||
bvh_optix.h
|
||||
bvh_params.h
|
||||
|
@@ -17,17 +17,11 @@
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
|
||||
#include "render/hair.h"
|
||||
#include "render/mesh.h"
|
||||
#include "render/object.h"
|
||||
|
||||
#include "bvh/bvh2.h"
|
||||
#include "bvh/bvh_build.h"
|
||||
#include "bvh/bvh_embree.h"
|
||||
#include "bvh/bvh_node.h"
|
||||
#include "bvh/bvh_multi.h"
|
||||
#include "bvh/bvh_optix.h"
|
||||
|
||||
#include "util/util_foreach.h"
|
||||
#include "util/util_logging.h"
|
||||
#include "util/util_progress.h"
|
||||
|
||||
@@ -38,14 +32,17 @@ CCL_NAMESPACE_BEGIN
|
||||
const char *bvh_layout_name(BVHLayout layout)
|
||||
{
|
||||
switch (layout) {
|
||||
case BVH_LAYOUT_BVH2:
|
||||
return "BVH2";
|
||||
case BVH_LAYOUT_NONE:
|
||||
return "NONE";
|
||||
case BVH_LAYOUT_BVH2:
|
||||
return "BVH2";
|
||||
case BVH_LAYOUT_EMBREE:
|
||||
return "EMBREE";
|
||||
case BVH_LAYOUT_OPTIX:
|
||||
return "OPTIX";
|
||||
case BVH_LAYOUT_MULTI_OPTIX:
|
||||
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
|
||||
return "MULTI";
|
||||
case BVH_LAYOUT_ALL:
|
||||
return "ALL";
|
||||
}
|
||||
@@ -76,17 +73,6 @@ BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask s
|
||||
return (BVHLayout)(1 << widest_allowed_layout_mask);
|
||||
}
|
||||
|
||||
/* Pack Utility */
|
||||
|
||||
BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i)
|
||||
{
|
||||
}
|
||||
|
||||
int BVHStackEntry::encodeIdx() const
|
||||
{
|
||||
return (node->is_leaf()) ? ~idx : idx;
|
||||
}
|
||||
|
||||
/* BVH */
|
||||
|
||||
BVH::BVH(const BVHParams ¶ms_,
|
||||
@@ -99,24 +85,27 @@ BVH::BVH(const BVHParams ¶ms_,
|
||||
BVH *BVH::create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
const Device *device)
|
||||
Device *device)
|
||||
{
|
||||
switch (params.bvh_layout) {
|
||||
case BVH_LAYOUT_BVH2:
|
||||
return new BVH2(params, geometry, objects);
|
||||
case BVH_LAYOUT_EMBREE:
|
||||
#ifdef WITH_EMBREE
|
||||
return new BVHEmbree(params, geometry, objects, device);
|
||||
return new BVHEmbree(params, geometry, objects);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_OPTIX:
|
||||
#ifdef WITH_OPTIX
|
||||
return new BVHOptiX(params, geometry, objects);
|
||||
return new BVHOptiX(params, geometry, objects, device);
|
||||
#else
|
||||
(void)device;
|
||||
break;
|
||||
#endif
|
||||
case BVH_LAYOUT_MULTI_OPTIX:
|
||||
case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
|
||||
return new BVHMulti(params, geometry, objects);
|
||||
case BVH_LAYOUT_NONE:
|
||||
case BVH_LAYOUT_ALL:
|
||||
break;
|
||||
@@ -125,399 +114,4 @@ BVH *BVH::create(const BVHParams ¶ms,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Building */
|
||||
|
||||
void BVH::build(Progress &progress, Stats *)
|
||||
{
|
||||
progress.set_substatus("Building BVH");
|
||||
|
||||
/* build nodes */
|
||||
BVHBuild bvh_build(objects,
|
||||
pack.prim_type,
|
||||
pack.prim_index,
|
||||
pack.prim_object,
|
||||
pack.prim_time,
|
||||
params,
|
||||
progress);
|
||||
BVHNode *bvh2_root = bvh_build.run();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
if (bvh2_root != NULL) {
|
||||
bvh2_root->deleteSubtree();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* BVH builder returns tree in a binary mode (with two children per inner
|
||||
* node. Need to adopt that for a wider BVH implementations. */
|
||||
BVHNode *root = widen_children_nodes(bvh2_root);
|
||||
if (root != bvh2_root) {
|
||||
bvh2_root->deleteSubtree();
|
||||
}
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
if (root != NULL) {
|
||||
root->deleteSubtree();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack triangles */
|
||||
progress.set_substatus("Packing BVH triangles and strands");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
root->deleteSubtree();
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack nodes */
|
||||
progress.set_substatus("Packing BVH nodes");
|
||||
pack_nodes(root);
|
||||
|
||||
/* free build nodes */
|
||||
root->deleteSubtree();
|
||||
}
|
||||
|
||||
/* Refitting */
|
||||
|
||||
void BVH::refit(Progress &progress)
|
||||
{
|
||||
progress.set_substatus("Packing BVH primitives");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel())
|
||||
return;
|
||||
|
||||
progress.set_substatus("Refitting BVH nodes");
|
||||
refit_nodes();
|
||||
}
|
||||
|
||||
void BVH::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility)
|
||||
{
|
||||
/* Refit range of primitives. */
|
||||
for (int prim = start; prim < end; prim++) {
|
||||
int pidx = pack.prim_index[prim];
|
||||
int tob = pack.prim_object[prim];
|
||||
Object *ob = objects[tob];
|
||||
|
||||
if (pidx == -1) {
|
||||
/* Object instance. */
|
||||
bbox.grow(ob->bounds);
|
||||
}
|
||||
else {
|
||||
/* Primitives. */
|
||||
if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
|
||||
/* Curves. */
|
||||
const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
|
||||
int prim_offset = (params.top_level) ? hair->prim_offset : 0;
|
||||
Hair::Curve curve = hair->get_curve(pidx - prim_offset);
|
||||
int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
|
||||
|
||||
curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox);
|
||||
|
||||
/* Motion curves. */
|
||||
if (hair->get_use_motion_blur()) {
|
||||
Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
|
||||
if (attr) {
|
||||
size_t hair_size = hair->get_curve_keys().size();
|
||||
size_t steps = hair->get_motion_steps() - 1;
|
||||
float3 *key_steps = attr->data_float3();
|
||||
|
||||
for (size_t i = 0; i < steps; i++)
|
||||
curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Triangles. */
|
||||
const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
|
||||
int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
|
||||
Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
|
||||
const float3 *vpos = &mesh->verts[0];
|
||||
|
||||
triangle.bounds_grow(vpos, bbox);
|
||||
|
||||
/* Motion triangles. */
|
||||
if (mesh->use_motion_blur) {
|
||||
Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
|
||||
if (attr) {
|
||||
size_t mesh_size = mesh->verts.size();
|
||||
size_t steps = mesh->motion_steps - 1;
|
||||
float3 *vert_steps = attr->data_float3();
|
||||
|
||||
for (size_t i = 0; i < steps; i++)
|
||||
triangle.bounds_grow(vert_steps + i * mesh_size, bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visibility |= ob->visibility_for_tracing();
|
||||
}
|
||||
}
|
||||
|
||||
/* Triangles */
|
||||
|
||||
void BVH::pack_triangle(int idx, float4 tri_verts[3])
|
||||
{
|
||||
int tob = pack.prim_object[idx];
|
||||
assert(tob >= 0 && tob < objects.size());
|
||||
const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry());
|
||||
|
||||
int tidx = pack.prim_index[idx];
|
||||
Mesh::Triangle t = mesh->get_triangle(tidx);
|
||||
const float3 *vpos = &mesh->verts[0];
|
||||
float3 v0 = vpos[t.v[0]];
|
||||
float3 v1 = vpos[t.v[1]];
|
||||
float3 v2 = vpos[t.v[2]];
|
||||
|
||||
tri_verts[0] = float3_to_float4(v0);
|
||||
tri_verts[1] = float3_to_float4(v1);
|
||||
tri_verts[2] = float3_to_float4(v2);
|
||||
}
|
||||
|
||||
void BVH::pack_primitives()
|
||||
{
|
||||
const size_t tidx_size = pack.prim_index.size();
|
||||
size_t num_prim_triangles = 0;
|
||||
/* Count number of triangles primitives in BVH. */
|
||||
for (unsigned int i = 0; i < tidx_size; i++) {
|
||||
if ((pack.prim_index[i] != -1)) {
|
||||
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
|
||||
++num_prim_triangles;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Reserve size for arrays. */
|
||||
pack.prim_tri_index.clear();
|
||||
pack.prim_tri_index.resize(tidx_size);
|
||||
pack.prim_tri_verts.clear();
|
||||
pack.prim_tri_verts.resize(num_prim_triangles * 3);
|
||||
pack.prim_visibility.clear();
|
||||
pack.prim_visibility.resize(tidx_size);
|
||||
/* Fill in all the arrays. */
|
||||
size_t prim_triangle_index = 0;
|
||||
for (unsigned int i = 0; i < tidx_size; i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
int tob = pack.prim_object[i];
|
||||
Object *ob = objects[tob];
|
||||
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
|
||||
pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]);
|
||||
pack.prim_tri_index[i] = 3 * prim_triangle_index;
|
||||
++prim_triangle_index;
|
||||
}
|
||||
else {
|
||||
pack.prim_tri_index[i] = -1;
|
||||
}
|
||||
pack.prim_visibility[i] = ob->visibility_for_tracing();
|
||||
}
|
||||
else {
|
||||
pack.prim_tri_index[i] = -1;
|
||||
pack.prim_visibility[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack Instances */
|
||||
|
||||
void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
{
|
||||
/* Adjust primitive index to point to the triangle in the global array, for
|
||||
* geometry with transform applied and already in the top level BVH.
|
||||
*/
|
||||
for (size_t i = 0; i < pack.prim_index.size(); i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* track offsets of instanced BVH data in global array */
|
||||
size_t prim_offset = pack.prim_index.size();
|
||||
size_t nodes_offset = nodes_size;
|
||||
size_t nodes_leaf_offset = leaf_nodes_size;
|
||||
|
||||
/* clear array that gives the node indexes for instanced objects */
|
||||
pack.object_node.clear();
|
||||
|
||||
/* reserve */
|
||||
size_t prim_index_size = pack.prim_index.size();
|
||||
size_t prim_tri_verts_size = pack.prim_tri_verts.size();
|
||||
|
||||
size_t pack_prim_index_offset = prim_index_size;
|
||||
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
|
||||
size_t pack_nodes_offset = nodes_size;
|
||||
size_t pack_leaf_nodes_offset = leaf_nodes_size;
|
||||
size_t object_offset = 0;
|
||||
|
||||
foreach (Geometry *geom, geometry) {
|
||||
BVH *bvh = geom->bvh;
|
||||
|
||||
if (geom->need_build_bvh(params.bvh_layout)) {
|
||||
prim_index_size += bvh->pack.prim_index.size();
|
||||
prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
|
||||
nodes_size += bvh->pack.nodes.size();
|
||||
leaf_nodes_size += bvh->pack.leaf_nodes.size();
|
||||
}
|
||||
}
|
||||
|
||||
pack.prim_index.resize(prim_index_size);
|
||||
pack.prim_type.resize(prim_index_size);
|
||||
pack.prim_object.resize(prim_index_size);
|
||||
pack.prim_visibility.resize(prim_index_size);
|
||||
pack.prim_tri_verts.resize(prim_tri_verts_size);
|
||||
pack.prim_tri_index.resize(prim_index_size);
|
||||
pack.nodes.resize(nodes_size);
|
||||
pack.leaf_nodes.resize(leaf_nodes_size);
|
||||
pack.object_node.resize(objects.size());
|
||||
|
||||
if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
|
||||
pack.prim_time.resize(prim_index_size);
|
||||
}
|
||||
|
||||
int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
|
||||
int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
|
||||
int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
|
||||
uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL;
|
||||
float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
|
||||
uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
|
||||
int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL;
|
||||
int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL;
|
||||
float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL;
|
||||
|
||||
map<Geometry *, int> geometry_map;
|
||||
|
||||
/* merge */
|
||||
foreach (Object *ob, objects) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
/* We assume that if mesh doesn't need own BVH it was already included
|
||||
* into a top-level BVH and no packing here is needed.
|
||||
*/
|
||||
if (!geom->need_build_bvh(params.bvh_layout)) {
|
||||
pack.object_node[object_offset++] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if mesh already added once, don't add it again, but used set
|
||||
* node offset for this object */
|
||||
map<Geometry *, int>::iterator it = geometry_map.find(geom);
|
||||
|
||||
if (geometry_map.find(geom) != geometry_map.end()) {
|
||||
int noffset = it->second;
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
continue;
|
||||
}
|
||||
|
||||
BVH *bvh = geom->bvh;
|
||||
|
||||
int noffset = nodes_offset;
|
||||
int noffset_leaf = nodes_leaf_offset;
|
||||
int geom_prim_offset = geom->prim_offset;
|
||||
|
||||
/* fill in node indexes for instances */
|
||||
if (bvh->pack.root_index == -1)
|
||||
pack.object_node[object_offset++] = -noffset_leaf - 1;
|
||||
else
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
|
||||
geometry_map[geom] = pack.object_node[object_offset - 1];
|
||||
|
||||
/* merge primitive, object and triangle indexes */
|
||||
if (bvh->pack.prim_index.size()) {
|
||||
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
|
||||
int *bvh_prim_index = &bvh->pack.prim_index[0];
|
||||
int *bvh_prim_type = &bvh->pack.prim_type[0];
|
||||
uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
|
||||
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
|
||||
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL;
|
||||
|
||||
for (size_t i = 0; i < bvh_prim_index_size; i++) {
|
||||
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_prim_index_offset] = -1;
|
||||
}
|
||||
else {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
|
||||
pack_prim_tri_verts_offset;
|
||||
}
|
||||
|
||||
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
|
||||
pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
|
||||
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
|
||||
if (bvh_prim_time != NULL) {
|
||||
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
|
||||
}
|
||||
pack_prim_index_offset++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge triangle vertices data. */
|
||||
if (bvh->pack.prim_tri_verts.size()) {
|
||||
const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
|
||||
memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
|
||||
&bvh->pack.prim_tri_verts[0],
|
||||
prim_tri_size * sizeof(float4));
|
||||
pack_prim_tri_verts_offset += prim_tri_size;
|
||||
}
|
||||
|
||||
/* merge nodes */
|
||||
if (bvh->pack.leaf_nodes.size()) {
|
||||
int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0];
|
||||
size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
|
||||
for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) {
|
||||
int4 data = leaf_nodes_offset[i];
|
||||
data.x += prim_offset;
|
||||
data.y += prim_offset;
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset] = data;
|
||||
for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) {
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j];
|
||||
}
|
||||
pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bvh->pack.nodes.size()) {
|
||||
int4 *bvh_nodes = &bvh->pack.nodes[0];
|
||||
size_t bvh_nodes_size = bvh->pack.nodes.size();
|
||||
|
||||
for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
|
||||
size_t nsize, nsize_bbox;
|
||||
if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) {
|
||||
nsize = BVH_UNALIGNED_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
else {
|
||||
nsize = BVH_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
|
||||
memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4));
|
||||
|
||||
/* Modify offsets into arrays */
|
||||
int4 data = bvh_nodes[i + nsize_bbox];
|
||||
data.z += (data.z < 0) ? -noffset_leaf : noffset;
|
||||
data.w += (data.w < 0) ? -noffset_leaf : noffset;
|
||||
pack_nodes[pack_nodes_offset + nsize_bbox] = data;
|
||||
|
||||
/* Usually this copies nothing, but we better
|
||||
* be prepared for possible node size extension.
|
||||
*/
|
||||
memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1],
|
||||
&bvh_nodes[i + nsize_bbox + 1],
|
||||
sizeof(int4) * (nsize - (nsize_bbox + 1)));
|
||||
|
||||
pack_nodes_offset += nsize;
|
||||
i += nsize;
|
||||
}
|
||||
}
|
||||
|
||||
nodes_offset += bvh->pack.nodes.size();
|
||||
nodes_leaf_offset += bvh->pack.leaf_nodes.size();
|
||||
prim_offset += bvh->pack.prim_index.size();
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -25,17 +25,16 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Stats;
|
||||
class BoundBox;
|
||||
class BVHNode;
|
||||
class BVHParams;
|
||||
class Device;
|
||||
class DeviceScene;
|
||||
class BVHNode;
|
||||
struct BVHStackEntry;
|
||||
class BVHParams;
|
||||
class BoundBox;
|
||||
class LeafNode;
|
||||
class Geometry;
|
||||
class LeafNode;
|
||||
class Object;
|
||||
class Progress;
|
||||
class Stats;
|
||||
|
||||
#define BVH_ALIGN 4096
|
||||
#define TRI_NODE_SIZE 3
|
||||
@@ -76,13 +75,10 @@ struct PackedBVH {
|
||||
}
|
||||
};
|
||||
|
||||
enum BVH_TYPE { bvh2 };
|
||||
|
||||
/* BVH */
|
||||
|
||||
class BVH {
|
||||
public:
|
||||
PackedBVH pack;
|
||||
BVHParams params;
|
||||
vector<Geometry *> geometry;
|
||||
vector<Object *> objects;
|
||||
@@ -90,47 +86,15 @@ class BVH {
|
||||
static BVH *create(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
const Device *device);
|
||||
Device *device);
|
||||
virtual ~BVH()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void build(Progress &progress, Stats *stats = NULL);
|
||||
virtual void copy_to_device(Progress & /*progress*/, DeviceScene * /*dscene*/)
|
||||
{
|
||||
}
|
||||
|
||||
void refit(Progress &progress);
|
||||
|
||||
protected:
|
||||
BVH(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
|
||||
/* Refit range of primitives. */
|
||||
void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility);
|
||||
|
||||
/* triangles and strands */
|
||||
void pack_primitives();
|
||||
void pack_triangle(int idx, float4 storage[3]);
|
||||
|
||||
/* merge instance BVH's */
|
||||
void pack_instances(size_t nodes_size, size_t leaf_nodes_size);
|
||||
|
||||
/* for subclasses to implement */
|
||||
virtual void pack_nodes(const BVHNode *root) = 0;
|
||||
virtual void refit_nodes() = 0;
|
||||
|
||||
virtual BVHNode *widen_children_nodes(const BVHNode *root) = 0;
|
||||
};
|
||||
|
||||
/* Pack Utility */
|
||||
struct BVHStackEntry {
|
||||
const BVHNode *node;
|
||||
int idx;
|
||||
|
||||
BVHStackEntry(const BVHNode *n = 0, int i = 0);
|
||||
int encodeIdx() const;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -17,14 +17,28 @@
|
||||
|
||||
#include "bvh/bvh2.h"
|
||||
|
||||
#include "render/hair.h"
|
||||
#include "render/mesh.h"
|
||||
#include "render/object.h"
|
||||
|
||||
#include "bvh/bvh_build.h"
|
||||
#include "bvh/bvh_node.h"
|
||||
#include "bvh/bvh_unaligned.h"
|
||||
|
||||
#include "util/util_foreach.h"
|
||||
#include "util/util_progress.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i)
|
||||
{
|
||||
}
|
||||
|
||||
int BVHStackEntry::encodeIdx() const
|
||||
{
|
||||
return (node->is_leaf()) ? ~idx : idx;
|
||||
}
|
||||
|
||||
BVH2::BVH2(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
@@ -32,6 +46,70 @@ BVH2::BVH2(const BVHParams ¶ms_,
|
||||
{
|
||||
}
|
||||
|
||||
void BVH2::build(Progress &progress, Stats *)
|
||||
{
|
||||
progress.set_substatus("Building BVH");
|
||||
|
||||
/* build nodes */
|
||||
BVHBuild bvh_build(objects,
|
||||
pack.prim_type,
|
||||
pack.prim_index,
|
||||
pack.prim_object,
|
||||
pack.prim_time,
|
||||
params,
|
||||
progress);
|
||||
BVHNode *bvh2_root = bvh_build.run();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
if (bvh2_root != NULL) {
|
||||
bvh2_root->deleteSubtree();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* BVH builder returns tree in a binary mode (with two children per inner
|
||||
* node. Need to adopt that for a wider BVH implementations. */
|
||||
BVHNode *root = widen_children_nodes(bvh2_root);
|
||||
if (root != bvh2_root) {
|
||||
bvh2_root->deleteSubtree();
|
||||
}
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
if (root != NULL) {
|
||||
root->deleteSubtree();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack triangles */
|
||||
progress.set_substatus("Packing BVH triangles and strands");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
root->deleteSubtree();
|
||||
return;
|
||||
}
|
||||
|
||||
/* pack nodes */
|
||||
progress.set_substatus("Packing BVH nodes");
|
||||
pack_nodes(root);
|
||||
|
||||
/* free build nodes */
|
||||
root->deleteSubtree();
|
||||
}
|
||||
|
||||
void BVH2::refit(Progress &progress)
|
||||
{
|
||||
progress.set_substatus("Packing BVH primitives");
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel())
|
||||
return;
|
||||
|
||||
progress.set_substatus("Refitting BVH nodes");
|
||||
refit_nodes();
|
||||
}
|
||||
|
||||
BVHNode *BVH2::widen_children_nodes(const BVHNode *root)
|
||||
{
|
||||
return const_cast<BVHNode *>(root);
|
||||
@@ -253,7 +331,7 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
|
||||
const int c0 = data[0].x;
|
||||
const int c1 = data[0].y;
|
||||
|
||||
BVH::refit_primitives(c0, c1, bbox, visibility);
|
||||
refit_primitives(c0, c1, bbox, visibility);
|
||||
|
||||
/* TODO(sergey): De-duplicate with pack_leaf(). */
|
||||
float4 leaf_data[BVH_NODE_LEAF_SIZE];
|
||||
@@ -292,4 +370,333 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
|
||||
}
|
||||
}
|
||||
|
||||
/* Refitting */
|
||||
|
||||
void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility)
|
||||
{
|
||||
/* Refit range of primitives. */
|
||||
for (int prim = start; prim < end; prim++) {
|
||||
int pidx = pack.prim_index[prim];
|
||||
int tob = pack.prim_object[prim];
|
||||
Object *ob = objects[tob];
|
||||
|
||||
if (pidx == -1) {
|
||||
/* Object instance. */
|
||||
bbox.grow(ob->bounds);
|
||||
}
|
||||
else {
|
||||
/* Primitives. */
|
||||
if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
|
||||
/* Curves. */
|
||||
const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
|
||||
int prim_offset = (params.top_level) ? hair->prim_offset : 0;
|
||||
Hair::Curve curve = hair->get_curve(pidx - prim_offset);
|
||||
int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
|
||||
|
||||
curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox);
|
||||
|
||||
/* Motion curves. */
|
||||
if (hair->get_use_motion_blur()) {
|
||||
Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
|
||||
if (attr) {
|
||||
size_t hair_size = hair->get_curve_keys().size();
|
||||
size_t steps = hair->get_motion_steps() - 1;
|
||||
float3 *key_steps = attr->data_float3();
|
||||
|
||||
for (size_t i = 0; i < steps; i++)
|
||||
curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Triangles. */
|
||||
const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
|
||||
int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
|
||||
Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
|
||||
const float3 *vpos = &mesh->verts[0];
|
||||
|
||||
triangle.bounds_grow(vpos, bbox);
|
||||
|
||||
/* Motion triangles. */
|
||||
if (mesh->use_motion_blur) {
|
||||
Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
|
||||
if (attr) {
|
||||
size_t mesh_size = mesh->verts.size();
|
||||
size_t steps = mesh->motion_steps - 1;
|
||||
float3 *vert_steps = attr->data_float3();
|
||||
|
||||
for (size_t i = 0; i < steps; i++)
|
||||
triangle.bounds_grow(vert_steps + i * mesh_size, bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visibility |= ob->visibility_for_tracing();
|
||||
}
|
||||
}
|
||||
|
||||
/* Triangles */
|
||||
|
||||
void BVH2::pack_triangle(int idx, float4 tri_verts[3])
|
||||
{
|
||||
int tob = pack.prim_object[idx];
|
||||
assert(tob >= 0 && tob < objects.size());
|
||||
const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry());
|
||||
|
||||
int tidx = pack.prim_index[idx];
|
||||
Mesh::Triangle t = mesh->get_triangle(tidx);
|
||||
const float3 *vpos = &mesh->verts[0];
|
||||
float3 v0 = vpos[t.v[0]];
|
||||
float3 v1 = vpos[t.v[1]];
|
||||
float3 v2 = vpos[t.v[2]];
|
||||
|
||||
tri_verts[0] = float3_to_float4(v0);
|
||||
tri_verts[1] = float3_to_float4(v1);
|
||||
tri_verts[2] = float3_to_float4(v2);
|
||||
}
|
||||
|
||||
void BVH2::pack_primitives()
|
||||
{
|
||||
const size_t tidx_size = pack.prim_index.size();
|
||||
size_t num_prim_triangles = 0;
|
||||
/* Count number of triangles primitives in BVH. */
|
||||
for (unsigned int i = 0; i < tidx_size; i++) {
|
||||
if ((pack.prim_index[i] != -1)) {
|
||||
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
|
||||
++num_prim_triangles;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Reserve size for arrays. */
|
||||
pack.prim_tri_index.clear();
|
||||
pack.prim_tri_index.resize(tidx_size);
|
||||
pack.prim_tri_verts.clear();
|
||||
pack.prim_tri_verts.resize(num_prim_triangles * 3);
|
||||
pack.prim_visibility.clear();
|
||||
pack.prim_visibility.resize(tidx_size);
|
||||
/* Fill in all the arrays. */
|
||||
size_t prim_triangle_index = 0;
|
||||
for (unsigned int i = 0; i < tidx_size; i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
int tob = pack.prim_object[i];
|
||||
Object *ob = objects[tob];
|
||||
if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
|
||||
pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]);
|
||||
pack.prim_tri_index[i] = 3 * prim_triangle_index;
|
||||
++prim_triangle_index;
|
||||
}
|
||||
else {
|
||||
pack.prim_tri_index[i] = -1;
|
||||
}
|
||||
pack.prim_visibility[i] = ob->visibility_for_tracing();
|
||||
}
|
||||
else {
|
||||
pack.prim_tri_index[i] = -1;
|
||||
pack.prim_visibility[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack Instances */
|
||||
|
||||
void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
{
|
||||
/* Adjust primitive index to point to the triangle in the global array, for
|
||||
* geometry with transform applied and already in the top level BVH.
|
||||
*/
|
||||
for (size_t i = 0; i < pack.prim_index.size(); i++) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* track offsets of instanced BVH data in global array */
|
||||
size_t prim_offset = pack.prim_index.size();
|
||||
size_t nodes_offset = nodes_size;
|
||||
size_t nodes_leaf_offset = leaf_nodes_size;
|
||||
|
||||
/* clear array that gives the node indexes for instanced objects */
|
||||
pack.object_node.clear();
|
||||
|
||||
/* reserve */
|
||||
size_t prim_index_size = pack.prim_index.size();
|
||||
size_t prim_tri_verts_size = pack.prim_tri_verts.size();
|
||||
|
||||
size_t pack_prim_index_offset = prim_index_size;
|
||||
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
|
||||
size_t pack_nodes_offset = nodes_size;
|
||||
size_t pack_leaf_nodes_offset = leaf_nodes_size;
|
||||
size_t object_offset = 0;
|
||||
|
||||
foreach (Geometry *geom, geometry) {
|
||||
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh);
|
||||
|
||||
if (geom->need_build_bvh(params.bvh_layout)) {
|
||||
prim_index_size += bvh->pack.prim_index.size();
|
||||
prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
|
||||
nodes_size += bvh->pack.nodes.size();
|
||||
leaf_nodes_size += bvh->pack.leaf_nodes.size();
|
||||
}
|
||||
}
|
||||
|
||||
pack.prim_index.resize(prim_index_size);
|
||||
pack.prim_type.resize(prim_index_size);
|
||||
pack.prim_object.resize(prim_index_size);
|
||||
pack.prim_visibility.resize(prim_index_size);
|
||||
pack.prim_tri_verts.resize(prim_tri_verts_size);
|
||||
pack.prim_tri_index.resize(prim_index_size);
|
||||
pack.nodes.resize(nodes_size);
|
||||
pack.leaf_nodes.resize(leaf_nodes_size);
|
||||
pack.object_node.resize(objects.size());
|
||||
|
||||
if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
|
||||
pack.prim_time.resize(prim_index_size);
|
||||
}
|
||||
|
||||
int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
|
||||
int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
|
||||
int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
|
||||
uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL;
|
||||
float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
|
||||
uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
|
||||
int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL;
|
||||
int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL;
|
||||
float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL;
|
||||
|
||||
unordered_map<Geometry *, int> geometry_map;
|
||||
|
||||
/* merge */
|
||||
foreach (Object *ob, objects) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
/* We assume that if mesh doesn't need own BVH it was already included
|
||||
* into a top-level BVH and no packing here is needed.
|
||||
*/
|
||||
if (!geom->need_build_bvh(params.bvh_layout)) {
|
||||
pack.object_node[object_offset++] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if mesh already added once, don't add it again, but used set
|
||||
* node offset for this object */
|
||||
unordered_map<Geometry *, int>::iterator it = geometry_map.find(geom);
|
||||
|
||||
if (geometry_map.find(geom) != geometry_map.end()) {
|
||||
int noffset = it->second;
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
continue;
|
||||
}
|
||||
|
||||
BVH2 *bvh = static_cast<BVH2 *>(geom->bvh);
|
||||
|
||||
int noffset = nodes_offset;
|
||||
int noffset_leaf = nodes_leaf_offset;
|
||||
int geom_prim_offset = geom->prim_offset;
|
||||
|
||||
/* fill in node indexes for instances */
|
||||
if (bvh->pack.root_index == -1)
|
||||
pack.object_node[object_offset++] = -noffset_leaf - 1;
|
||||
else
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
|
||||
geometry_map[geom] = pack.object_node[object_offset - 1];
|
||||
|
||||
/* merge primitive, object and triangle indexes */
|
||||
if (bvh->pack.prim_index.size()) {
|
||||
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
|
||||
int *bvh_prim_index = &bvh->pack.prim_index[0];
|
||||
int *bvh_prim_type = &bvh->pack.prim_type[0];
|
||||
uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
|
||||
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
|
||||
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL;
|
||||
|
||||
for (size_t i = 0; i < bvh_prim_index_size; i++) {
|
||||
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_prim_index_offset] = -1;
|
||||
}
|
||||
else {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
|
||||
pack_prim_tri_verts_offset;
|
||||
}
|
||||
|
||||
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
|
||||
pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
|
||||
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
|
||||
if (bvh_prim_time != NULL) {
|
||||
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
|
||||
}
|
||||
pack_prim_index_offset++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge triangle vertices data. */
|
||||
if (bvh->pack.prim_tri_verts.size()) {
|
||||
const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
|
||||
memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
|
||||
&bvh->pack.prim_tri_verts[0],
|
||||
prim_tri_size * sizeof(float4));
|
||||
pack_prim_tri_verts_offset += prim_tri_size;
|
||||
}
|
||||
|
||||
/* merge nodes */
|
||||
if (bvh->pack.leaf_nodes.size()) {
|
||||
int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0];
|
||||
size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
|
||||
for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) {
|
||||
int4 data = leaf_nodes_offset[i];
|
||||
data.x += prim_offset;
|
||||
data.y += prim_offset;
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset] = data;
|
||||
for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) {
|
||||
pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j];
|
||||
}
|
||||
pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bvh->pack.nodes.size()) {
|
||||
int4 *bvh_nodes = &bvh->pack.nodes[0];
|
||||
size_t bvh_nodes_size = bvh->pack.nodes.size();
|
||||
|
||||
for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
|
||||
size_t nsize, nsize_bbox;
|
||||
if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) {
|
||||
nsize = BVH_UNALIGNED_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
else {
|
||||
nsize = BVH_NODE_SIZE;
|
||||
nsize_bbox = 0;
|
||||
}
|
||||
|
||||
memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4));
|
||||
|
||||
/* Modify offsets into arrays */
|
||||
int4 data = bvh_nodes[i + nsize_bbox];
|
||||
data.z += (data.z < 0) ? -noffset_leaf : noffset;
|
||||
data.w += (data.w < 0) ? -noffset_leaf : noffset;
|
||||
pack_nodes[pack_nodes_offset + nsize_bbox] = data;
|
||||
|
||||
/* Usually this copies nothing, but we better
|
||||
* be prepared for possible node size extension.
|
||||
*/
|
||||
memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1],
|
||||
&bvh_nodes[i + nsize_bbox + 1],
|
||||
sizeof(int4) * (nsize - (nsize_bbox + 1)));
|
||||
|
||||
pack_nodes_offset += nsize;
|
||||
i += nsize;
|
||||
}
|
||||
}
|
||||
|
||||
nodes_offset += bvh->pack.nodes.size();
|
||||
nodes_leaf_offset += bvh->pack.leaf_nodes.size();
|
||||
prim_offset += bvh->pack.prim_index.size();
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -26,23 +26,30 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHNode;
|
||||
struct BVHStackEntry;
|
||||
class BVHParams;
|
||||
class BoundBox;
|
||||
class LeafNode;
|
||||
class Object;
|
||||
class Progress;
|
||||
|
||||
#define BVH_NODE_SIZE 4
|
||||
#define BVH_NODE_LEAF_SIZE 1
|
||||
#define BVH_UNALIGNED_NODE_SIZE 7
|
||||
|
||||
/* Pack Utility */
|
||||
struct BVHStackEntry {
|
||||
const BVHNode *node;
|
||||
int idx;
|
||||
|
||||
BVHStackEntry(const BVHNode *n = 0, int i = 0);
|
||||
int encodeIdx() const;
|
||||
};
|
||||
|
||||
/* BVH2
|
||||
*
|
||||
* Typical BVH with each node having two children.
|
||||
*/
|
||||
class BVH2 : public BVH {
|
||||
public:
|
||||
void build(Progress &progress, Stats *stats);
|
||||
void refit(Progress &progress);
|
||||
|
||||
PackedBVH pack;
|
||||
|
||||
protected:
|
||||
/* constructor */
|
||||
friend class BVH;
|
||||
@@ -51,10 +58,10 @@ class BVH2 : public BVH {
|
||||
const vector<Object *> &objects);
|
||||
|
||||
/* Building process. */
|
||||
virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
|
||||
virtual BVHNode *widen_children_nodes(const BVHNode *root);
|
||||
|
||||
/* pack */
|
||||
void pack_nodes(const BVHNode *root) override;
|
||||
void pack_nodes(const BVHNode *root);
|
||||
|
||||
void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf);
|
||||
void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1);
|
||||
@@ -84,8 +91,18 @@ class BVH2 : public BVH {
|
||||
uint visibility1);
|
||||
|
||||
/* refit */
|
||||
void refit_nodes() override;
|
||||
void refit_nodes();
|
||||
void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility);
|
||||
|
||||
/* Refit range of primitives. */
|
||||
void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility);
|
||||
|
||||
/* triangles and strands */
|
||||
void pack_primitives();
|
||||
void pack_triangle(int idx, float4 storage[3]);
|
||||
|
||||
/* merge instance BVH's */
|
||||
void pack_instances(size_t nodes_size, size_t leaf_nodes_size);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -298,82 +298,31 @@ static bool rtc_progress_func(void *user_ptr, const double n)
|
||||
return !progress->get_cancel();
|
||||
}
|
||||
|
||||
static size_t count_primitives(Geometry *geom)
|
||||
{
|
||||
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
return mesh->num_triangles();
|
||||
}
|
||||
else if (geom->geometry_type == Geometry::HAIR) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
return hair->num_segments();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BVHEmbree::BVHEmbree(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_,
|
||||
const Device *device)
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_),
|
||||
scene(NULL),
|
||||
mem_used(0),
|
||||
top_level(NULL),
|
||||
rtc_device((RTCDevice)device->bvh_device()),
|
||||
stats(NULL),
|
||||
curve_subdivisions(params.curve_subdivisions),
|
||||
build_quality(RTC_BUILD_QUALITY_REFIT),
|
||||
dynamic_scene(true)
|
||||
rtc_device(NULL),
|
||||
build_quality(RTC_BUILD_QUALITY_REFIT)
|
||||
{
|
||||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
||||
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
||||
|
||||
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
|
||||
|
||||
pack.root_index = -1;
|
||||
}
|
||||
|
||||
BVHEmbree::~BVHEmbree()
|
||||
{
|
||||
if (!params.top_level) {
|
||||
destroy(scene);
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::destroy(RTCScene scene)
|
||||
{
|
||||
if (scene) {
|
||||
rtcReleaseScene(scene);
|
||||
scene = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::delete_rtcScene()
|
||||
{
|
||||
if (scene) {
|
||||
/* When this BVH is used as an instance in a top level BVH, don't delete now
|
||||
* Let the top_level BVH know that it should delete it later. */
|
||||
if (top_level) {
|
||||
top_level->add_delayed_delete_scene(scene);
|
||||
}
|
||||
else {
|
||||
rtcReleaseScene(scene);
|
||||
if (delayed_delete_scenes.size()) {
|
||||
foreach (RTCScene s, delayed_delete_scenes) {
|
||||
rtcReleaseScene(s);
|
||||
}
|
||||
}
|
||||
delayed_delete_scenes.clear();
|
||||
}
|
||||
scene = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::build(Progress &progress, Stats *stats_)
|
||||
void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
|
||||
{
|
||||
rtc_device = rtc_device_;
|
||||
assert(rtc_device);
|
||||
stats = stats_;
|
||||
|
||||
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
|
||||
rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats);
|
||||
|
||||
progress.set_substatus("Building BVH");
|
||||
@@ -394,35 +343,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
|
||||
RTC_BUILD_QUALITY_MEDIUM);
|
||||
rtcSetSceneBuildQuality(scene, build_quality);
|
||||
|
||||
/* Count triangles and curves first, reserve arrays once. */
|
||||
size_t prim_count = 0;
|
||||
|
||||
foreach (Object *ob, objects) {
|
||||
if (params.top_level) {
|
||||
if (!ob->is_traceable()) {
|
||||
continue;
|
||||
}
|
||||
if (!ob->get_geometry()->is_instanced()) {
|
||||
prim_count += count_primitives(ob->get_geometry());
|
||||
}
|
||||
else {
|
||||
++prim_count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
prim_count += count_primitives(ob->get_geometry());
|
||||
}
|
||||
}
|
||||
|
||||
pack.prim_object.reserve(prim_count);
|
||||
pack.prim_type.reserve(prim_count);
|
||||
pack.prim_index.reserve(prim_count);
|
||||
pack.prim_tri_index.reserve(prim_count);
|
||||
|
||||
int i = 0;
|
||||
|
||||
pack.object_node.clear();
|
||||
|
||||
foreach (Object *ob, objects) {
|
||||
if (params.top_level) {
|
||||
if (!ob->is_traceable()) {
|
||||
@@ -445,37 +366,11 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
|
||||
}
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
delete_rtcScene();
|
||||
stats = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
|
||||
rtcCommitScene(scene);
|
||||
|
||||
pack_primitives();
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
delete_rtcScene();
|
||||
stats = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
progress.set_substatus("Packing geometry");
|
||||
pack_nodes(NULL);
|
||||
|
||||
stats = NULL;
|
||||
}
|
||||
|
||||
void BVHEmbree::copy_to_device(Progress & /*progress*/, DeviceScene *dscene)
|
||||
{
|
||||
dscene->data.bvh.scene = scene;
|
||||
}
|
||||
|
||||
BVHNode *BVHEmbree::widen_children_nodes(const BVHNode * /*root*/)
|
||||
{
|
||||
assert(!"Must not be called.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void BVHEmbree::add_object(Object *ob, int i)
|
||||
@@ -498,15 +393,8 @@ void BVHEmbree::add_object(Object *ob, int i)
|
||||
|
||||
void BVHEmbree::add_instance(Object *ob, int i)
|
||||
{
|
||||
if (!ob || !ob->get_geometry()) {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh);
|
||||
|
||||
if (instance_bvh->top_level != this) {
|
||||
instance_bvh->top_level = this;
|
||||
}
|
||||
assert(instance_bvh != NULL);
|
||||
|
||||
const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1;
|
||||
const size_t num_motion_steps = min(num_object_motion_steps, RTC_MAX_TIME_STEP_COUNT);
|
||||
@@ -538,11 +426,6 @@ void BVHEmbree::add_instance(Object *ob, int i)
|
||||
geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_tfm());
|
||||
}
|
||||
|
||||
pack.prim_index.push_back_slow(-1);
|
||||
pack.prim_object.push_back_slow(i);
|
||||
pack.prim_type.push_back_slow(PRIMITIVE_NONE);
|
||||
pack.prim_tri_index.push_back_slow(-1);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)instance_bvh->scene);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
|
||||
@@ -553,20 +436,22 @@ void BVHEmbree::add_instance(Object *ob, int i)
|
||||
|
||||
void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
|
||||
{
|
||||
size_t prim_offset = pack.prim_index.size();
|
||||
size_t prim_offset = mesh->optix_prim_offset;
|
||||
|
||||
const Attribute *attr_mP = NULL;
|
||||
size_t num_geometry_motion_steps = 1;
|
||||
size_t num_motion_steps = 1;
|
||||
if (mesh->has_motion_blur()) {
|
||||
attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (attr_mP) {
|
||||
num_geometry_motion_steps = mesh->get_motion_steps();
|
||||
num_motion_steps = mesh->get_motion_steps();
|
||||
}
|
||||
}
|
||||
|
||||
const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
|
||||
assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
const size_t num_triangles = mesh->num_triangles();
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE);
|
||||
rtcSetGeometryBuildQuality(geom_id, build_quality);
|
||||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
@@ -588,22 +473,6 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
|
||||
|
||||
set_tri_vertex_buffer(geom_id, mesh, false);
|
||||
|
||||
size_t prim_object_size = pack.prim_object.size();
|
||||
pack.prim_object.resize(prim_object_size + num_triangles);
|
||||
size_t prim_type_size = pack.prim_type.size();
|
||||
pack.prim_type.resize(prim_type_size + num_triangles);
|
||||
size_t prim_index_size = pack.prim_index.size();
|
||||
pack.prim_index.resize(prim_index_size + num_triangles);
|
||||
pack.prim_tri_index.resize(prim_index_size + num_triangles);
|
||||
int prim_type = (num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE);
|
||||
|
||||
for (size_t j = 0; j < num_triangles; ++j) {
|
||||
pack.prim_object[prim_object_size + j] = i;
|
||||
pack.prim_type[prim_type_size + j] = prim_type;
|
||||
pack.prim_index[prim_index_size + j] = j;
|
||||
pack.prim_tri_index[prim_index_size + j] = j;
|
||||
}
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
@@ -629,12 +498,12 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con
|
||||
}
|
||||
}
|
||||
}
|
||||
const size_t num_verts = mesh->verts.size();
|
||||
const size_t num_verts = mesh->get_verts().size();
|
||||
|
||||
for (int t = 0; t < num_motion_steps; ++t) {
|
||||
const float3 *verts;
|
||||
if (t == t_mid) {
|
||||
verts = &mesh->verts[0];
|
||||
verts = mesh->get_verts().data();
|
||||
}
|
||||
else {
|
||||
int t_ = (t > t_mid) ? (t - 1) : t;
|
||||
@@ -736,24 +605,19 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c
|
||||
|
||||
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
|
||||
{
|
||||
size_t prim_offset = pack.prim_index.size();
|
||||
size_t prim_offset = hair->optix_prim_offset;
|
||||
|
||||
const Attribute *attr_mP = NULL;
|
||||
size_t num_geometry_motion_steps = 1;
|
||||
size_t num_motion_steps = 1;
|
||||
if (hair->has_motion_blur()) {
|
||||
attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (attr_mP) {
|
||||
num_geometry_motion_steps = hair->get_motion_steps();
|
||||
num_motion_steps = hair->get_motion_steps();
|
||||
}
|
||||
}
|
||||
|
||||
const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
|
||||
const PrimitiveType primitive_type =
|
||||
(num_motion_steps > 1) ?
|
||||
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
|
||||
PRIMITIVE_MOTION_CURVE_THICK) :
|
||||
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
|
||||
|
||||
assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
|
||||
num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
|
||||
|
||||
const size_t num_curves = hair->num_curves();
|
||||
size_t num_segments = 0;
|
||||
@@ -763,22 +627,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
|
||||
num_segments += c.num_segments();
|
||||
}
|
||||
|
||||
/* Make room for Cycles specific data. */
|
||||
size_t prim_object_size = pack.prim_object.size();
|
||||
pack.prim_object.resize(prim_object_size + num_segments);
|
||||
size_t prim_type_size = pack.prim_type.size();
|
||||
pack.prim_type.resize(prim_type_size + num_segments);
|
||||
size_t prim_index_size = pack.prim_index.size();
|
||||
pack.prim_index.resize(prim_index_size + num_segments);
|
||||
size_t prim_tri_index_size = pack.prim_index.size();
|
||||
pack.prim_tri_index.resize(prim_tri_index_size + num_segments);
|
||||
|
||||
enum RTCGeometryType type = (hair->curve_shape == CURVE_RIBBON ?
|
||||
RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
|
||||
RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
|
||||
|
||||
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
|
||||
rtcSetGeometryTessellationRate(geom_id, curve_subdivisions + 1);
|
||||
rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1);
|
||||
unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
|
||||
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments);
|
||||
size_t rtc_index = 0;
|
||||
@@ -788,11 +642,6 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
|
||||
rtc_indices[rtc_index] = c.first_key + k;
|
||||
/* Room for extra CVs at Catmull-Rom splines. */
|
||||
rtc_indices[rtc_index] += j * 2;
|
||||
/* Cycles specific data. */
|
||||
pack.prim_object[prim_object_size + rtc_index] = i;
|
||||
pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(primitive_type, k));
|
||||
pack.prim_index[prim_index_size + rtc_index] = j;
|
||||
pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index;
|
||||
|
||||
++rtc_index;
|
||||
}
|
||||
@@ -818,134 +667,10 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
|
||||
rtcReleaseGeometry(geom_id);
|
||||
}
|
||||
|
||||
void BVHEmbree::pack_nodes(const BVHNode *)
|
||||
void BVHEmbree::refit(Progress &progress)
|
||||
{
|
||||
/* Quite a bit of this code is for compatibility with Cycles' native BVH. */
|
||||
if (!params.top_level) {
|
||||
return;
|
||||
}
|
||||
progress.set_substatus("Refitting BVH nodes");
|
||||
|
||||
for (size_t i = 0; i < pack.prim_index.size(); ++i) {
|
||||
if (pack.prim_index[i] != -1) {
|
||||
pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
|
||||
}
|
||||
}
|
||||
|
||||
size_t prim_offset = pack.prim_index.size();
|
||||
|
||||
/* reserve */
|
||||
size_t prim_index_size = pack.prim_index.size();
|
||||
size_t prim_tri_verts_size = pack.prim_tri_verts.size();
|
||||
|
||||
size_t pack_prim_index_offset = prim_index_size;
|
||||
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
|
||||
size_t object_offset = 0;
|
||||
|
||||
map<Geometry *, int> geometry_map;
|
||||
|
||||
foreach (Object *ob, objects) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
BVH *bvh = geom->bvh;
|
||||
|
||||
if (geom->need_build_bvh(BVH_LAYOUT_EMBREE)) {
|
||||
if (geometry_map.find(geom) == geometry_map.end()) {
|
||||
prim_index_size += bvh->pack.prim_index.size();
|
||||
prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
|
||||
geometry_map[geom] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geometry_map.clear();
|
||||
|
||||
pack.prim_index.resize(prim_index_size);
|
||||
pack.prim_type.resize(prim_index_size);
|
||||
pack.prim_object.resize(prim_index_size);
|
||||
pack.prim_visibility.clear();
|
||||
pack.prim_tri_verts.resize(prim_tri_verts_size);
|
||||
pack.prim_tri_index.resize(prim_index_size);
|
||||
pack.object_node.resize(objects.size());
|
||||
|
||||
int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
|
||||
int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
|
||||
int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
|
||||
float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
|
||||
uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
|
||||
|
||||
/* merge */
|
||||
foreach (Object *ob, objects) {
|
||||
Geometry *geom = ob->get_geometry();
|
||||
|
||||
/* We assume that if mesh doesn't need own BVH it was already included
|
||||
* into a top-level BVH and no packing here is needed.
|
||||
*/
|
||||
if (!geom->need_build_bvh(BVH_LAYOUT_EMBREE)) {
|
||||
pack.object_node[object_offset++] = prim_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if geom already added once, don't add it again, but used set
|
||||
* node offset for this object */
|
||||
map<Geometry *, int>::iterator it = geometry_map.find(geom);
|
||||
|
||||
if (geometry_map.find(geom) != geometry_map.end()) {
|
||||
int noffset = it->second;
|
||||
pack.object_node[object_offset++] = noffset;
|
||||
continue;
|
||||
}
|
||||
|
||||
BVHEmbree *bvh = (BVHEmbree *)geom->bvh;
|
||||
|
||||
rtc_memory_monitor_func(stats, unaccounted_mem, true);
|
||||
unaccounted_mem = 0;
|
||||
|
||||
int geom_prim_offset = geom->prim_offset;
|
||||
|
||||
/* fill in node indexes for instances */
|
||||
pack.object_node[object_offset++] = prim_offset;
|
||||
|
||||
geometry_map[geom] = pack.object_node[object_offset - 1];
|
||||
|
||||
/* merge primitive, object and triangle indexes */
|
||||
if (bvh->pack.prim_index.size()) {
|
||||
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
|
||||
int *bvh_prim_index = &bvh->pack.prim_index[0];
|
||||
int *bvh_prim_type = &bvh->pack.prim_type[0];
|
||||
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
|
||||
|
||||
for (size_t i = 0; i < bvh_prim_index_size; ++i) {
|
||||
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_prim_index_offset] = -1;
|
||||
}
|
||||
else {
|
||||
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
|
||||
pack_prim_tri_verts_offset;
|
||||
}
|
||||
|
||||
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
|
||||
pack_prim_object[pack_prim_index_offset] = 0;
|
||||
|
||||
++pack_prim_index_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge triangle vertices data. */
|
||||
if (bvh->pack.prim_tri_verts.size()) {
|
||||
const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
|
||||
memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
|
||||
&bvh->pack.prim_tri_verts[0],
|
||||
prim_tri_size * sizeof(float4));
|
||||
pack_prim_tri_verts_offset += prim_tri_size;
|
||||
}
|
||||
|
||||
prim_offset += bvh->pack.prim_index.size();
|
||||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::refit_nodes()
|
||||
{
|
||||
/* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */
|
||||
unsigned geom_id = 0;
|
||||
foreach (Object *ob, objects) {
|
||||
@@ -957,6 +682,7 @@ void BVHEmbree::refit_nodes()
|
||||
if (mesh->num_triangles() > 0) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id);
|
||||
set_tri_vertex_buffer(geom, mesh, true);
|
||||
rtcSetGeometryUserData(geom, (void *)mesh->optix_prim_offset);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
@@ -965,14 +691,17 @@ void BVHEmbree::refit_nodes()
|
||||
if (hair->num_curves() > 0) {
|
||||
RTCGeometry geom = rtcGetGeometry(scene, geom_id + 1);
|
||||
set_curve_vertex_buffer(geom, hair, true);
|
||||
rtcSetGeometryUserData(geom, (void *)hair->optix_prim_offset);
|
||||
rtcCommitGeometry(geom);
|
||||
}
|
||||
}
|
||||
}
|
||||
geom_id += 2;
|
||||
}
|
||||
|
||||
rtcCommitScene(scene);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_EMBREE */
|
||||
|
@@ -31,56 +31,34 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Geometry;
|
||||
class Hair;
|
||||
class Mesh;
|
||||
|
||||
class BVHEmbree : public BVH {
|
||||
public:
|
||||
virtual void build(Progress &progress, Stats *stats) override;
|
||||
virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override;
|
||||
virtual ~BVHEmbree();
|
||||
RTCScene scene;
|
||||
static void destroy(RTCScene);
|
||||
void build(Progress &progress, Stats *stats, RTCDevice rtc_device);
|
||||
void refit(Progress &progress);
|
||||
|
||||
/* Building process. */
|
||||
virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
|
||||
RTCScene scene;
|
||||
|
||||
protected:
|
||||
friend class BVH;
|
||||
BVHEmbree(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects,
|
||||
const Device *device);
|
||||
|
||||
virtual void pack_nodes(const BVHNode *) override;
|
||||
virtual void refit_nodes() override;
|
||||
const vector<Object *> &objects);
|
||||
virtual ~BVHEmbree();
|
||||
|
||||
void add_object(Object *ob, int i);
|
||||
void add_instance(Object *ob, int i);
|
||||
void add_curves(const Object *ob, const Hair *hair, int i);
|
||||
void add_triangles(const Object *ob, const Mesh *mesh, int i);
|
||||
|
||||
ssize_t mem_used;
|
||||
|
||||
void add_delayed_delete_scene(RTCScene scene)
|
||||
{
|
||||
delayed_delete_scenes.push_back(scene);
|
||||
}
|
||||
BVHEmbree *top_level;
|
||||
|
||||
private:
|
||||
void delete_rtcScene();
|
||||
void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update);
|
||||
void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update);
|
||||
|
||||
RTCDevice rtc_device;
|
||||
|
||||
Stats *stats;
|
||||
vector<RTCScene> delayed_delete_scenes;
|
||||
int curve_subdivisions;
|
||||
enum RTCBuildQuality build_quality;
|
||||
bool dynamic_scene;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
37
intern/cycles/bvh/bvh_multi.cpp
Normal file
37
intern/cycles/bvh/bvh_multi.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020, Blender Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "bvh/bvh_multi.h"
|
||||
|
||||
#include "util/util_foreach.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHMulti::BVHMulti(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_)
|
||||
{
|
||||
}
|
||||
|
||||
BVHMulti::~BVHMulti()
|
||||
{
|
||||
foreach (BVH *bvh, sub_bvhs) {
|
||||
delete bvh;
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
39
intern/cycles/bvh/bvh_multi.h
Normal file
39
intern/cycles/bvh/bvh_multi.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2020, 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.
|
||||
*/
|
||||
|
||||
#ifndef __BVH_MULTI_H__
|
||||
#define __BVH_MULTI_H__
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
#include "bvh/bvh_params.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class BVHMulti : public BVH {
|
||||
public:
|
||||
vector<BVH *> sub_bvhs;
|
||||
|
||||
protected:
|
||||
friend class BVH;
|
||||
BVHMulti(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
virtual ~BVHMulti();
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* __BVH_MULTI_H__ */
|
@@ -19,212 +19,22 @@
|
||||
|
||||
# include "bvh/bvh_optix.h"
|
||||
|
||||
# include "device/device.h"
|
||||
|
||||
# include "render/geometry.h"
|
||||
# include "render/hair.h"
|
||||
# include "render/mesh.h"
|
||||
# include "render/object.h"
|
||||
|
||||
# include "util/util_foreach.h"
|
||||
# include "util/util_logging.h"
|
||||
# include "util/util_progress.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
BVHOptiX::BVHOptiX(const BVHParams ¶ms_,
|
||||
const vector<Geometry *> &geometry_,
|
||||
const vector<Object *> &objects_)
|
||||
: BVH(params_, geometry_, objects_)
|
||||
const vector<Object *> &objects_,
|
||||
Device *device)
|
||||
: BVH(params_, geometry_, objects_),
|
||||
traversable_handle(0),
|
||||
as_data(device, params_.top_level ? "optix tlas" : "optix blas"),
|
||||
motion_transform_data(device, "optix motion transform")
|
||||
{
|
||||
optix_handle = 0;
|
||||
optix_data_handle = 0;
|
||||
do_refit = false;
|
||||
}
|
||||
|
||||
BVHOptiX::~BVHOptiX()
|
||||
{
|
||||
}
|
||||
|
||||
void BVHOptiX::build(Progress &, Stats *)
|
||||
{
|
||||
if (params.top_level)
|
||||
pack_tlas();
|
||||
else
|
||||
pack_blas();
|
||||
}
|
||||
|
||||
void BVHOptiX::copy_to_device(Progress &progress, DeviceScene *dscene)
|
||||
{
|
||||
progress.set_status("Updating Scene BVH", "Building OptiX acceleration structure");
|
||||
|
||||
Device *const device = dscene->bvh_nodes.device;
|
||||
if (!device->build_optix_bvh(this))
|
||||
progress.set_error("Failed to build OptiX acceleration structure");
|
||||
}
|
||||
|
||||
void BVHOptiX::pack_blas()
|
||||
{
|
||||
// Bottom-level BVH can contain multiple primitive types, so merge them:
|
||||
assert(geometry.size() == 1 && objects.size() == 1); // These are built per-mesh
|
||||
Geometry *const geom = geometry[0];
|
||||
|
||||
if (geom->geometry_type == Geometry::HAIR) {
|
||||
Hair *const hair = static_cast<Hair *const>(geom);
|
||||
if (hair->num_curves() > 0) {
|
||||
const size_t num_curves = hair->num_curves();
|
||||
const size_t num_segments = hair->num_segments();
|
||||
pack.prim_type.reserve(pack.prim_type.size() + num_segments);
|
||||
pack.prim_index.reserve(pack.prim_index.size() + num_segments);
|
||||
pack.prim_object.reserve(pack.prim_object.size() + num_segments);
|
||||
// 'pack.prim_time' is only used in geom_curve_intersect.h
|
||||
// It is not needed because of OPTIX_MOTION_FLAG_[START|END]_VANISH
|
||||
|
||||
uint type = (hair->get_use_motion_blur() &&
|
||||
hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) ?
|
||||
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
|
||||
PRIMITIVE_MOTION_CURVE_THICK) :
|
||||
((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON :
|
||||
PRIMITIVE_CURVE_THICK);
|
||||
|
||||
for (size_t j = 0; j < num_curves; ++j) {
|
||||
const Hair::Curve curve = hair->get_curve(j);
|
||||
for (size_t k = 0; k < curve.num_segments(); ++k) {
|
||||
pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
|
||||
// Each curve segment points back to its curve index
|
||||
pack.prim_index.push_back_reserved(j);
|
||||
pack.prim_object.push_back_reserved(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
|
||||
Mesh *const mesh = static_cast<Mesh *const>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
const size_t num_triangles = mesh->num_triangles();
|
||||
pack.prim_type.reserve(pack.prim_type.size() + num_triangles);
|
||||
pack.prim_index.reserve(pack.prim_index.size() + num_triangles);
|
||||
pack.prim_object.reserve(pack.prim_object.size() + num_triangles);
|
||||
|
||||
uint type = PRIMITIVE_TRIANGLE;
|
||||
if (mesh->get_use_motion_blur() && mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION))
|
||||
type = PRIMITIVE_MOTION_TRIANGLE;
|
||||
|
||||
for (size_t k = 0; k < num_triangles; ++k) {
|
||||
pack.prim_type.push_back_reserved(type);
|
||||
pack.prim_index.push_back_reserved(k);
|
||||
pack.prim_object.push_back_reserved(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize visibility to zero and later update it during top-level build
|
||||
uint prev_visibility = objects[0]->get_visibility();
|
||||
objects[0]->set_visibility(0);
|
||||
|
||||
// Update 'pack.prim_tri_index', 'pack.prim_tri_verts' and 'pack.prim_visibility'
|
||||
pack_primitives();
|
||||
|
||||
// Reset visibility after packing
|
||||
objects[0]->set_visibility(prev_visibility);
|
||||
}
|
||||
|
||||
void BVHOptiX::pack_tlas()
|
||||
{
|
||||
// Calculate total packed size
|
||||
size_t prim_index_size = 0;
|
||||
size_t prim_tri_verts_size = 0;
|
||||
foreach (Geometry *geom, geometry) {
|
||||
BVH *const bvh = geom->bvh;
|
||||
prim_index_size += bvh->pack.prim_index.size();
|
||||
prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
|
||||
}
|
||||
|
||||
if (prim_index_size == 0)
|
||||
return; // Abort right away if this is an empty BVH
|
||||
|
||||
size_t pack_offset = 0;
|
||||
size_t pack_verts_offset = 0;
|
||||
|
||||
pack.prim_type.resize(prim_index_size);
|
||||
int *pack_prim_type = pack.prim_type.data();
|
||||
pack.prim_index.resize(prim_index_size);
|
||||
int *pack_prim_index = pack.prim_index.data();
|
||||
pack.prim_object.resize(prim_index_size);
|
||||
int *pack_prim_object = pack.prim_object.data();
|
||||
pack.prim_visibility.resize(prim_index_size);
|
||||
uint *pack_prim_visibility = pack.prim_visibility.data();
|
||||
pack.prim_tri_index.resize(prim_index_size);
|
||||
uint *pack_prim_tri_index = pack.prim_tri_index.data();
|
||||
pack.prim_tri_verts.resize(prim_tri_verts_size);
|
||||
float4 *pack_prim_tri_verts = pack.prim_tri_verts.data();
|
||||
|
||||
// Top-level BVH should only contain instances, see 'Geometry::need_build_bvh'
|
||||
// Iterate over scene mesh list instead of objects, since the 'prim_offset' is calculated based
|
||||
// on that list, which may be ordered differently from the object list.
|
||||
foreach (Geometry *geom, geometry) {
|
||||
PackedBVH &bvh_pack = geom->bvh->pack;
|
||||
int geom_prim_offset = geom->prim_offset;
|
||||
|
||||
// Merge visibility flags of all objects and fix object indices for non-instanced geometry
|
||||
int object_index = 0; // Unused for instanced geometry
|
||||
int object_visibility = 0;
|
||||
foreach (Object *ob, objects) {
|
||||
if (ob->get_geometry() == geom) {
|
||||
object_visibility |= ob->visibility_for_tracing();
|
||||
if (!geom->is_instanced()) {
|
||||
object_index = ob->get_device_index();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge primitive, object and triangle indexes
|
||||
if (!bvh_pack.prim_index.empty()) {
|
||||
int *bvh_prim_type = &bvh_pack.prim_type[0];
|
||||
int *bvh_prim_index = &bvh_pack.prim_index[0];
|
||||
uint *bvh_prim_tri_index = &bvh_pack.prim_tri_index[0];
|
||||
uint *bvh_prim_visibility = &bvh_pack.prim_visibility[0];
|
||||
|
||||
for (size_t i = 0; i < bvh_pack.prim_index.size(); i++, pack_offset++) {
|
||||
if (bvh_pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
|
||||
pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_offset] = -1;
|
||||
}
|
||||
else {
|
||||
pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset;
|
||||
pack_prim_tri_index[pack_offset] = bvh_prim_tri_index[i] + pack_verts_offset;
|
||||
}
|
||||
|
||||
pack_prim_type[pack_offset] = bvh_prim_type[i];
|
||||
pack_prim_object[pack_offset] = object_index;
|
||||
pack_prim_visibility[pack_offset] = bvh_prim_visibility[i] | object_visibility;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge triangle vertex data
|
||||
if (!bvh_pack.prim_tri_verts.empty()) {
|
||||
const size_t prim_tri_size = bvh_pack.prim_tri_verts.size();
|
||||
memcpy(pack_prim_tri_verts + pack_verts_offset,
|
||||
bvh_pack.prim_tri_verts.data(),
|
||||
prim_tri_size * sizeof(float4));
|
||||
pack_verts_offset += prim_tri_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BVHOptiX::pack_nodes(const BVHNode *)
|
||||
{
|
||||
}
|
||||
|
||||
void BVHOptiX::refit_nodes()
|
||||
{
|
||||
do_refit = true;
|
||||
}
|
||||
|
||||
BVHNode *BVHOptiX::widen_children_nodes(const BVHNode *)
|
||||
{
|
||||
return NULL;
|
||||
// Acceleration structure memory is freed via the 'as_data' destructor
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -26,33 +26,19 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Geometry;
|
||||
class Optix;
|
||||
|
||||
class BVHOptiX : public BVH {
|
||||
friend class BVH;
|
||||
|
||||
public:
|
||||
uint64_t optix_handle;
|
||||
uint64_t optix_data_handle;
|
||||
bool do_refit;
|
||||
uint64_t traversable_handle;
|
||||
device_only_memory<char> as_data;
|
||||
device_only_memory<char> motion_transform_data;
|
||||
|
||||
protected:
|
||||
friend class BVH;
|
||||
BVHOptiX(const BVHParams ¶ms,
|
||||
const vector<Geometry *> &geometry,
|
||||
const vector<Object *> &objects);
|
||||
const vector<Object *> &objects,
|
||||
Device *device);
|
||||
virtual ~BVHOptiX();
|
||||
|
||||
virtual void build(Progress &progress, Stats *) override;
|
||||
virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override;
|
||||
|
||||
private:
|
||||
void pack_blas();
|
||||
void pack_tlas();
|
||||
|
||||
virtual void pack_nodes(const BVHNode *) override;
|
||||
virtual void refit_nodes() override;
|
||||
|
||||
virtual BVHNode *widen_children_nodes(const BVHNode *) override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -71,6 +71,7 @@ class CUDADevice : public Device {
|
||||
};
|
||||
typedef map<device_memory *, CUDAMem> CUDAMemMap;
|
||||
CUDAMemMap cuda_mem_map;
|
||||
thread_mutex cuda_mem_map_mutex;
|
||||
|
||||
struct PixelMem {
|
||||
GLuint cuPBO;
|
||||
|
@@ -718,8 +718,10 @@ void CUDADevice::init_host_memory()
|
||||
void CUDADevice::load_texture_info()
|
||||
{
|
||||
if (need_texture_info) {
|
||||
texture_info.copy_to_device();
|
||||
/* Unset flag before copying, so this does not loop indefinitely if the copy below calls
|
||||
* into 'move_textures_to_host' (which calls 'load_texture_info' again). */
|
||||
need_texture_info = false;
|
||||
texture_info.copy_to_device();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -988,6 +990,7 @@ void CUDADevice::mem_alloc(device_memory &mem)
|
||||
assert(!"mem_alloc not supported for global memory.");
|
||||
}
|
||||
else {
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
generic_alloc(mem);
|
||||
}
|
||||
}
|
||||
@@ -1006,10 +1009,10 @@ void CUDADevice::mem_copy_to(device_memory &mem)
|
||||
tex_alloc((device_texture &)mem);
|
||||
}
|
||||
else {
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
if (!mem.device_pointer) {
|
||||
generic_alloc(mem);
|
||||
}
|
||||
|
||||
generic_copy_to(mem);
|
||||
}
|
||||
}
|
||||
@@ -1048,6 +1051,7 @@ void CUDADevice::mem_zero(device_memory &mem)
|
||||
|
||||
/* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory
|
||||
* regardless of mem.host_pointer and mem.shared_pointer. */
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) {
|
||||
const CUDAContextScope scope(this);
|
||||
cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size()));
|
||||
@@ -1069,6 +1073,7 @@ void CUDADevice::mem_free(device_memory &mem)
|
||||
tex_free((device_texture &)mem);
|
||||
}
|
||||
else {
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
generic_free(mem);
|
||||
}
|
||||
}
|
||||
@@ -1092,6 +1097,7 @@ void CUDADevice::const_copy_to(const char *name, void *host, size_t size)
|
||||
void CUDADevice::global_alloc(device_memory &mem)
|
||||
{
|
||||
if (mem.is_resident(this)) {
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
generic_alloc(mem);
|
||||
generic_copy_to(mem);
|
||||
}
|
||||
@@ -1102,6 +1108,7 @@ void CUDADevice::global_alloc(device_memory &mem)
|
||||
void CUDADevice::global_free(device_memory &mem)
|
||||
{
|
||||
if (mem.is_resident(this) && mem.device_pointer) {
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
generic_free(mem);
|
||||
}
|
||||
}
|
||||
@@ -1170,6 +1177,8 @@ void CUDADevice::tex_alloc(device_texture &mem)
|
||||
size_t src_pitch = mem.data_width * dsize * mem.data_elements;
|
||||
size_t dst_pitch = src_pitch;
|
||||
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
|
||||
if (!mem.is_resident(this)) {
|
||||
cmem = &cuda_mem_map[&mem];
|
||||
cmem->texobject = 0;
|
||||
@@ -1257,6 +1266,9 @@ void CUDADevice::tex_alloc(device_texture &mem)
|
||||
cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size));
|
||||
}
|
||||
|
||||
/* Unlock mutex before resizing texture info, since that may attempt to lock it again. */
|
||||
lock.unlock();
|
||||
|
||||
/* Resize once */
|
||||
const uint slot = mem.slot;
|
||||
if (slot >= texture_info.size()) {
|
||||
@@ -1305,6 +1317,11 @@ void CUDADevice::tex_alloc(device_texture &mem)
|
||||
texDesc.filterMode = filter_mode;
|
||||
texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES;
|
||||
|
||||
/* Lock again and refresh the data pointer (in case another thread modified the map in the
|
||||
* meantime). */
|
||||
lock.lock();
|
||||
cmem = &cuda_mem_map[&mem];
|
||||
|
||||
cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
|
||||
|
||||
texture_info[slot].data = (uint64_t)cmem->texobject;
|
||||
@@ -1318,6 +1335,7 @@ void CUDADevice::tex_free(device_texture &mem)
|
||||
{
|
||||
if (mem.device_pointer) {
|
||||
CUDAContextScope scope(this);
|
||||
thread_scoped_lock lock(cuda_mem_map_mutex);
|
||||
const CUDAMem &cmem = cuda_mem_map[&mem];
|
||||
|
||||
if (cmem.texobject) {
|
||||
|
@@ -17,6 +17,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bvh/bvh2.h"
|
||||
|
||||
#include "device/device.h"
|
||||
#include "device/device_intern.h"
|
||||
|
||||
@@ -364,6 +366,19 @@ void Device::draw_pixels(device_memory &rgba,
|
||||
}
|
||||
}
|
||||
|
||||
void Device::build_bvh(BVH *bvh, Progress &progress, bool refit)
|
||||
{
|
||||
assert(bvh->params.bvh_layout == BVH_LAYOUT_BVH2);
|
||||
|
||||
BVH2 *const bvh2 = static_cast<BVH2 *>(bvh);
|
||||
if (refit) {
|
||||
bvh2->refit(progress);
|
||||
}
|
||||
else {
|
||||
bvh2->build(progress, &stats);
|
||||
}
|
||||
}
|
||||
|
||||
Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background)
|
||||
{
|
||||
#ifdef WITH_MULTI
|
||||
|
@@ -373,12 +373,6 @@ class Device {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Device specific pointer for BVH creation. Currently only used by Embree. */
|
||||
virtual void *bvh_device() const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* load/compile kernels, must be called before adding tasks */
|
||||
virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/)
|
||||
{
|
||||
@@ -427,10 +421,7 @@ class Device {
|
||||
const DeviceDrawParams &draw_params);
|
||||
|
||||
/* acceleration structure building */
|
||||
virtual bool build_optix_bvh(BVH *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
|
||||
|
||||
#ifdef WITH_NETWORK
|
||||
/* networking */
|
||||
|
@@ -47,6 +47,8 @@
|
||||
#include "kernel/osl/osl_globals.h"
|
||||
// clang-format on
|
||||
|
||||
#include "bvh/bvh_embree.h"
|
||||
|
||||
#include "render/buffers.h"
|
||||
#include "render/coverage.h"
|
||||
|
||||
@@ -188,6 +190,7 @@ class CPUDevice : public Device {
|
||||
#endif
|
||||
thread_spin_lock oidn_task_lock;
|
||||
#ifdef WITH_EMBREE
|
||||
RTCScene embree_scene = NULL;
|
||||
RTCDevice embree_device;
|
||||
#endif
|
||||
|
||||
@@ -472,6 +475,15 @@ class CPUDevice : public Device {
|
||||
|
||||
virtual void const_copy_to(const char *name, void *host, size_t size) override
|
||||
{
|
||||
#if WITH_EMBREE
|
||||
if (strcmp(name, "__data") == 0) {
|
||||
assert(size <= sizeof(KernelData));
|
||||
|
||||
// Update scene handle (since it is different for each device on multi devices)
|
||||
KernelData *const data = (KernelData *)host;
|
||||
data->bvh.scene = embree_scene;
|
||||
}
|
||||
#endif
|
||||
kernel_const_copy(&kernel_globals, name, host, size);
|
||||
}
|
||||
|
||||
@@ -537,13 +549,26 @@ class CPUDevice : public Device {
|
||||
#endif
|
||||
}
|
||||
|
||||
void *bvh_device() const override
|
||||
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
|
||||
{
|
||||
#ifdef WITH_EMBREE
|
||||
return embree_device;
|
||||
#else
|
||||
return NULL;
|
||||
if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE ||
|
||||
bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) {
|
||||
BVHEmbree *const bvh_embree = static_cast<BVHEmbree *>(bvh);
|
||||
if (refit) {
|
||||
bvh_embree->refit(progress);
|
||||
}
|
||||
else {
|
||||
bvh_embree->build(progress, &stats, embree_device);
|
||||
}
|
||||
|
||||
if (bvh->params.top_level) {
|
||||
embree_scene = bvh_embree->scene;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
Device::build_bvh(bvh, progress, refit);
|
||||
}
|
||||
|
||||
void thread_run(DeviceTask &task)
|
||||
|
@@ -17,11 +17,14 @@
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bvh/bvh_multi.h"
|
||||
|
||||
#include "device/device.h"
|
||||
#include "device/device_intern.h"
|
||||
#include "device/device_network.h"
|
||||
|
||||
#include "render/buffers.h"
|
||||
#include "render/geometry.h"
|
||||
|
||||
#include "util/util_foreach.h"
|
||||
#include "util/util_list.h"
|
||||
@@ -141,7 +144,7 @@ class MultiDevice : public Device {
|
||||
delete sub.device;
|
||||
}
|
||||
|
||||
const string &error_message()
|
||||
const string &error_message() override
|
||||
{
|
||||
error_msg.clear();
|
||||
|
||||
@@ -153,7 +156,7 @@ class MultiDevice : public Device {
|
||||
return error_msg;
|
||||
}
|
||||
|
||||
virtual bool show_samples() const
|
||||
virtual bool show_samples() const override
|
||||
{
|
||||
if (devices.size() > 1) {
|
||||
return false;
|
||||
@@ -161,16 +164,31 @@ class MultiDevice : public Device {
|
||||
return devices.front().device->show_samples();
|
||||
}
|
||||
|
||||
virtual BVHLayoutMask get_bvh_layout_mask() const
|
||||
virtual BVHLayoutMask get_bvh_layout_mask() const override
|
||||
{
|
||||
BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL;
|
||||
BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE;
|
||||
foreach (const SubDevice &sub_device, devices) {
|
||||
bvh_layout_mask &= sub_device.device->get_bvh_layout_mask();
|
||||
BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask();
|
||||
bvh_layout_mask &= device_bvh_layout_mask;
|
||||
bvh_layout_mask_all |= device_bvh_layout_mask;
|
||||
}
|
||||
|
||||
/* With multiple OptiX devices, every device needs its own acceleration structure */
|
||||
if (bvh_layout_mask == BVH_LAYOUT_OPTIX) {
|
||||
return BVH_LAYOUT_MULTI_OPTIX;
|
||||
}
|
||||
|
||||
/* When devices do not share a common BVH layout, fall back to creating one for each */
|
||||
const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE);
|
||||
if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) {
|
||||
return BVH_LAYOUT_MULTI_OPTIX_EMBREE;
|
||||
}
|
||||
|
||||
return bvh_layout_mask;
|
||||
}
|
||||
|
||||
bool load_kernels(const DeviceRequestedFeatures &requested_features)
|
||||
bool load_kernels(const DeviceRequestedFeatures &requested_features) override
|
||||
{
|
||||
foreach (SubDevice &sub, devices)
|
||||
if (!sub.device->load_kernels(requested_features))
|
||||
@@ -188,7 +206,7 @@ class MultiDevice : public Device {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wait_for_availability(const DeviceRequestedFeatures &requested_features)
|
||||
bool wait_for_availability(const DeviceRequestedFeatures &requested_features) override
|
||||
{
|
||||
foreach (SubDevice &sub, devices)
|
||||
if (!sub.device->wait_for_availability(requested_features))
|
||||
@@ -203,7 +221,7 @@ class MultiDevice : public Device {
|
||||
return true;
|
||||
}
|
||||
|
||||
DeviceKernelStatus get_active_kernel_switch_state()
|
||||
DeviceKernelStatus get_active_kernel_switch_state() override
|
||||
{
|
||||
DeviceKernelStatus result = DEVICE_KERNEL_USING_FEATURE_KERNEL;
|
||||
|
||||
@@ -227,24 +245,64 @@ class MultiDevice : public Device {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool build_optix_bvh(BVH *bvh)
|
||||
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
|
||||
{
|
||||
/* Broadcast acceleration structure build to all render devices */
|
||||
foreach (SubDevice &sub, devices) {
|
||||
if (!sub.device->build_optix_bvh(bvh))
|
||||
return false;
|
||||
/* Try to build and share a single acceleration structure, if possible */
|
||||
if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2 || bvh->params.bvh_layout == BVH_LAYOUT_EMBREE) {
|
||||
devices.back().device->build_bvh(bvh, progress, refit);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX ||
|
||||
bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE);
|
||||
|
||||
BVHMulti *const bvh_multi = static_cast<BVHMulti *>(bvh);
|
||||
bvh_multi->sub_bvhs.resize(devices.size());
|
||||
|
||||
vector<BVHMulti *> geom_bvhs;
|
||||
geom_bvhs.reserve(bvh->geometry.size());
|
||||
foreach (Geometry *geom, bvh->geometry) {
|
||||
geom_bvhs.push_back(static_cast<BVHMulti *>(geom->bvh));
|
||||
}
|
||||
|
||||
/* Broadcast acceleration structure build to all render devices */
|
||||
size_t i = 0;
|
||||
foreach (SubDevice &sub, devices) {
|
||||
/* Change geometry BVH pointers to the sub BVH */
|
||||
for (size_t k = 0; k < bvh->geometry.size(); ++k) {
|
||||
bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i];
|
||||
}
|
||||
|
||||
if (!bvh_multi->sub_bvhs[i]) {
|
||||
BVHParams params = bvh->params;
|
||||
if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX)
|
||||
params.bvh_layout = BVH_LAYOUT_OPTIX;
|
||||
else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE)
|
||||
params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX :
|
||||
BVH_LAYOUT_EMBREE;
|
||||
|
||||
/* Skip building a bottom level acceleration structure for non-instanced geometry on Embree
|
||||
* (since they are put into the top level directly, see bvh_embree.cpp) */
|
||||
if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE &&
|
||||
!bvh->geometry[0]->is_instanced()) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device);
|
||||
}
|
||||
|
||||
sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Change geomtry BVH pointers back to the multi BVH */
|
||||
for (size_t k = 0; k < bvh->geometry.size(); ++k) {
|
||||
bvh->geometry[k]->bvh = geom_bvhs[k];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void *bvh_device() const
|
||||
{
|
||||
/* CPU devices will always be at the back, so simply choose the last one.
|
||||
There should only ever be one CPU device anyway and we need the Embree device for it. */
|
||||
return devices.back().device->bvh_device();
|
||||
}
|
||||
|
||||
virtual void *osl_memory()
|
||||
virtual void *osl_memory() override
|
||||
{
|
||||
if (devices.size() > 1) {
|
||||
return NULL;
|
||||
@@ -252,7 +310,7 @@ class MultiDevice : public Device {
|
||||
return devices.front().device->osl_memory();
|
||||
}
|
||||
|
||||
bool is_resident(device_ptr key, Device *sub_device)
|
||||
bool is_resident(device_ptr key, Device *sub_device) override
|
||||
{
|
||||
foreach (SubDevice &sub, devices) {
|
||||
if (sub.device == sub_device) {
|
||||
@@ -299,7 +357,7 @@ class MultiDevice : public Device {
|
||||
return find_matching_mem_device(key, sub)->ptr_map[key];
|
||||
}
|
||||
|
||||
void mem_alloc(device_memory &mem)
|
||||
void mem_alloc(device_memory &mem) override
|
||||
{
|
||||
device_ptr key = unique_key++;
|
||||
|
||||
@@ -335,7 +393,7 @@ class MultiDevice : public Device {
|
||||
stats.mem_alloc(mem.device_size);
|
||||
}
|
||||
|
||||
void mem_copy_to(device_memory &mem)
|
||||
void mem_copy_to(device_memory &mem) override
|
||||
{
|
||||
device_ptr existing_key = mem.device_pointer;
|
||||
device_ptr key = (existing_key) ? existing_key : unique_key++;
|
||||
@@ -378,7 +436,7 @@ class MultiDevice : public Device {
|
||||
stats.mem_alloc(mem.device_size - existing_size);
|
||||
}
|
||||
|
||||
void mem_copy_from(device_memory &mem, int y, int w, int h, int elem)
|
||||
void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override
|
||||
{
|
||||
device_ptr key = mem.device_pointer;
|
||||
int i = 0, sub_h = h / devices.size();
|
||||
@@ -399,7 +457,7 @@ class MultiDevice : public Device {
|
||||
mem.device_pointer = key;
|
||||
}
|
||||
|
||||
void mem_zero(device_memory &mem)
|
||||
void mem_zero(device_memory &mem) override
|
||||
{
|
||||
device_ptr existing_key = mem.device_pointer;
|
||||
device_ptr key = (existing_key) ? existing_key : unique_key++;
|
||||
@@ -454,7 +512,7 @@ class MultiDevice : public Device {
|
||||
stats.mem_alloc(mem.device_size - existing_size);
|
||||
}
|
||||
|
||||
void mem_free(device_memory &mem)
|
||||
void mem_free(device_memory &mem) override
|
||||
{
|
||||
device_ptr key = mem.device_pointer;
|
||||
size_t existing_size = mem.device_size;
|
||||
@@ -510,7 +568,7 @@ class MultiDevice : public Device {
|
||||
stats.mem_free(existing_size);
|
||||
}
|
||||
|
||||
void const_copy_to(const char *name, void *host, size_t size)
|
||||
void const_copy_to(const char *name, void *host, size_t size) override
|
||||
{
|
||||
foreach (SubDevice &sub, devices)
|
||||
sub.device->const_copy_to(name, host, size);
|
||||
@@ -527,7 +585,7 @@ class MultiDevice : public Device {
|
||||
int dw,
|
||||
int dh,
|
||||
bool transparent,
|
||||
const DeviceDrawParams &draw_params)
|
||||
const DeviceDrawParams &draw_params) override
|
||||
{
|
||||
assert(rgba.type == MEM_PIXELS);
|
||||
|
||||
@@ -551,7 +609,7 @@ class MultiDevice : public Device {
|
||||
rgba.device_pointer = key;
|
||||
}
|
||||
|
||||
void map_tile(Device *sub_device, RenderTile &tile)
|
||||
void map_tile(Device *sub_device, RenderTile &tile) override
|
||||
{
|
||||
if (!tile.buffer) {
|
||||
return;
|
||||
@@ -572,7 +630,7 @@ class MultiDevice : public Device {
|
||||
}
|
||||
}
|
||||
|
||||
int device_number(Device *sub_device)
|
||||
int device_number(Device *sub_device) override
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
@@ -591,7 +649,7 @@ class MultiDevice : public Device {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
|
||||
void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override
|
||||
{
|
||||
for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
|
||||
RenderTile &tile = neighbors.tiles[i];
|
||||
@@ -643,7 +701,7 @@ class MultiDevice : public Device {
|
||||
}
|
||||
}
|
||||
|
||||
void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
|
||||
void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override
|
||||
{
|
||||
RenderTile &target_tile = neighbors.target;
|
||||
device_vector<float> &mem = target_tile.buffers->buffer;
|
||||
@@ -677,7 +735,7 @@ class MultiDevice : public Device {
|
||||
}
|
||||
}
|
||||
|
||||
int get_split_task_count(DeviceTask &task)
|
||||
int get_split_task_count(DeviceTask &task) override
|
||||
{
|
||||
int total_tasks = 0;
|
||||
list<DeviceTask> tasks;
|
||||
@@ -693,7 +751,7 @@ class MultiDevice : public Device {
|
||||
return total_tasks;
|
||||
}
|
||||
|
||||
void task_add(DeviceTask &task)
|
||||
void task_add(DeviceTask &task) override
|
||||
{
|
||||
list<SubDevice> task_devices = devices;
|
||||
if (!denoising_devices.empty()) {
|
||||
@@ -743,7 +801,7 @@ class MultiDevice : public Device {
|
||||
}
|
||||
}
|
||||
|
||||
void task_wait()
|
||||
void task_wait() override
|
||||
{
|
||||
foreach (SubDevice &sub, devices)
|
||||
sub.device->task_wait();
|
||||
@@ -751,7 +809,7 @@ class MultiDevice : public Device {
|
||||
sub.device->task_wait();
|
||||
}
|
||||
|
||||
void task_cancel()
|
||||
void task_cancel() override
|
||||
{
|
||||
foreach (SubDevice &sub, devices)
|
||||
sub.device->task_cancel();
|
||||
|
@@ -31,6 +31,7 @@
|
||||
# include "util/util_logging.h"
|
||||
# include "util/util_md5.h"
|
||||
# include "util/util_path.h"
|
||||
# include "util/util_progress.h"
|
||||
# include "util/util_time.h"
|
||||
|
||||
# ifdef WITH_CUDA_DYNLOAD
|
||||
@@ -186,7 +187,6 @@ class OptiXDevice : public CUDADevice {
|
||||
bool motion_blur = false;
|
||||
device_vector<SbtRecord> sbt_data;
|
||||
device_only_memory<KernelParams> launch_params;
|
||||
vector<CUdeviceptr> as_mem;
|
||||
OptixTraversableHandle tlas_handle = 0;
|
||||
|
||||
OptixDenoiser denoiser = NULL;
|
||||
@@ -258,11 +258,6 @@ class OptiXDevice : public CUDADevice {
|
||||
// Make CUDA context current
|
||||
const CUDAContextScope scope(cuContext);
|
||||
|
||||
// Free all acceleration structures
|
||||
for (CUdeviceptr mem : as_mem) {
|
||||
cuMemFree(mem);
|
||||
}
|
||||
|
||||
sbt_data.free();
|
||||
texture_info.free();
|
||||
launch_params.free();
|
||||
@@ -319,6 +314,14 @@ class OptiXDevice : public CUDADevice {
|
||||
common_cflags += string_printf(" -I\"%s/include\"", optix_sdk_path);
|
||||
}
|
||||
|
||||
// Specialization for shader raytracing
|
||||
if (requested_features.use_shader_raytrace) {
|
||||
common_cflags += " --keep-device-functions";
|
||||
}
|
||||
else {
|
||||
common_cflags += " -D __NO_SHADER_RAYTRACE__";
|
||||
}
|
||||
|
||||
return common_cflags;
|
||||
}
|
||||
|
||||
@@ -1136,11 +1139,10 @@ class OptiXDevice : public CUDADevice {
|
||||
}
|
||||
}
|
||||
|
||||
bool build_optix_bvh(const OptixBuildInput &build_input,
|
||||
uint16_t num_motion_steps,
|
||||
OptixTraversableHandle &out_handle,
|
||||
CUdeviceptr &out_data,
|
||||
OptixBuildOperation operation)
|
||||
bool build_optix_bvh(BVHOptiX *bvh,
|
||||
OptixBuildOperation operation,
|
||||
const OptixBuildInput &build_input,
|
||||
uint16_t num_motion_steps)
|
||||
{
|
||||
const CUDAContextScope scope(cuContext);
|
||||
|
||||
@@ -1166,24 +1168,21 @@ class OptiXDevice : public CUDADevice {
|
||||
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
|
||||
|
||||
// Allocate required output buffers
|
||||
device_only_memory<char> temp_mem(this, "temp_build_mem");
|
||||
device_only_memory<char> temp_mem(this, "optix temp as build mem");
|
||||
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
|
||||
if (!temp_mem.device_pointer)
|
||||
return false; // Make sure temporary memory allocation succeeded
|
||||
|
||||
// Move textures to host memory if there is not enough room
|
||||
size_t size = 0, free = 0;
|
||||
cuMemGetInfo(&free, &size);
|
||||
size = sizes.outputSizeInBytes + device_working_headroom;
|
||||
if (size >= free && can_map_host) {
|
||||
move_textures_to_host(size - free, false);
|
||||
}
|
||||
|
||||
device_only_memory<char> &out_data = bvh->as_data;
|
||||
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
|
||||
check_result_cuda_ret(cuMemAlloc(&out_data, sizes.outputSizeInBytes));
|
||||
assert(out_data.device == this);
|
||||
out_data.alloc_to_device(sizes.outputSizeInBytes);
|
||||
if (!out_data.device_pointer)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes);
|
||||
}
|
||||
|
||||
as_mem.push_back(out_data);
|
||||
|
||||
// Finally build the acceleration structure
|
||||
OptixAccelEmitDesc compacted_size_prop;
|
||||
@@ -1192,6 +1191,7 @@ class OptiXDevice : public CUDADevice {
|
||||
// Make sure this pointer is 8-byte aligned
|
||||
compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8);
|
||||
|
||||
OptixTraversableHandle out_handle = 0;
|
||||
check_result_optix_ret(optixAccelBuild(context,
|
||||
NULL,
|
||||
&options,
|
||||
@@ -1199,11 +1199,12 @@ class OptiXDevice : public CUDADevice {
|
||||
1,
|
||||
temp_mem.device_pointer,
|
||||
sizes.tempSizeInBytes,
|
||||
out_data,
|
||||
out_data.device_pointer,
|
||||
sizes.outputSizeInBytes,
|
||||
&out_handle,
|
||||
background ? &compacted_size_prop : NULL,
|
||||
background ? 1 : 0));
|
||||
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
|
||||
|
||||
// Wait for all operations to finish
|
||||
check_result_cuda_ret(cuStreamSynchronize(NULL));
|
||||
@@ -1219,81 +1220,66 @@ class OptiXDevice : public CUDADevice {
|
||||
|
||||
// There is no point compacting if the size does not change
|
||||
if (compacted_size < sizes.outputSizeInBytes) {
|
||||
CUdeviceptr compacted_data = 0;
|
||||
if (cuMemAlloc(&compacted_data, compacted_size) != CUDA_SUCCESS)
|
||||
device_only_memory<char> compacted_data(this, "optix compacted as");
|
||||
compacted_data.alloc_to_device(compacted_size);
|
||||
if (!compacted_data.device_pointer)
|
||||
// Do not compact if memory allocation for compacted acceleration structure fails
|
||||
// Can just use the uncompacted one then, so succeed here regardless
|
||||
return true;
|
||||
as_mem.push_back(compacted_data);
|
||||
|
||||
check_result_optix_ret(optixAccelCompact(
|
||||
context, NULL, out_handle, compacted_data, compacted_size, &out_handle));
|
||||
check_result_optix_ret(optixAccelCompact(context,
|
||||
NULL,
|
||||
out_handle,
|
||||
compacted_data.device_pointer,
|
||||
compacted_size,
|
||||
&out_handle));
|
||||
bvh->traversable_handle = static_cast<uint64_t>(out_handle);
|
||||
|
||||
// Wait for compaction to finish
|
||||
check_result_cuda_ret(cuStreamSynchronize(NULL));
|
||||
|
||||
// Free uncompacted acceleration structure
|
||||
cuMemFree(out_data);
|
||||
as_mem.erase(as_mem.end() - 2); // Remove 'out_data' from 'as_mem' array
|
||||
std::swap(out_data.device_size, compacted_data.device_size);
|
||||
std::swap(out_data.device_pointer, compacted_data.device_pointer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool build_optix_bvh(BVH *bvh) override
|
||||
void build_bvh(BVH *bvh, Progress &progress, bool refit) override
|
||||
{
|
||||
assert(bvh->params.top_level);
|
||||
|
||||
unsigned int num_instances = 0;
|
||||
unordered_map<Geometry *, OptixTraversableHandle> geometry;
|
||||
geometry.reserve(bvh->geometry.size());
|
||||
|
||||
// Free all previous acceleration structures which can not be refit
|
||||
std::set<CUdeviceptr> refit_mem;
|
||||
|
||||
for (Geometry *geom : bvh->geometry) {
|
||||
if (static_cast<BVHOptiX *>(geom->bvh)->do_refit) {
|
||||
refit_mem.insert(static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle);
|
||||
}
|
||||
if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) {
|
||||
/* For baking CUDA is used, build appropriate BVH for that. */
|
||||
Device::build_bvh(bvh, progress, refit);
|
||||
return;
|
||||
}
|
||||
|
||||
for (CUdeviceptr mem : as_mem) {
|
||||
if (refit_mem.find(mem) == refit_mem.end()) {
|
||||
cuMemFree(mem);
|
||||
}
|
||||
}
|
||||
BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
|
||||
|
||||
as_mem.clear();
|
||||
progress.set_substatus("Building OptiX acceleration structure");
|
||||
|
||||
// Build bottom level acceleration structures (BLAS)
|
||||
// Note: Always keep this logic in sync with bvh_optix.cpp!
|
||||
for (Object *ob : bvh->objects) {
|
||||
// Skip geometry for which acceleration structure already exists
|
||||
Geometry *geom = ob->get_geometry();
|
||||
if (geometry.find(geom) != geometry.end())
|
||||
continue;
|
||||
if (!bvh->params.top_level) {
|
||||
assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1);
|
||||
|
||||
OptixTraversableHandle handle;
|
||||
OptixBuildOperation operation;
|
||||
CUdeviceptr out_data;
|
||||
// Refit is only possible in viewport for now.
|
||||
if (static_cast<BVHOptiX *>(geom->bvh)->do_refit && !background) {
|
||||
out_data = static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle;
|
||||
handle = static_cast<BVHOptiX *>(geom->bvh)->optix_handle;
|
||||
// Refit is only possible in viewport for now (because AS is built with
|
||||
// OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above)
|
||||
OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD;
|
||||
if (refit && !background) {
|
||||
assert(bvh_optix->traversable_handle != 0);
|
||||
operation = OPTIX_BUILD_OPERATION_UPDATE;
|
||||
}
|
||||
else {
|
||||
out_data = 0;
|
||||
handle = 0;
|
||||
operation = OPTIX_BUILD_OPERATION_BUILD;
|
||||
bvh_optix->as_data.free();
|
||||
bvh_optix->traversable_handle = 0;
|
||||
}
|
||||
|
||||
// Build bottom level acceleration structures (BLAS)
|
||||
Geometry *const geom = bvh->geometry[0];
|
||||
if (geom->geometry_type == Geometry::HAIR) {
|
||||
// Build BLAS for curve primitives
|
||||
Hair *const hair = static_cast<Hair *const>(ob->get_geometry());
|
||||
Hair *const hair = static_cast<Hair *const>(geom);
|
||||
if (hair->num_curves() == 0) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t num_segments = hair->num_segments();
|
||||
@@ -1304,10 +1290,10 @@ class OptiXDevice : public CUDADevice {
|
||||
num_motion_steps = hair->get_motion_steps();
|
||||
}
|
||||
|
||||
device_vector<OptixAabb> aabb_data(this, "temp_aabb_data", MEM_READ_ONLY);
|
||||
device_vector<OptixAabb> aabb_data(this, "optix temp aabb data", MEM_READ_ONLY);
|
||||
# if OPTIX_ABI_VERSION >= 36
|
||||
device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
|
||||
device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
|
||||
device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
|
||||
device_vector<float4> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
|
||||
// Four control points for each curve segment
|
||||
const size_t num_vertices = num_segments * 4;
|
||||
if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) {
|
||||
@@ -1325,7 +1311,7 @@ class OptiXDevice : public CUDADevice {
|
||||
size_t center_step = (num_motion_steps - 1) / 2;
|
||||
if (step != center_step) {
|
||||
size_t attr_offset = (step > center_step) ? step - 1 : step;
|
||||
// Technically this is a float4 array, but sizeof(float3) is the same as sizeof(float4)
|
||||
// Technically this is a float4 array, but sizeof(float3) == sizeof(float4)
|
||||
keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size();
|
||||
}
|
||||
|
||||
@@ -1452,22 +1438,15 @@ class OptiXDevice : public CUDADevice {
|
||||
# endif
|
||||
}
|
||||
|
||||
// Allocate memory for new BLAS and build it
|
||||
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
|
||||
geometry.insert({ob->get_geometry(), handle});
|
||||
static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data;
|
||||
static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
|
||||
static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
|
||||
progress.set_error("Failed to build OptiX acceleration structure");
|
||||
}
|
||||
}
|
||||
else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
|
||||
// Build BLAS for triangle primitives
|
||||
Mesh *const mesh = static_cast<Mesh *const>(ob->get_geometry());
|
||||
Mesh *const mesh = static_cast<Mesh *const>(geom);
|
||||
if (mesh->num_triangles() == 0) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t num_verts = mesh->get_verts().size();
|
||||
@@ -1478,12 +1457,12 @@ class OptiXDevice : public CUDADevice {
|
||||
num_motion_steps = mesh->get_motion_steps();
|
||||
}
|
||||
|
||||
device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
|
||||
device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
|
||||
index_data.alloc(mesh->get_triangles().size());
|
||||
memcpy(index_data.data(),
|
||||
mesh->get_triangles().data(),
|
||||
mesh->get_triangles().size() * sizeof(int));
|
||||
device_vector<float3> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
|
||||
device_vector<float3> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
|
||||
vertex_data.alloc(num_verts * num_motion_steps);
|
||||
|
||||
for (size_t step = 0; step < num_motion_steps; ++step) {
|
||||
@@ -1528,190 +1507,221 @@ class OptiXDevice : public CUDADevice {
|
||||
build_input.triangleArray.numSbtRecords = 1;
|
||||
build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset;
|
||||
|
||||
// Allocate memory for new BLAS and build it
|
||||
if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
|
||||
geometry.insert({ob->get_geometry(), handle});
|
||||
static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data;
|
||||
static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
|
||||
static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
|
||||
progress.set_error("Failed to build OptiX acceleration structure");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned int num_instances = 0;
|
||||
unsigned int max_num_instances = 0xFFFFFFFF;
|
||||
|
||||
// Fill instance descriptions
|
||||
# if OPTIX_ABI_VERSION < 41
|
||||
device_vector<OptixAabb> aabbs(this, "tlas_aabbs", MEM_READ_ONLY);
|
||||
aabbs.alloc(bvh->objects.size());
|
||||
# endif
|
||||
device_vector<OptixInstance> instances(this, "tlas_instances", MEM_READ_ONLY);
|
||||
instances.alloc(bvh->objects.size());
|
||||
bvh_optix->as_data.free();
|
||||
bvh_optix->traversable_handle = 0;
|
||||
bvh_optix->motion_transform_data.free();
|
||||
|
||||
for (Object *ob : bvh->objects) {
|
||||
// Skip non-traceable objects
|
||||
if (!ob->is_traceable())
|
||||
continue;
|
||||
|
||||
// Create separate instance for triangle/curve meshes of an object
|
||||
const auto handle_it = geometry.find(ob->get_geometry());
|
||||
if (handle_it == geometry.end()) {
|
||||
continue;
|
||||
}
|
||||
OptixTraversableHandle handle = handle_it->second;
|
||||
|
||||
# if OPTIX_ABI_VERSION < 41
|
||||
OptixAabb &aabb = aabbs[num_instances];
|
||||
aabb.minX = ob->bounds.min.x;
|
||||
aabb.minY = ob->bounds.min.y;
|
||||
aabb.minZ = ob->bounds.min.z;
|
||||
aabb.maxX = ob->bounds.max.x;
|
||||
aabb.maxY = ob->bounds.max.y;
|
||||
aabb.maxZ = ob->bounds.max.z;
|
||||
# endif
|
||||
|
||||
OptixInstance &instance = instances[num_instances++];
|
||||
memset(&instance, 0, sizeof(instance));
|
||||
|
||||
// Clear transform to identity matrix
|
||||
instance.transform[0] = 1.0f;
|
||||
instance.transform[5] = 1.0f;
|
||||
instance.transform[10] = 1.0f;
|
||||
|
||||
// Set user instance ID to object index
|
||||
instance.instanceId = ob->get_device_index();
|
||||
|
||||
// Have to have at least one bit in the mask, or else instance would always be culled
|
||||
instance.visibilityMask = 1;
|
||||
|
||||
if (ob->get_geometry()->has_volume) {
|
||||
// Volumes have a special bit set in the visibility mask so a trace can mask only volumes
|
||||
instance.visibilityMask |= 2;
|
||||
optixDeviceContextGetProperty(context,
|
||||
OPTIX_DEVICE_PROPERTY_LIMIT_MAX_INSTANCE_ID,
|
||||
&max_num_instances,
|
||||
sizeof(max_num_instances));
|
||||
// Do not count first bit, which is used to distinguish instanced and non-instanced objects
|
||||
max_num_instances >>= 1;
|
||||
if (bvh->objects.size() > max_num_instances) {
|
||||
progress.set_error(
|
||||
"Failed to build OptiX acceleration structure because there are too many instances");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
|
||||
// Same applies to curves (so they can be skipped in local trace calls)
|
||||
instance.visibilityMask |= 4;
|
||||
// Fill instance descriptions
|
||||
# if OPTIX_ABI_VERSION < 41
|
||||
device_vector<OptixAabb> aabbs(this, "optix tlas aabbs", MEM_READ_ONLY);
|
||||
aabbs.alloc(bvh->objects.size());
|
||||
# endif
|
||||
device_vector<OptixInstance> instances(this, "optix tlas instances", MEM_READ_ONLY);
|
||||
instances.alloc(bvh->objects.size());
|
||||
|
||||
// Calculate total motion transform size and allocate memory for them
|
||||
size_t motion_transform_offset = 0;
|
||||
if (motion_blur) {
|
||||
size_t total_motion_transform_size = 0;
|
||||
for (Object *const ob : bvh->objects) {
|
||||
if (ob->is_traceable() && ob->use_motion()) {
|
||||
total_motion_transform_size = align_up(total_motion_transform_size,
|
||||
OPTIX_TRANSFORM_BYTE_ALIGNMENT);
|
||||
const size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
|
||||
total_motion_transform_size = total_motion_transform_size +
|
||||
sizeof(OptixSRTMotionTransform) +
|
||||
motion_keys * sizeof(OptixSRTData);
|
||||
}
|
||||
}
|
||||
|
||||
assert(bvh_optix->motion_transform_data.device == this);
|
||||
bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size);
|
||||
}
|
||||
|
||||
for (Object *ob : bvh->objects) {
|
||||
// Skip non-traceable objects
|
||||
if (!ob->is_traceable())
|
||||
continue;
|
||||
|
||||
BVHOptiX *const blas = static_cast<BVHOptiX *>(ob->get_geometry()->bvh);
|
||||
OptixTraversableHandle handle = blas->traversable_handle;
|
||||
|
||||
# if OPTIX_ABI_VERSION < 41
|
||||
OptixAabb &aabb = aabbs[num_instances];
|
||||
aabb.minX = ob->bounds.min.x;
|
||||
aabb.minY = ob->bounds.min.y;
|
||||
aabb.minZ = ob->bounds.min.z;
|
||||
aabb.maxX = ob->bounds.max.x;
|
||||
aabb.maxY = ob->bounds.max.y;
|
||||
aabb.maxZ = ob->bounds.max.z;
|
||||
# endif
|
||||
|
||||
OptixInstance &instance = instances[num_instances++];
|
||||
memset(&instance, 0, sizeof(instance));
|
||||
|
||||
// Clear transform to identity matrix
|
||||
instance.transform[0] = 1.0f;
|
||||
instance.transform[5] = 1.0f;
|
||||
instance.transform[10] = 1.0f;
|
||||
|
||||
// Set user instance ID to object index (but leave low bit blank)
|
||||
instance.instanceId = ob->get_device_index() << 1;
|
||||
|
||||
// Have to have at least one bit in the mask, or else instance would always be culled
|
||||
instance.visibilityMask = 1;
|
||||
|
||||
if (ob->get_geometry()->has_volume) {
|
||||
// Volumes have a special bit set in the visibility mask so a trace can mask only volumes
|
||||
instance.visibilityMask |= 2;
|
||||
}
|
||||
|
||||
if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
|
||||
// Same applies to curves (so they can be skipped in local trace calls)
|
||||
instance.visibilityMask |= 4;
|
||||
|
||||
# if OPTIX_ABI_VERSION >= 36
|
||||
if (motion_blur && ob->get_geometry()->has_motion_blur() &&
|
||||
DebugFlags().optix.curves_api &&
|
||||
static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
|
||||
// Select between motion blur and non-motion blur built-in intersection module
|
||||
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
|
||||
}
|
||||
if (motion_blur && ob->get_geometry()->has_motion_blur() &&
|
||||
DebugFlags().optix.curves_api &&
|
||||
static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
|
||||
// Select between motion blur and non-motion blur built-in intersection module
|
||||
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
// Insert motion traversable if object has motion
|
||||
if (motion_blur && ob->use_motion()) {
|
||||
size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
|
||||
size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
|
||||
motion_keys * sizeof(OptixSRTData);
|
||||
|
||||
const CUDAContextScope scope(cuContext);
|
||||
|
||||
CUdeviceptr motion_transform_gpu = 0;
|
||||
check_result_cuda_ret(cuMemAlloc(&motion_transform_gpu, motion_transform_size));
|
||||
as_mem.push_back(motion_transform_gpu);
|
||||
|
||||
// Allocate host side memory for motion transform and fill it with transform data
|
||||
OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
|
||||
new uint8_t[motion_transform_size]);
|
||||
motion_transform.child = handle;
|
||||
motion_transform.motionOptions.numKeys = ob->get_motion().size();
|
||||
motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
|
||||
motion_transform.motionOptions.timeBegin = 0.0f;
|
||||
motion_transform.motionOptions.timeEnd = 1.0f;
|
||||
|
||||
OptixSRTData *const srt_data = motion_transform.srtData;
|
||||
array<DecomposedTransform> decomp(ob->get_motion().size());
|
||||
transform_motion_decompose(
|
||||
decomp.data(), ob->get_motion().data(), ob->get_motion().size());
|
||||
|
||||
for (size_t i = 0; i < ob->get_motion().size(); ++i) {
|
||||
// Scale
|
||||
srt_data[i].sx = decomp[i].y.w; // scale.x.x
|
||||
srt_data[i].sy = decomp[i].z.w; // scale.y.y
|
||||
srt_data[i].sz = decomp[i].w.w; // scale.z.z
|
||||
|
||||
// Shear
|
||||
srt_data[i].a = decomp[i].z.x; // scale.x.y
|
||||
srt_data[i].b = decomp[i].z.y; // scale.x.z
|
||||
srt_data[i].c = decomp[i].w.x; // scale.y.z
|
||||
assert(decomp[i].z.z == 0.0f); // scale.y.x
|
||||
assert(decomp[i].w.y == 0.0f); // scale.z.x
|
||||
assert(decomp[i].w.z == 0.0f); // scale.z.y
|
||||
|
||||
// Pivot point
|
||||
srt_data[i].pvx = 0.0f;
|
||||
srt_data[i].pvy = 0.0f;
|
||||
srt_data[i].pvz = 0.0f;
|
||||
|
||||
// Rotation
|
||||
srt_data[i].qx = decomp[i].x.x;
|
||||
srt_data[i].qy = decomp[i].x.y;
|
||||
srt_data[i].qz = decomp[i].x.z;
|
||||
srt_data[i].qw = decomp[i].x.w;
|
||||
|
||||
// Translation
|
||||
srt_data[i].tx = decomp[i].y.x;
|
||||
srt_data[i].ty = decomp[i].y.y;
|
||||
srt_data[i].tz = decomp[i].y.z;
|
||||
}
|
||||
|
||||
// Upload motion transform to GPU
|
||||
cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
|
||||
delete[] reinterpret_cast<uint8_t *>(&motion_transform);
|
||||
// Insert motion traversable if object has motion
|
||||
if (motion_blur && ob->use_motion()) {
|
||||
size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
|
||||
size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
|
||||
motion_keys * sizeof(OptixSRTData);
|
||||
|
||||
// Disable instance transform if object uses motion transform already
|
||||
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
|
||||
const CUDAContextScope scope(cuContext);
|
||||
|
||||
// Get traversable handle to motion transform
|
||||
optixConvertPointerToTraversableHandle(context,
|
||||
motion_transform_gpu,
|
||||
OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
|
||||
&instance.traversableHandle);
|
||||
}
|
||||
else {
|
||||
instance.traversableHandle = handle;
|
||||
motion_transform_offset = align_up(motion_transform_offset,
|
||||
OPTIX_TRANSFORM_BYTE_ALIGNMENT);
|
||||
CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer +
|
||||
motion_transform_offset;
|
||||
motion_transform_offset += motion_transform_size;
|
||||
|
||||
if (ob->get_geometry()->is_instanced()) {
|
||||
// Set transform matrix
|
||||
memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
|
||||
// Allocate host side memory for motion transform and fill it with transform data
|
||||
OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
|
||||
new uint8_t[motion_transform_size]);
|
||||
motion_transform.child = handle;
|
||||
motion_transform.motionOptions.numKeys = ob->get_motion().size();
|
||||
motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
|
||||
motion_transform.motionOptions.timeBegin = 0.0f;
|
||||
motion_transform.motionOptions.timeEnd = 1.0f;
|
||||
|
||||
OptixSRTData *const srt_data = motion_transform.srtData;
|
||||
array<DecomposedTransform> decomp(ob->get_motion().size());
|
||||
transform_motion_decompose(
|
||||
decomp.data(), ob->get_motion().data(), ob->get_motion().size());
|
||||
|
||||
for (size_t i = 0; i < ob->get_motion().size(); ++i) {
|
||||
// Scale
|
||||
srt_data[i].sx = decomp[i].y.w; // scale.x.x
|
||||
srt_data[i].sy = decomp[i].z.w; // scale.y.y
|
||||
srt_data[i].sz = decomp[i].w.w; // scale.z.z
|
||||
|
||||
// Shear
|
||||
srt_data[i].a = decomp[i].z.x; // scale.x.y
|
||||
srt_data[i].b = decomp[i].z.y; // scale.x.z
|
||||
srt_data[i].c = decomp[i].w.x; // scale.y.z
|
||||
assert(decomp[i].z.z == 0.0f); // scale.y.x
|
||||
assert(decomp[i].w.y == 0.0f); // scale.z.x
|
||||
assert(decomp[i].w.z == 0.0f); // scale.z.y
|
||||
|
||||
// Pivot point
|
||||
srt_data[i].pvx = 0.0f;
|
||||
srt_data[i].pvy = 0.0f;
|
||||
srt_data[i].pvz = 0.0f;
|
||||
|
||||
// Rotation
|
||||
srt_data[i].qx = decomp[i].x.x;
|
||||
srt_data[i].qy = decomp[i].x.y;
|
||||
srt_data[i].qz = decomp[i].x.z;
|
||||
srt_data[i].qw = decomp[i].x.w;
|
||||
|
||||
// Translation
|
||||
srt_data[i].tx = decomp[i].y.x;
|
||||
srt_data[i].ty = decomp[i].y.y;
|
||||
srt_data[i].tz = decomp[i].y.z;
|
||||
}
|
||||
|
||||
// Upload motion transform to GPU
|
||||
cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
|
||||
delete[] reinterpret_cast<uint8_t *>(&motion_transform);
|
||||
|
||||
// Disable instance transform if object uses motion transform already
|
||||
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
|
||||
|
||||
// Get traversable handle to motion transform
|
||||
optixConvertPointerToTraversableHandle(context,
|
||||
motion_transform_gpu,
|
||||
OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
|
||||
&instance.traversableHandle);
|
||||
}
|
||||
else {
|
||||
// Disable instance transform if geometry already has it applied to vertex data
|
||||
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
|
||||
// Non-instanced objects read ID from prim_object, so
|
||||
// distinguish them from instanced objects with high bit set
|
||||
instance.instanceId |= 0x800000;
|
||||
instance.traversableHandle = handle;
|
||||
|
||||
if (ob->get_geometry()->is_instanced()) {
|
||||
// Set transform matrix
|
||||
memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
|
||||
}
|
||||
else {
|
||||
// Disable instance transform if geometry already has it applied to vertex data
|
||||
instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
|
||||
// Non-instanced objects read ID from 'prim_object', so distinguish
|
||||
// them from instanced objects with the low bit set
|
||||
instance.instanceId |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upload instance descriptions
|
||||
// Upload instance descriptions
|
||||
# if OPTIX_ABI_VERSION < 41
|
||||
aabbs.resize(num_instances);
|
||||
aabbs.copy_to_device();
|
||||
aabbs.resize(num_instances);
|
||||
aabbs.copy_to_device();
|
||||
# endif
|
||||
instances.resize(num_instances);
|
||||
instances.copy_to_device();
|
||||
instances.resize(num_instances);
|
||||
instances.copy_to_device();
|
||||
|
||||
// Build top-level acceleration structure (TLAS)
|
||||
OptixBuildInput build_input = {};
|
||||
build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
|
||||
// Build top-level acceleration structure (TLAS)
|
||||
OptixBuildInput build_input = {};
|
||||
build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
|
||||
# if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2
|
||||
build_input.instanceArray.aabbs = aabbs.device_pointer;
|
||||
build_input.instanceArray.numAabbs = num_instances;
|
||||
build_input.instanceArray.aabbs = aabbs.device_pointer;
|
||||
build_input.instanceArray.numAabbs = num_instances;
|
||||
# endif
|
||||
build_input.instanceArray.instances = instances.device_pointer;
|
||||
build_input.instanceArray.numInstances = num_instances;
|
||||
build_input.instanceArray.instances = instances.device_pointer;
|
||||
build_input.instanceArray.numInstances = num_instances;
|
||||
|
||||
CUdeviceptr out_data = 0;
|
||||
tlas_handle = 0;
|
||||
return build_optix_bvh(build_input, 0, tlas_handle, out_data, OPTIX_BUILD_OPERATION_BUILD);
|
||||
if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) {
|
||||
progress.set_error("Failed to build OptiX acceleration structure");
|
||||
}
|
||||
tlas_handle = bvh_optix->traversable_handle;
|
||||
}
|
||||
}
|
||||
|
||||
void const_copy_to(const char *name, void *host, size_t size) override
|
||||
@@ -1724,7 +1734,7 @@ class OptiXDevice : public CUDADevice {
|
||||
if (strcmp(name, "__data") == 0) {
|
||||
assert(size <= sizeof(KernelData));
|
||||
|
||||
// Fix traversable handle on multi devices
|
||||
// Update traversable handle (since it is different for each device on multi devices)
|
||||
KernelData *const data = (KernelData *)host;
|
||||
*(OptixTraversableHandle *)&data->bvh.scene = tlas_handle;
|
||||
|
||||
|
@@ -780,13 +780,25 @@ bool OpenCLInfo::device_supported(const string &platform_name, const cl_device_i
|
||||
return true;
|
||||
}
|
||||
|
||||
/* It is possible to have Iris GPU on AMD/Apple OpenCL framework
|
||||
* (aka, it will not be on Intel framework). This isn't supported
|
||||
* and needs an explicit blacklist.
|
||||
*/
|
||||
if (strstr(device_name.c_str(), "Iris")) {
|
||||
/* Allow Intel GPUs on Intel OpenCL platform. */
|
||||
if (platform_name.find("Intel") != string::npos) {
|
||||
if (device_type != CL_DEVICE_TYPE_GPU) {
|
||||
/* OpenCL on Intel CPU is not an officially supported configuration.
|
||||
* Use hybrid CPU+GPU rendering to utilize both GPU and CPU. */
|
||||
return false;
|
||||
}
|
||||
|
||||
# ifdef __APPLE__
|
||||
/* Apple uses own framework, which can also put Iris onto AMD frame-work.
|
||||
* This isn't supported configuration. */
|
||||
return false;
|
||||
# else
|
||||
if (device_name.find("Iris") != string::npos || device_name.find("Xe") != string::npos) {
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
if (platform_name == "AMD Accelerated Parallel Processing" &&
|
||||
device_type == CL_DEVICE_TYPE_GPU) {
|
||||
if (driver_major < 2236) {
|
||||
|
@@ -112,8 +112,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg,
|
||||
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
|
||||
rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0]));
|
||||
isect->prim = hit->primID +
|
||||
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) +
|
||||
kernel_tex_fetch(__object_node, hit->instID[0] / 2);
|
||||
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
|
||||
isect->object = hit->instID[0] / 2;
|
||||
}
|
||||
else {
|
||||
@@ -137,8 +136,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg,
|
||||
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
|
||||
rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2));
|
||||
isect->prim = hit->primID +
|
||||
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) +
|
||||
kernel_tex_fetch(__object_node, local_object_id);
|
||||
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
|
||||
isect->object = local_object_id;
|
||||
isect->type = kernel_tex_fetch(__prim_type, isect->prim);
|
||||
}
|
||||
|
@@ -214,13 +214,6 @@ ccl_device_inline float3 object_location(KernelGlobals *kg, const ShaderData *sd
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Total surface area of object */
|
||||
|
||||
ccl_device_inline float object_surface_area(KernelGlobals *kg, int object)
|
||||
{
|
||||
return kernel_tex_fetch(__objects, object).surface_area;
|
||||
}
|
||||
|
||||
/* Color of the object */
|
||||
|
||||
ccl_device_inline float3 object_color(KernelGlobals *kg, int object)
|
||||
@@ -328,7 +321,7 @@ ccl_device_inline float object_volume_density(KernelGlobals *kg, int object)
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return kernel_tex_fetch(__objects, object).surface_area;
|
||||
return kernel_tex_fetch(__objects, object).volume_density;
|
||||
}
|
||||
|
||||
ccl_device_inline float object_volume_step_size(KernelGlobals *kg, int object)
|
||||
|
@@ -1397,10 +1397,12 @@ typedef enum KernelBVHLayout {
|
||||
BVH_LAYOUT_BVH2 = (1 << 0),
|
||||
BVH_LAYOUT_EMBREE = (1 << 1),
|
||||
BVH_LAYOUT_OPTIX = (1 << 2),
|
||||
BVH_LAYOUT_MULTI_OPTIX = (1 << 3),
|
||||
BVH_LAYOUT_MULTI_OPTIX_EMBREE = (1 << 4),
|
||||
|
||||
/* Default BVH layout to use for CPU. */
|
||||
BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE,
|
||||
BVH_LAYOUT_ALL = (unsigned int)(~0u),
|
||||
BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX,
|
||||
} KernelBVHLayout;
|
||||
|
||||
typedef struct KernelBVH {
|
||||
@@ -1459,7 +1461,7 @@ typedef struct KernelObject {
|
||||
Transform tfm;
|
||||
Transform itfm;
|
||||
|
||||
float surface_area;
|
||||
float volume_density;
|
||||
float pass_id;
|
||||
float random_number;
|
||||
float color[3];
|
||||
|
@@ -45,13 +45,12 @@ template<bool always = false> ccl_device_forceinline uint get_object_id()
|
||||
uint object = optixGetInstanceId();
|
||||
#endif
|
||||
// Choose between always returning object ID or only for instances
|
||||
if (always)
|
||||
// Can just remove the high bit since instance always contains object ID
|
||||
return object & 0x7FFFFF;
|
||||
// Set to OBJECT_NONE if this is not an instanced object
|
||||
else if (object & 0x800000)
|
||||
object = OBJECT_NONE;
|
||||
return object;
|
||||
if (always || (object & 1) == 0)
|
||||
// Can just remove the low bit since instance always contains object ID
|
||||
return object >> 1;
|
||||
else
|
||||
// Set to OBJECT_NONE if this is not an instanced object
|
||||
return OBJECT_NONE;
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_path_trace()
|
||||
|
@@ -109,7 +109,7 @@ static void shaderdata_to_shaderglobals(
|
||||
globals->dvdy = sd->dv.dy;
|
||||
globals->dPdu = TO_VEC3(sd->dPdu);
|
||||
globals->dPdv = TO_VEC3(sd->dPdv);
|
||||
globals->surfacearea = (sd->object == OBJECT_NONE) ? 1.0f : object_surface_area(kg, sd->object);
|
||||
globals->surfacearea = 1.0f;
|
||||
globals->time = sd->time;
|
||||
|
||||
/* booleans */
|
||||
|
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
#include "bvh/bvh_build.h"
|
||||
#include "bvh/bvh_embree.h"
|
||||
#include "bvh/bvh2.h"
|
||||
|
||||
#include "device/device.h"
|
||||
|
||||
@@ -41,6 +40,7 @@
|
||||
#include "util/util_foreach.h"
|
||||
#include "util/util_logging.h"
|
||||
#include "util/util_progress.h"
|
||||
#include "util/util_task.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
@@ -162,7 +162,8 @@ int Geometry::motion_step(float time) const
|
||||
|
||||
bool Geometry::need_build_bvh(BVHLayout layout) const
|
||||
{
|
||||
return !transform_applied || has_surface_bssrdf || layout == BVH_LAYOUT_OPTIX;
|
||||
return is_instanced() || layout == BVH_LAYOUT_OPTIX || layout == BVH_LAYOUT_MULTI_OPTIX ||
|
||||
layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE;
|
||||
}
|
||||
|
||||
bool Geometry::is_instanced() const
|
||||
@@ -218,7 +219,7 @@ void Geometry::compute_bvh(
|
||||
bvh->geometry = geometry;
|
||||
bvh->objects = objects;
|
||||
|
||||
bvh->refit(*progress);
|
||||
device->build_bvh(bvh, *progress, true);
|
||||
}
|
||||
else {
|
||||
progress->set_status(msg, "Building BVH");
|
||||
@@ -235,7 +236,7 @@ void Geometry::compute_bvh(
|
||||
|
||||
delete bvh;
|
||||
bvh = BVH::create(bparams, geometry, objects, device);
|
||||
MEM_GUARDED_CALL(progress, bvh->build, *progress);
|
||||
MEM_GUARDED_CALL(progress, device->build_bvh, bvh, *progress, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +280,15 @@ void Geometry::tag_update(Scene *scene, bool rebuild)
|
||||
scene->object_manager->need_update = true;
|
||||
}
|
||||
|
||||
void Geometry::tag_bvh_update(bool rebuild)
|
||||
{
|
||||
tag_modified();
|
||||
|
||||
if (rebuild) {
|
||||
need_update_rebuild = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Geometry Manager */
|
||||
|
||||
GeometryManager::GeometryManager()
|
||||
@@ -914,7 +924,7 @@ void GeometryManager::device_update_attributes(Device *device,
|
||||
scene->object_manager->device_update_mesh_offsets(device, dscene, scene);
|
||||
}
|
||||
|
||||
void GeometryManager::mesh_calc_offset(Scene *scene)
|
||||
void GeometryManager::mesh_calc_offset(Scene *scene, BVHLayout bvh_layout)
|
||||
{
|
||||
size_t vert_size = 0;
|
||||
size_t tri_size = 0;
|
||||
@@ -929,6 +939,14 @@ void GeometryManager::mesh_calc_offset(Scene *scene)
|
||||
size_t optix_prim_size = 0;
|
||||
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
if (geom->optix_prim_offset != optix_prim_size) {
|
||||
/* Need to rebuild BVH in OptiX, since refit only allows modified mesh data there */
|
||||
const bool has_optix_bvh = bvh_layout == BVH_LAYOUT_OPTIX ||
|
||||
bvh_layout == BVH_LAYOUT_MULTI_OPTIX ||
|
||||
bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE;
|
||||
geom->tag_bvh_update(has_optix_bvh);
|
||||
}
|
||||
|
||||
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
|
||||
@@ -1162,25 +1180,66 @@ void GeometryManager::device_update_bvh(Device *device,
|
||||
|
||||
VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
|
||||
|
||||
BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
|
||||
bvh->build(progress, &device->stats);
|
||||
delete scene->bvh;
|
||||
BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
|
||||
device->build_bvh(bvh, progress, false);
|
||||
|
||||
if (progress.get_cancel()) {
|
||||
#ifdef WITH_EMBREE
|
||||
if (dscene->data.bvh.scene) {
|
||||
BVHEmbree::destroy(dscene->data.bvh.scene);
|
||||
dscene->data.bvh.scene = NULL;
|
||||
}
|
||||
#endif
|
||||
delete bvh;
|
||||
return;
|
||||
}
|
||||
|
||||
PackedBVH pack;
|
||||
if (bparams.bvh_layout == BVH_LAYOUT_BVH2) {
|
||||
pack = std::move(static_cast<BVH2 *>(bvh)->pack);
|
||||
}
|
||||
else {
|
||||
progress.set_status("Updating Scene BVH", "Packing BVH primitives");
|
||||
|
||||
size_t num_prims = 0;
|
||||
size_t num_tri_verts = 0;
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
num_prims += mesh->num_triangles();
|
||||
num_tri_verts += 3 * mesh->num_triangles();
|
||||
}
|
||||
else if (geom->is_hair()) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
num_prims += hair->num_segments();
|
||||
}
|
||||
}
|
||||
|
||||
pack.root_index = -1;
|
||||
pack.prim_tri_index.reserve(num_prims);
|
||||
pack.prim_tri_verts.reserve(num_tri_verts);
|
||||
pack.prim_type.reserve(num_prims);
|
||||
pack.prim_index.reserve(num_prims);
|
||||
pack.prim_object.reserve(num_prims);
|
||||
pack.prim_visibility.reserve(num_prims);
|
||||
|
||||
// Merge visibility flags of all objects and find object index for non-instanced geometry
|
||||
unordered_map<const Geometry *, pair<int, uint>> geometry_to_object_info;
|
||||
geometry_to_object_info.reserve(scene->geometry.size());
|
||||
foreach (Object *ob, scene->objects) {
|
||||
const Geometry *const geom = ob->get_geometry();
|
||||
pair<int, uint> &info = geometry_to_object_info[geom];
|
||||
info.second |= ob->visibility_for_tracing();
|
||||
if (!geom->is_instanced()) {
|
||||
info.first = ob->get_device_index();
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated
|
||||
// based on that list, which may be ordered differently from the object list.
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
const pair<int, uint> &info = geometry_to_object_info[geom];
|
||||
geom->pack_primitives(pack, info.first, info.second);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy to device */
|
||||
progress.set_status("Updating Scene BVH", "Copying BVH to device");
|
||||
|
||||
PackedBVH &pack = bvh->pack;
|
||||
|
||||
if (pack.nodes.size()) {
|
||||
dscene->bvh_nodes.steal_data(pack.nodes);
|
||||
dscene->bvh_nodes.copy_to_device();
|
||||
@@ -1226,10 +1285,8 @@ void GeometryManager::device_update_bvh(Device *device,
|
||||
dscene->data.bvh.bvh_layout = bparams.bvh_layout;
|
||||
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
|
||||
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
|
||||
|
||||
bvh->copy_to_device(progress, dscene);
|
||||
|
||||
delete bvh;
|
||||
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
|
||||
dscene->data.bvh.scene = NULL;
|
||||
}
|
||||
|
||||
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
|
||||
@@ -1486,7 +1543,9 @@ void GeometryManager::device_update(Device *device,
|
||||
/* Device update. */
|
||||
device_free(device, dscene);
|
||||
|
||||
mesh_calc_offset(scene);
|
||||
const BVHLayout bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
|
||||
device->get_bvh_layout_mask());
|
||||
mesh_calc_offset(scene, bvh_layout);
|
||||
if (true_displacement_used) {
|
||||
scoped_callback_timer timer([scene](double time) {
|
||||
if (scene->update_stats) {
|
||||
@@ -1513,8 +1572,6 @@ void GeometryManager::device_update(Device *device,
|
||||
}
|
||||
|
||||
/* Update displacement. */
|
||||
BVHLayout bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
|
||||
device->get_bvh_layout_mask());
|
||||
bool displacement_done = false;
|
||||
size_t num_bvh = 0;
|
||||
|
||||
@@ -1653,14 +1710,6 @@ void GeometryManager::device_update(Device *device,
|
||||
|
||||
void GeometryManager::device_free(Device *device, DeviceScene *dscene)
|
||||
{
|
||||
#ifdef WITH_EMBREE
|
||||
if (dscene->data.bvh.scene) {
|
||||
if (dscene->data.bvh.bvh_layout == BVH_LAYOUT_EMBREE)
|
||||
BVHEmbree::destroy(dscene->data.bvh.scene);
|
||||
dscene->data.bvh.scene = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
dscene->bvh_nodes.free();
|
||||
dscene->bvh_leaf_nodes.free();
|
||||
dscene->object_node.free();
|
||||
|
@@ -41,6 +41,7 @@ class Scene;
|
||||
class SceneParams;
|
||||
class Shader;
|
||||
class Volume;
|
||||
struct PackedBVH;
|
||||
|
||||
/* Geometry
|
||||
*
|
||||
@@ -124,6 +125,8 @@ class Geometry : public Node {
|
||||
int n,
|
||||
int total);
|
||||
|
||||
virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0;
|
||||
|
||||
/* Check whether the geometry should have own BVH built separately. Briefly,
|
||||
* own BVH is needed for geometry, if:
|
||||
*
|
||||
@@ -154,6 +157,8 @@ class Geometry : public Node {
|
||||
|
||||
/* Updates */
|
||||
void tag_update(Scene *scene, bool rebuild);
|
||||
|
||||
void tag_bvh_update(bool rebuild);
|
||||
};
|
||||
|
||||
/* Geometry Manager */
|
||||
@@ -195,7 +200,7 @@ class GeometryManager {
|
||||
vector<AttributeRequestSet> &object_attributes);
|
||||
|
||||
/* Compute verts/triangles/curves offsets in global arrays. */
|
||||
void mesh_calc_offset(Scene *scene);
|
||||
void mesh_calc_offset(Scene *scene, BVHLayout bvh_layout);
|
||||
|
||||
void device_update_object(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
|
||||
|
@@ -14,8 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "render/hair.h"
|
||||
#include "bvh/bvh.h"
|
||||
|
||||
#include "render/curves.h"
|
||||
#include "render/hair.h"
|
||||
#include "render/scene.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
@@ -492,4 +494,35 @@ void Hair::pack_curves(Scene *scene,
|
||||
}
|
||||
}
|
||||
|
||||
void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility)
|
||||
{
|
||||
if (curve_first_key.empty())
|
||||
return;
|
||||
|
||||
const size_t num_prims = num_segments();
|
||||
pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
|
||||
pack.prim_type.reserve(pack.prim_type.size() + num_prims);
|
||||
pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
|
||||
pack.prim_index.reserve(pack.prim_index.size() + num_prims);
|
||||
pack.prim_object.reserve(pack.prim_object.size() + num_prims);
|
||||
// 'pack.prim_time' is unused by Embree and OptiX
|
||||
|
||||
uint type = has_motion_blur() ?
|
||||
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
|
||||
PRIMITIVE_MOTION_CURVE_THICK) :
|
||||
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
|
||||
|
||||
for (size_t j = 0; j < num_curves(); ++j) {
|
||||
Curve curve = get_curve(j);
|
||||
for (size_t k = 0; k < curve.num_segments(); ++k) {
|
||||
pack.prim_tri_index.push_back_reserved(-1);
|
||||
pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
|
||||
pack.prim_visibility.push_back_reserved(visibility);
|
||||
// Each curve segment points back to its curve index
|
||||
pack.prim_index.push_back_reserved(j + prim_offset);
|
||||
pack.prim_object.push_back_reserved(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -145,6 +145,8 @@ class Hair : public Geometry {
|
||||
|
||||
/* BVH */
|
||||
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
|
||||
|
||||
void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -493,8 +493,8 @@ static bool image_associate_alpha(ImageManager::Image *img)
|
||||
template<TypeDesc::BASETYPE FileFormat, typename StorageType>
|
||||
bool ImageManager::file_load_image(Image *img, int texture_limit)
|
||||
{
|
||||
/* we only handle certain number of components */
|
||||
if (!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) {
|
||||
/* Ignore empty images. */
|
||||
if (!(img->metadata.channels > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -805,4 +805,35 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::pack_primitives(PackedBVH &pack, int object, uint visibility)
|
||||
{
|
||||
if (triangles.empty())
|
||||
return;
|
||||
|
||||
const size_t num_prims = num_triangles();
|
||||
pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
|
||||
pack.prim_tri_verts.reserve(pack.prim_tri_verts.size() + num_prims * 3);
|
||||
pack.prim_type.reserve(pack.prim_type.size() + num_prims);
|
||||
pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
|
||||
pack.prim_index.reserve(pack.prim_index.size() + num_prims);
|
||||
pack.prim_object.reserve(pack.prim_object.size() + num_prims);
|
||||
// 'pack.prim_time' is unused by Embree and OptiX
|
||||
|
||||
uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE;
|
||||
|
||||
for (size_t k = 0; k < num_prims; ++k) {
|
||||
pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size());
|
||||
|
||||
const Mesh::Triangle t = get_triangle(k);
|
||||
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[0]]));
|
||||
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[1]]));
|
||||
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[2]]));
|
||||
|
||||
pack.prim_type.push_back_reserved(type);
|
||||
pack.prim_visibility.push_back_reserved(visibility);
|
||||
pack.prim_index.push_back_reserved(k + prim_offset);
|
||||
pack.prim_object.push_back_reserved(object);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -184,9 +184,8 @@ class Mesh : public Geometry {
|
||||
unordered_multimap<int, int>
|
||||
vert_stitching_map; /* stitching index -> multiple real vert indices */
|
||||
|
||||
friend class BVH;
|
||||
friend class BVH2;
|
||||
friend class BVHBuild;
|
||||
friend class BVHEmbree;
|
||||
friend class BVHSpatialSplit;
|
||||
friend class DiagSplit;
|
||||
friend class EdgeDice;
|
||||
@@ -233,6 +232,8 @@ class Mesh : public Geometry {
|
||||
size_t tri_offset);
|
||||
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
|
||||
|
||||
void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
|
||||
|
||||
void tessellate(DiagSplit *split);
|
||||
|
||||
SubdFace get_subd_face(size_t index) const;
|
||||
|
@@ -55,12 +55,6 @@ struct UpdateObjectTransformState {
|
||||
*/
|
||||
map<ParticleSystem *, int> particle_offset;
|
||||
|
||||
/* Mesh area.
|
||||
* Used to avoid calculation of mesh area multiple times. Used for both
|
||||
* read and write. Acquire surface_area_lock to keep it all thread safe.
|
||||
*/
|
||||
map<Mesh *, float> surface_area_map;
|
||||
|
||||
/* Motion offsets for each object. */
|
||||
array<uint> motion_offset;
|
||||
|
||||
@@ -76,12 +70,8 @@ struct UpdateObjectTransformState {
|
||||
bool have_curves;
|
||||
|
||||
/* ** Scheduling queue. ** */
|
||||
|
||||
Scene *scene;
|
||||
|
||||
/* Some locks to keep everything thread-safe. */
|
||||
thread_spin_lock surface_area_lock;
|
||||
|
||||
/* First unused object index in the queue. */
|
||||
int queue_start_object;
|
||||
};
|
||||
@@ -379,80 +369,17 @@ ObjectManager::~ObjectManager()
|
||||
{
|
||||
}
|
||||
|
||||
static float object_surface_area(UpdateObjectTransformState *state,
|
||||
const Transform &tfm,
|
||||
Geometry *geom)
|
||||
static float object_volume_density(const Transform &tfm, Geometry *geom)
|
||||
{
|
||||
if (geom->geometry_type != Geometry::MESH && geom->geometry_type != Geometry::VOLUME) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->has_volume || geom->geometry_type == Geometry::VOLUME) {
|
||||
if (geom->geometry_type == Geometry::VOLUME) {
|
||||
/* Volume density automatically adjust to object scale. */
|
||||
if (geom->geometry_type == Geometry::VOLUME &&
|
||||
static_cast<Volume *>(geom)->get_object_space()) {
|
||||
if (static_cast<Volume *>(geom)->get_object_space()) {
|
||||
const float3 unit = normalize(make_float3(1.0f, 1.0f, 1.0f));
|
||||
return 1.0f / len(transform_direction(&tfm, unit));
|
||||
}
|
||||
else {
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute surface area. for uniform scale we can do avoid the many
|
||||
* transform calls and share computation for instances.
|
||||
*
|
||||
* TODO(brecht): Correct for displacement, and move to a better place.
|
||||
*/
|
||||
float surface_area = 0.0f;
|
||||
float uniform_scale;
|
||||
if (transform_uniform_scale(tfm, uniform_scale)) {
|
||||
map<Mesh *, float>::iterator it;
|
||||
|
||||
/* NOTE: This isn't fully optimal and could in theory lead to multiple
|
||||
* threads calculating area of the same mesh in parallel. However, this
|
||||
* also prevents suspending all the threads when some mesh's area is
|
||||
* not yet known.
|
||||
*/
|
||||
state->surface_area_lock.lock();
|
||||
it = state->surface_area_map.find(mesh);
|
||||
state->surface_area_lock.unlock();
|
||||
|
||||
if (it == state->surface_area_map.end()) {
|
||||
size_t num_triangles = mesh->num_triangles();
|
||||
for (size_t j = 0; j < num_triangles; j++) {
|
||||
Mesh::Triangle t = mesh->get_triangle(j);
|
||||
float3 p1 = mesh->get_verts()[t.v[0]];
|
||||
float3 p2 = mesh->get_verts()[t.v[1]];
|
||||
float3 p3 = mesh->get_verts()[t.v[2]];
|
||||
|
||||
surface_area += triangle_area(p1, p2, p3);
|
||||
}
|
||||
|
||||
state->surface_area_lock.lock();
|
||||
state->surface_area_map[mesh] = surface_area;
|
||||
state->surface_area_lock.unlock();
|
||||
}
|
||||
else {
|
||||
surface_area = it->second;
|
||||
}
|
||||
|
||||
surface_area *= uniform_scale;
|
||||
}
|
||||
else {
|
||||
size_t num_triangles = mesh->num_triangles();
|
||||
for (size_t j = 0; j < num_triangles; j++) {
|
||||
Mesh::Triangle t = mesh->get_triangle(j);
|
||||
float3 p1 = transform_point(&tfm, mesh->get_verts()[t.v[0]]);
|
||||
float3 p2 = transform_point(&tfm, mesh->get_verts()[t.v[1]]);
|
||||
float3 p3 = transform_point(&tfm, mesh->get_verts()[t.v[2]]);
|
||||
|
||||
surface_area += triangle_area(p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
return surface_area;
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void ObjectManager::device_update_object_transform(UpdateObjectTransformState *state, Object *ob)
|
||||
@@ -476,7 +403,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
|
||||
|
||||
kobject.tfm = tfm;
|
||||
kobject.itfm = itfm;
|
||||
kobject.surface_area = object_surface_area(state, tfm, geom);
|
||||
kobject.volume_density = object_volume_density(tfm, geom);
|
||||
kobject.color[0] = color.x;
|
||||
kobject.color[1] = color.y;
|
||||
kobject.color[2] = color.z;
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
#include "device/device.h"
|
||||
#include "render/background.h"
|
||||
#include "render/bake.h"
|
||||
@@ -100,6 +101,7 @@ Scene::Scene(const SceneParams ¶ms_, Device *device)
|
||||
{
|
||||
memset((void *)&dscene.data, 0, sizeof(dscene.data));
|
||||
|
||||
bvh = NULL;
|
||||
camera = create_node<Camera>();
|
||||
dicing_camera = create_node<Camera>();
|
||||
lookup_tables = new LookupTables();
|
||||
@@ -135,6 +137,9 @@ Scene::~Scene()
|
||||
|
||||
void Scene::free_memory(bool final)
|
||||
{
|
||||
delete bvh;
|
||||
bvh = NULL;
|
||||
|
||||
foreach (Shader *s, shaders)
|
||||
delete s;
|
||||
foreach (Geometry *g, geometry)
|
||||
|
@@ -38,6 +38,7 @@ CCL_NAMESPACE_BEGIN
|
||||
|
||||
class AttributeRequestSet;
|
||||
class Background;
|
||||
class BVH;
|
||||
class Camera;
|
||||
class Device;
|
||||
class DeviceInfo;
|
||||
@@ -220,6 +221,7 @@ class Scene : public NodeOwner {
|
||||
string name;
|
||||
|
||||
/* data */
|
||||
BVH *bvh;
|
||||
Camera *camera;
|
||||
Camera *dicing_camera;
|
||||
LookupTables *lookup_tables;
|
||||
|
@@ -347,8 +347,15 @@ void Shader::tag_update(Scene *scene)
|
||||
foreach (ShaderNode *node, graph->nodes)
|
||||
node->attributes(this, &attributes);
|
||||
|
||||
if (has_displacement && displacement_method == DISPLACE_BOTH) {
|
||||
attributes.add(ATTR_STD_POSITION_UNDISPLACED);
|
||||
if (has_displacement) {
|
||||
if (displacement_method == DISPLACE_BOTH) {
|
||||
attributes.add(ATTR_STD_POSITION_UNDISPLACED);
|
||||
}
|
||||
if (displacement_method_is_modified()) {
|
||||
need_update_geometry = true;
|
||||
scene->geometry_manager->need_update = true;
|
||||
scene->object_manager->need_flags_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* compare if the attributes changed, mesh manager will check
|
||||
|
@@ -548,22 +548,23 @@ void SVMCompiler::generated_shared_closure_nodes(ShaderNode *root_node,
|
||||
}
|
||||
}
|
||||
|
||||
void SVMCompiler::generate_aov_node(ShaderNode *node, CompilerState *state)
|
||||
void SVMCompiler::find_aov_nodes_and_dependencies(ShaderNodeSet &aov_nodes,
|
||||
ShaderGraph *graph,
|
||||
CompilerState *state)
|
||||
{
|
||||
/* execute dependencies for node */
|
||||
foreach (ShaderInput *in, node->inputs) {
|
||||
if (in->link != NULL) {
|
||||
ShaderNodeSet dependencies;
|
||||
find_dependencies(dependencies, state->nodes_done, in);
|
||||
generate_svm_nodes(dependencies, state);
|
||||
foreach (ShaderNode *node, graph->nodes) {
|
||||
if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
|
||||
OutputAOVNode *aov_node = static_cast<OutputAOVNode *>(node);
|
||||
if (aov_node->slot >= 0) {
|
||||
aov_nodes.insert(aov_node);
|
||||
foreach (ShaderInput *in, node->inputs) {
|
||||
if (in->link != NULL) {
|
||||
find_dependencies(aov_nodes, state->nodes_done, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* compile node itself */
|
||||
generate_node(node, state->nodes_done);
|
||||
|
||||
state->nodes_done.insert(node);
|
||||
state->nodes_done_flag[node->id] = true;
|
||||
}
|
||||
|
||||
void SVMCompiler::generate_multi_closure(ShaderNode *root_node,
|
||||
@@ -631,6 +632,25 @@ void SVMCompiler::generate_multi_closure(ShaderNode *root_node,
|
||||
}
|
||||
}
|
||||
|
||||
/* For dependencies AOV nodes, prevent them from being categorized
|
||||
* as exclusive deps of one or the other closure, since the need to
|
||||
* execute them for AOV writing is not dependent on the closure
|
||||
* weights. */
|
||||
if (state->aov_nodes.size()) {
|
||||
set_intersection(state->aov_nodes.begin(),
|
||||
state->aov_nodes.end(),
|
||||
cl1deps.begin(),
|
||||
cl1deps.end(),
|
||||
std::inserter(shareddeps, shareddeps.begin()),
|
||||
node_id_comp);
|
||||
set_intersection(state->aov_nodes.begin(),
|
||||
state->aov_nodes.end(),
|
||||
cl2deps.begin(),
|
||||
cl2deps.end(),
|
||||
std::inserter(shareddeps, shareddeps.begin()),
|
||||
node_id_comp);
|
||||
}
|
||||
|
||||
if (!shareddeps.empty()) {
|
||||
if (cl1in->link) {
|
||||
generated_shared_closure_nodes(root_node, cl1in->link->parent, state, shareddeps);
|
||||
@@ -782,6 +802,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
|
||||
}
|
||||
|
||||
if (generate) {
|
||||
if (type == SHADER_TYPE_SURFACE) {
|
||||
find_aov_nodes_and_dependencies(state.aov_nodes, graph, &state);
|
||||
}
|
||||
generate_multi_closure(clin->link->parent, clin->link->parent, &state);
|
||||
}
|
||||
}
|
||||
@@ -789,28 +812,15 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
|
||||
/* compile output node */
|
||||
output->compile(*this);
|
||||
|
||||
if (type == SHADER_TYPE_SURFACE) {
|
||||
vector<OutputAOVNode *> aov_outputs;
|
||||
foreach (ShaderNode *node, graph->nodes) {
|
||||
if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
|
||||
OutputAOVNode *aov_node = static_cast<OutputAOVNode *>(node);
|
||||
if (aov_node->slot >= 0) {
|
||||
aov_outputs.push_back(aov_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aov_outputs.size() > 0) {
|
||||
/* AOV passes are only written if the object is directly visible, so
|
||||
* there is no point in evaluating all the nodes generated only for the
|
||||
* AOV outputs if that's not the case. Therefore, we insert
|
||||
* NODE_AOV_START into the shader before the AOV-only nodes are
|
||||
* generated which tells the kernel that it can stop evaluation
|
||||
* early if AOVs will not be written. */
|
||||
add_node(NODE_AOV_START, 0, 0, 0);
|
||||
foreach (OutputAOVNode *node, aov_outputs) {
|
||||
generate_aov_node(node, &state);
|
||||
}
|
||||
}
|
||||
if (!state.aov_nodes.empty()) {
|
||||
/* AOV passes are only written if the object is directly visible, so
|
||||
* there is no point in evaluating all the nodes generated only for the
|
||||
* AOV outputs if that's not the case. Therefore, we insert
|
||||
* NODE_AOV_START into the shader before the AOV-only nodes are
|
||||
* generated which tells the kernel that it can stop evaluation
|
||||
* early if AOVs will not be written. */
|
||||
add_node(NODE_AOV_START, 0, 0, 0);
|
||||
generate_svm_nodes(state.aov_nodes, &state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -176,6 +176,9 @@ class SVMCompiler {
|
||||
/* Set of closures which were already compiled. */
|
||||
ShaderNodeSet closure_done;
|
||||
|
||||
/* Set of nodes used for writing AOVs. */
|
||||
ShaderNodeSet aov_nodes;
|
||||
|
||||
/* ** SVM nodes generation state ** */
|
||||
|
||||
/* Flag whether the node with corresponding ID was already compiled or
|
||||
@@ -197,6 +200,9 @@ class SVMCompiler {
|
||||
const ShaderNodeSet &done,
|
||||
ShaderInput *input,
|
||||
ShaderNode *skip_node = NULL);
|
||||
void find_aov_nodes_and_dependencies(ShaderNodeSet &aov_nodes,
|
||||
ShaderGraph *graph,
|
||||
CompilerState *state);
|
||||
void generate_node(ShaderNode *node, ShaderNodeSet &done);
|
||||
void generate_aov_node(ShaderNode *node, CompilerState *state);
|
||||
void generate_closure_node(ShaderNode *node, CompilerState *state);
|
||||
|
@@ -187,6 +187,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
||||
${X11_X11_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
${X11_X11_LIB}
|
||||
${X11_Xrender_LIB}
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_DisplayManagerX11.cpp
|
||||
intern/GHOST_SystemX11.cpp
|
||||
@@ -238,6 +243,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
||||
list(APPEND INC_SYS
|
||||
${X11_xf86vmode_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xf86vmode_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_X11_XFIXES)
|
||||
@@ -245,6 +253,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
||||
list(APPEND INC_SYS
|
||||
${X11_Xfixes_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xfixes_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_X11_ALPHA)
|
||||
@@ -256,6 +267,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
||||
list(APPEND INC_SYS
|
||||
${X11_Xinput_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xinput_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_definitions(-DWITH_GHOST_X11)
|
||||
|
@@ -1052,7 +1052,7 @@ int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_context);
|
||||
/**
|
||||
* Check if \a xr_context has a session that requires an upside-down frame-buffer (compared to
|
||||
* OpenGL). If true, the render result should be flipped vertically for correct output.
|
||||
* \note: Only to be called after session start, may otherwise result in a false negative.
|
||||
* \note Only to be called after session start, may otherwise result in a false negative.
|
||||
*/
|
||||
int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context);
|
||||
|
||||
|
@@ -77,6 +77,12 @@ class GHOST_ISystemPaths {
|
||||
*/
|
||||
virtual const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const = 0;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
|
||||
*/
|
||||
virtual const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const = 0;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary
|
||||
* \return Unsigned char string pointing to the binary dir
|
||||
|
@@ -56,6 +56,12 @@ extern const GHOST_TUns8 *GHOST_getSystemDir(int version, const char *versionstr
|
||||
*/
|
||||
extern const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr);
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
|
||||
*/
|
||||
extern const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type);
|
||||
|
||||
/**
|
||||
* Determine the dir in which the binary file is found.
|
||||
* \return Unsigned char string pointing to binary dir (eg ~/usr/local/bin/).
|
||||
|
@@ -565,6 +565,16 @@ typedef struct {
|
||||
char is_repeat;
|
||||
} GHOST_TEventKeyData;
|
||||
|
||||
typedef enum {
|
||||
GHOST_kUserSpecialDirDesktop,
|
||||
GHOST_kUserSpecialDirDocuments,
|
||||
GHOST_kUserSpecialDirDownloads,
|
||||
GHOST_kUserSpecialDirMusic,
|
||||
GHOST_kUserSpecialDirPictures,
|
||||
GHOST_kUserSpecialDirVideos,
|
||||
/* Can be extended as needed. */
|
||||
} GHOST_TUserSpecialDirTypes;
|
||||
|
||||
typedef struct {
|
||||
/** Number of pixels on a line. */
|
||||
GHOST_TUns32 xPixels;
|
||||
|
@@ -50,6 +50,12 @@ const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr)
|
||||
return systemPaths ? systemPaths->getUserDir(version, versionstr) : NULL; /* shouldn't be NULL */
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
|
||||
{
|
||||
GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();
|
||||
return systemPaths ? systemPaths->getUserSpecialDir(type) : NULL; /* shouldn't be NULL */
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_getBinaryDir()
|
||||
{
|
||||
GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();
|
||||
|
@@ -82,8 +82,8 @@ static GHOST_TButtonMask convertButton(int button)
|
||||
/**
|
||||
* Converts Mac rawkey codes (same for Cocoa & Carbon)
|
||||
* into GHOST key codes
|
||||
* \param rawCode The raw physical key code
|
||||
* \param recvChar the character ignoring modifiers (except for shift)
|
||||
* \param rawCode: The raw physical key code
|
||||
* \param recvChar: the character ignoring modifiers (except for shift)
|
||||
* \return Ghost key code
|
||||
*/
|
||||
static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction)
|
||||
@@ -783,7 +783,7 @@ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSet
|
||||
|
||||
/**
|
||||
* Dispose of a context.
|
||||
* \param context Pointer to the context to be disposed.
|
||||
* \param context: Pointer to the context to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess GHOST_SystemCocoa::disposeContext(GHOST_IContext *context)
|
||||
@@ -1730,13 +1730,14 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
|
||||
}
|
||||
window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
|
||||
|
||||
NSPoint delta = [[cocoawindow contentView] convertPointToBacking:NSMakePoint(dx, dy)];
|
||||
pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000,
|
||||
window,
|
||||
GHOST_kTrackpadEventScroll,
|
||||
x,
|
||||
y,
|
||||
dx,
|
||||
dy,
|
||||
delta.x,
|
||||
delta.y,
|
||||
[event isDirectionInvertedFromDevice]));
|
||||
}
|
||||
} break;
|
||||
|
@@ -55,6 +55,12 @@ class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
|
||||
*/
|
||||
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
|
||||
*/
|
||||
const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary
|
||||
* \return Unsigned char string pointing to the binary dir
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "GHOST_Debug.h"
|
||||
#include "GHOST_SystemPathsCocoa.h"
|
||||
|
||||
#pragma mark initialization/finalization
|
||||
@@ -89,6 +90,57 @@ const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserDir(int, const char *versionst
|
||||
return (GHOST_TUns8 *)tempPath;
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
|
||||
{
|
||||
static char tempPath[512] = "";
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString *basePath;
|
||||
NSArray *paths;
|
||||
NSSearchPathDirectory ns_directory;
|
||||
|
||||
switch (type) {
|
||||
case GHOST_kUserSpecialDirDesktop:
|
||||
ns_directory = NSDesktopDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDocuments:
|
||||
ns_directory = NSDocumentDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDownloads:
|
||||
ns_directory = NSDownloadsDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirMusic:
|
||||
ns_directory = NSMusicDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirPictures:
|
||||
ns_directory = NSPicturesDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirVideos:
|
||||
ns_directory = NSMoviesDirectory;
|
||||
break;
|
||||
default:
|
||||
GHOST_ASSERT(
|
||||
false,
|
||||
"GHOST_SystemPathsCocoa::getUserSpecialDir(): Invalid enum value for type parameter");
|
||||
[pool drain];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
paths = NSSearchPathForDirectoriesInDomains(ns_directory, NSUserDomainMask, YES);
|
||||
|
||||
if ([paths count] > 0)
|
||||
basePath = [paths objectAtIndex:0];
|
||||
else {
|
||||
[pool drain];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strncpy(
|
||||
(char *)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding], sizeof(tempPath));
|
||||
|
||||
[pool drain];
|
||||
return (GHOST_TUns8 *)tempPath;
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_SystemPathsCocoa::getBinaryDir() const
|
||||
{
|
||||
static GHOST_TUns8 tempPath[512] = "";
|
||||
|
@@ -21,6 +21,9 @@
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
#include "GHOST_SystemPathsUnix.h"
|
||||
|
||||
#include "GHOST_Debug.h"
|
||||
@@ -108,6 +111,62 @@ const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserDir(int version, const char *ve
|
||||
}
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
|
||||
{
|
||||
const char *type_str;
|
||||
|
||||
switch (type) {
|
||||
case GHOST_kUserSpecialDirDesktop:
|
||||
type_str = "DESKTOP";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDocuments:
|
||||
type_str = "DOCUMENTS";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDownloads:
|
||||
type_str = "DOWNLOAD";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirMusic:
|
||||
type_str = "MUSIC";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirPictures:
|
||||
type_str = "PICTURES";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirVideos:
|
||||
type_str = "VIDEOS";
|
||||
break;
|
||||
default:
|
||||
GHOST_ASSERT(
|
||||
false,
|
||||
"GHOST_SystemPathsUnix::getUserSpecialDir(): Invalid enum value for type parameter");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static string path = "";
|
||||
/* Pipe stderr to /dev/null to avoid error prints. We will fail gracefully still. */
|
||||
string command = string("xdg-user-dir ") + type_str + " 2> /dev/null";
|
||||
|
||||
FILE *fstream = popen(command.c_str(), "r");
|
||||
if (fstream == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
std::stringstream path_stream;
|
||||
while (!feof(fstream)) {
|
||||
char c = fgetc(fstream);
|
||||
/* xdg-user-dir ends the path with '\n'. */
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
path_stream << c;
|
||||
}
|
||||
if (pclose(fstream) == -1) {
|
||||
perror("GHOST_SystemPathsUnix::getUserSpecialDir failed at pclose()");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = path_stream.str();
|
||||
return path[0] ? (const GHOST_TUns8 *)path.c_str() : NULL;
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_SystemPathsUnix::getBinaryDir() const
|
||||
{
|
||||
return NULL;
|
||||
|
@@ -53,6 +53,12 @@ class GHOST_SystemPathsUnix : public GHOST_SystemPaths {
|
||||
*/
|
||||
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
|
||||
*/
|
||||
const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary
|
||||
* \return Unsigned char string pointing to the binary dir
|
||||
|
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "GHOST_SystemPathsWin32.h"
|
||||
#include "GHOST_Debug.h"
|
||||
|
||||
#ifndef _WIN32_IE
|
||||
# define _WIN32_IE 0x0501
|
||||
@@ -76,6 +77,50 @@ const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserDir(int, const char *versionst
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
|
||||
{
|
||||
GUID folderid;
|
||||
|
||||
switch (type) {
|
||||
case GHOST_kUserSpecialDirDesktop:
|
||||
folderid = FOLDERID_Desktop;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDocuments:
|
||||
folderid = FOLDERID_Documents;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDownloads:
|
||||
folderid = FOLDERID_Downloads;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirMusic:
|
||||
folderid = FOLDERID_Music;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirPictures:
|
||||
folderid = FOLDERID_Pictures;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirVideos:
|
||||
folderid = FOLDERID_Videos;
|
||||
break;
|
||||
default:
|
||||
GHOST_ASSERT(
|
||||
false,
|
||||
"GHOST_SystemPathsWin32::getUserSpecialDir(): Invalid enum value for type parameter");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char knownpath[MAX_PATH * 3] = {0};
|
||||
PWSTR knownpath_16 = NULL;
|
||||
HRESULT hResult = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, NULL, &knownpath_16);
|
||||
|
||||
if (hResult == S_OK) {
|
||||
conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3);
|
||||
CoTaskMemFree(knownpath_16);
|
||||
return (GHOST_TUns8 *)knownpath;
|
||||
}
|
||||
|
||||
CoTaskMemFree(knownpath_16);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const GHOST_TUns8 *GHOST_SystemPathsWin32::getBinaryDir() const
|
||||
{
|
||||
static char fullname[MAX_PATH * 3] = {0};
|
||||
|
@@ -62,6 +62,12 @@ class GHOST_SystemPathsWin32 : public GHOST_SystemPaths {
|
||||
*/
|
||||
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return Unsigned char string pointing to user dir (eg `~/Documents/`).
|
||||
*/
|
||||
const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary
|
||||
* \return Unsigned char string pointing to the binary dir
|
||||
|
@@ -422,7 +422,7 @@ finished:
|
||||
|
||||
/**
|
||||
* Dispose of a context.
|
||||
* \param context Pointer to the context to be disposed.
|
||||
* \param context: Pointer to the context to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
|
||||
@@ -569,13 +569,13 @@ GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons &buttons) const
|
||||
*/
|
||||
bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
|
||||
|
||||
bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
|
||||
bool down = HIBYTE(::GetAsyncKeyState(VK_LBUTTON)) != 0;
|
||||
buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
|
||||
|
||||
down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
|
||||
down = HIBYTE(::GetAsyncKeyState(VK_MBUTTON)) != 0;
|
||||
buttons.set(GHOST_kButtonMaskMiddle, down);
|
||||
|
||||
down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
|
||||
down = HIBYTE(::GetAsyncKeyState(VK_RBUTTON)) != 0;
|
||||
buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
@@ -939,148 +939,103 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
|
||||
{
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
if (type == GHOST_kEventButtonDown) {
|
||||
window->updateMouseCapture(MousePressed);
|
||||
}
|
||||
else if (type == GHOST_kEventButtonUp) {
|
||||
window->updateMouseCapture(MouseReleased);
|
||||
}
|
||||
GHOST_TabletData td = window->m_tabletInRange ? window->getLastTabletData() :
|
||||
GHOST_TABLET_DATA_NONE;
|
||||
|
||||
/* Check for active Wintab mouse emulation in addition to a tablet in range because a proximity
|
||||
* leave event might have fired before the Windows mouse up event, thus there are still tablet
|
||||
* events to grab. The described behavior was observed in a Wacom Bamboo CTE-450. */
|
||||
if (window->useTabletAPI(GHOST_kTabletWintab) &&
|
||||
(window->m_tabletInRange || window->wintabSysButPressed()) &&
|
||||
processWintabEvent(type, window, mask, window->getMousePressed())) {
|
||||
/* Wintab processing only handles in-contact events. */
|
||||
return NULL;
|
||||
}
|
||||
/* Ensure button click occurs at its intended position. */
|
||||
processCursorEvent(window);
|
||||
|
||||
return new GHOST_EventButton(
|
||||
system->getMilliSeconds(), type, window, mask, GHOST_TABLET_DATA_NONE);
|
||||
window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased);
|
||||
return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td);
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWin32::processWintabEvent(GHOST_TEventType type,
|
||||
GHOST_WindowWin32 *window,
|
||||
GHOST_TButtonMask mask,
|
||||
bool mousePressed)
|
||||
void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
|
||||
{
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
/* Only process Wintab packets if we can correlate them to a Window's mouse button event. When a
|
||||
* button event associated to a mouse button by Wintab occurs outside of WM_*BUTTON events,
|
||||
* there's no way to tell if other simultaneously pressed non-mouse mapped buttons are associated
|
||||
* to a modifier key (shift, alt, ctrl) or a system event (scroll, etc.) and thus it is not
|
||||
* possible to determine if a mouse click event should occur. */
|
||||
if (!mousePressed && !window->wintabSysButPressed()) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
std::vector<GHOST_WintabInfoWin32> wintabInfo;
|
||||
if (!window->getWintabInfo(wintabInfo)) {
|
||||
return GHOST_kFailure;
|
||||
return;
|
||||
}
|
||||
|
||||
auto wtiIter = wintabInfo.begin();
|
||||
|
||||
/* We only process events that correlate to a mouse button events, so there may exist Wintab
|
||||
* button down events that were instead mapped to e.g. scroll still in the queue. We need to
|
||||
* skip those and find the last button down mapped to mouse buttons. */
|
||||
if (!window->wintabSysButPressed()) {
|
||||
/* Assume there may be no button down event currently in the queue. */
|
||||
wtiIter = wintabInfo.end();
|
||||
|
||||
for (auto it = wintabInfo.begin(); it != wintabInfo.end(); it++) {
|
||||
if (it->type == GHOST_kEventButtonDown) {
|
||||
wtiIter = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool unhandledButton = type != GHOST_kEventCursorMove;
|
||||
|
||||
for (; wtiIter != wintabInfo.end(); wtiIter++) {
|
||||
auto info = *wtiIter;
|
||||
|
||||
for (auto info : wintabInfo) {
|
||||
switch (info.type) {
|
||||
case GHOST_kEventButtonDown: {
|
||||
/* While changing windows with a tablet, Window's mouse button events normally occur before
|
||||
* tablet proximity events, so a button up event can't be differentiated as occurring from
|
||||
* a Wintab tablet or a normal mouse and a Ghost button event will always be generated.
|
||||
*
|
||||
* If we were called during a button down event create a ghost button down event, otherwise
|
||||
* don't duplicate the prior button down as it interrupts drawing immediately after
|
||||
* changing a window. */
|
||||
case GHOST_kEventCursorMove: {
|
||||
system->pushEvent(new GHOST_EventCursor(
|
||||
info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
|
||||
if (type == GHOST_kEventButtonDown && mask == info.button) {
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventButtonDown: {
|
||||
system->pushEvent(new GHOST_EventCursor(
|
||||
info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
|
||||
|
||||
UINT message;
|
||||
switch (info.button) {
|
||||
case GHOST_kButtonMaskLeft:
|
||||
message = WM_LBUTTONDOWN;
|
||||
break;
|
||||
case GHOST_kButtonMaskRight:
|
||||
message = WM_RBUTTONDOWN;
|
||||
break;
|
||||
case GHOST_kButtonMaskMiddle:
|
||||
message = WM_MBUTTONDOWN;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
MSG msg;
|
||||
if (PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD) &&
|
||||
WM_QUIT != msg.message) {
|
||||
window->updateMouseCapture(MousePressed);
|
||||
system->pushEvent(
|
||||
new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
|
||||
unhandledButton = false;
|
||||
}
|
||||
window->updateWintabSysBut(MousePressed);
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventCursorMove:
|
||||
system->pushEvent(new GHOST_EventCursor(
|
||||
info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
|
||||
break;
|
||||
case GHOST_kEventButtonUp:
|
||||
system->pushEvent(
|
||||
new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
|
||||
if (type == GHOST_kEventButtonUp && mask == info.button) {
|
||||
unhandledButton = false;
|
||||
case GHOST_kEventButtonUp: {
|
||||
UINT message;
|
||||
switch (info.button) {
|
||||
case GHOST_kButtonMaskLeft:
|
||||
message = WM_LBUTTONUP;
|
||||
break;
|
||||
case GHOST_kButtonMaskRight:
|
||||
message = WM_RBUTTONUP;
|
||||
break;
|
||||
case GHOST_kButtonMaskMiddle:
|
||||
message = WM_MBUTTONUP;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
MSG msg;
|
||||
if (PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD) &&
|
||||
WM_QUIT != msg.message) {
|
||||
window->updateMouseCapture(MouseReleased);
|
||||
system->pushEvent(
|
||||
new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
|
||||
}
|
||||
window->updateWintabSysBut(MouseReleased);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No Wintab button found correlating to the system button event, handle it too.
|
||||
*
|
||||
* Wintab button up events may be handled during WM_MOUSEMOVE, before their corresponding
|
||||
* WM_*BUTTONUP event has fired, which results in two GHOST Button up events for a single Wintab
|
||||
* associated button event. Alternatively this Windows button up event may have been generated
|
||||
* from a non-stylus device such as a button on the tablet pad and needs to be handled for some
|
||||
* workflows.
|
||||
*
|
||||
* The ambiguity introduced by Windows and Wintab buttons being asynchronous and having no
|
||||
* definitive way to associate each, and that the Wintab API does not provide enough information
|
||||
* to differentiate whether the stylus down is or is not modified by another button to a
|
||||
* non-mouse mapping, means that we must pessimistically generate mouse up events when we are
|
||||
* unsure of an association to prevent the mouse locking into a down state. */
|
||||
if (unhandledButton) {
|
||||
if (!window->wintabSysButPressed()) {
|
||||
GHOST_TInt32 x, y;
|
||||
system->getCursorPosition(x, y);
|
||||
system->pushEvent(new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
x,
|
||||
y,
|
||||
GHOST_TABLET_DATA_NONE));
|
||||
}
|
||||
system->pushEvent(new GHOST_EventButton(
|
||||
system->getMilliSeconds(), type, window, mask, GHOST_TABLET_DATA_NONE));
|
||||
}
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
void GHOST_SystemWin32::processPointerEvent(
|
||||
UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventHandled)
|
||||
{
|
||||
std::vector<GHOST_PointerInfoWin32> pointerInfo;
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
/* Pointer events might fire when changing windows for a device which is set to use Wintab, even
|
||||
* when when Wintab is left enabled but set to the bottom of Wintab overlap order. */
|
||||
if (!window->useTabletAPI(GHOST_kTabletNative)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
std::vector<GHOST_PointerInfoWin32> pointerInfo;
|
||||
|
||||
if (window->getPointerInfo(pointerInfo, wParam, lParam) != GHOST_kSuccess) {
|
||||
return;
|
||||
}
|
||||
@@ -1148,35 +1103,19 @@ void GHOST_SystemWin32::processPointerEvent(
|
||||
|
||||
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
|
||||
{
|
||||
GHOST_TInt32 x_screen, y_screen;
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
if (window->m_tabletInRange || window->wintabSysButPressed()) {
|
||||
if (window->useTabletAPI(GHOST_kTabletWintab) &&
|
||||
processWintabEvent(
|
||||
GHOST_kEventCursorMove, window, GHOST_kButtonMaskNone, window->getMousePressed())) {
|
||||
return NULL;
|
||||
}
|
||||
else if (window->useTabletAPI(GHOST_kTabletNative)) {
|
||||
/* Tablet input handled in WM_POINTER* events. WM_MOUSEMOVE events in response to tablet
|
||||
* input aren't normally generated when using WM_POINTER events, but manually moving the
|
||||
* system cursor as we do in WM_POINTER handling does. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If using Wintab but no button event is currently active,
|
||||
* fall through to default handling. */
|
||||
}
|
||||
|
||||
system->getCursorPosition(x_screen, y_screen);
|
||||
DWORD msgPos = ::GetMessagePos();
|
||||
GHOST_TInt32 x_screen = GET_X_LPARAM(msgPos);
|
||||
GHOST_TInt32 y_screen = GET_Y_LPARAM(msgPos);
|
||||
GHOST_TInt32 x_accum = 0, y_accum = 0;
|
||||
|
||||
if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) {
|
||||
GHOST_TInt32 x_new = x_screen;
|
||||
GHOST_TInt32 y_new = y_screen;
|
||||
GHOST_TInt32 x_accum, y_accum;
|
||||
GHOST_Rect bounds;
|
||||
|
||||
/* fallback to window bounds */
|
||||
/* Fallback to window bounds. */
|
||||
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
|
||||
window->getClientBounds(bounds);
|
||||
}
|
||||
@@ -1187,29 +1126,19 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
|
||||
|
||||
window->getCursorGrabAccum(x_accum, y_accum);
|
||||
if (x_new != x_screen || y_new != y_screen) {
|
||||
/* when wrapping we don't need to add an event because the
|
||||
* setCursorPosition call will cause a new event after */
|
||||
system->setCursorPosition(x_new, y_new); /* wrap */
|
||||
window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
|
||||
}
|
||||
else {
|
||||
return new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
x_screen + x_accum,
|
||||
y_screen + y_accum,
|
||||
GHOST_TABLET_DATA_NONE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
x_screen,
|
||||
y_screen,
|
||||
GHOST_TABLET_DATA_NONE);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
GHOST_TabletData td = window->m_tabletInRange ? window->getLastTabletData() :
|
||||
GHOST_TABLET_DATA_NONE;
|
||||
return new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
x_screen + x_accum,
|
||||
y_screen + y_accum,
|
||||
td);
|
||||
}
|
||||
|
||||
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
|
||||
@@ -1317,6 +1246,23 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
|
||||
return event;
|
||||
}
|
||||
|
||||
GHOST_Event *GHOST_SystemWin32::processWindowSizeEvent(GHOST_WindowWin32 *window)
|
||||
{
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
GHOST_Event *sizeEvent = new GHOST_Event(
|
||||
system->getMilliSeconds(), GHOST_kEventWindowSize, window);
|
||||
|
||||
/* We get WM_SIZE before we fully init. Do not dispatch before we are continuously resizing. */
|
||||
if (window->m_inLiveResize) {
|
||||
system->pushEvent(sizeEvent);
|
||||
system->dispatchEvents();
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return sizeEvent;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type,
|
||||
GHOST_WindowWin32 *window)
|
||||
{
|
||||
@@ -1356,12 +1302,10 @@ void GHOST_SystemWin32::setTabletAPI(GHOST_TTabletAPI api)
|
||||
GHOST_System::setTabletAPI(api);
|
||||
|
||||
GHOST_WindowManager *wm = getWindowManager();
|
||||
GHOST_WindowWin32 *activeWindow = (GHOST_WindowWin32 *)wm->getActiveWindow();
|
||||
|
||||
for (GHOST_IWindow *win : wm->getWindows()) {
|
||||
GHOST_WindowWin32 *windowsWindow = (GHOST_WindowWin32 *)win;
|
||||
windowsWindow->updateWintab(windowsWindow == activeWindow,
|
||||
!::IsIconic(windowsWindow->getHWND()));
|
||||
GHOST_WindowWin32 *windowWin32 = (GHOST_WindowWin32 *)win;
|
||||
windowWin32->setWintabEnabled(windowWin32->useTabletAPI(GHOST_kTabletWintab));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1606,15 +1550,28 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
case WT_INFOCHANGE: {
|
||||
window->processWintabInfoChangeEvent(lParam);
|
||||
eventHandled = true;
|
||||
break;
|
||||
}
|
||||
case WT_CSRCHANGE:
|
||||
window->updateWintabCursorInfo();
|
||||
eventHandled = true;
|
||||
break;
|
||||
case WT_PROXIMITY: {
|
||||
bool inRange = LOWORD(lParam);
|
||||
window->processWintabProximityEvent(inRange);
|
||||
if (window->useTabletAPI(GHOST_kTabletWintab)) {
|
||||
if (LOWORD(lParam)) {
|
||||
window->m_tabletInRange = true;
|
||||
}
|
||||
else {
|
||||
window->processWintabLeave();
|
||||
}
|
||||
}
|
||||
eventHandled = true;
|
||||
break;
|
||||
}
|
||||
case WT_PACKET:
|
||||
window->updatePendingWintabEvents();
|
||||
processWintabEvent(window);
|
||||
eventHandled = true;
|
||||
break;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Pointer events, processed
|
||||
@@ -1664,7 +1621,18 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
event = processCursorEvent(window);
|
||||
if (!window->m_mousePresent) {
|
||||
TRACKMOUSEEVENT tme = {sizeof(tme)};
|
||||
tme.dwFlags = TME_LEAVE;
|
||||
tme.hwndTrack = hwnd;
|
||||
TrackMouseEvent(&tme);
|
||||
window->m_mousePresent = true;
|
||||
window->setWintabOverlap(true);
|
||||
}
|
||||
|
||||
if (!window->m_tabletInRange) {
|
||||
event = processCursorEvent(window);
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEWHEEL: {
|
||||
/* The WM_MOUSEWHEEL message is sent to the focus window
|
||||
@@ -1706,7 +1674,10 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
window->loadCursor(true, GHOST_kStandardCursorDefault);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_MOUSELEAVE:
|
||||
window->m_mousePresent = false;
|
||||
window->setWintabOverlap(false);
|
||||
break;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Mouse events, ignored
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@@ -1722,7 +1693,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
* is sent to the window that has captured the mouse.
|
||||
*/
|
||||
break;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Window events, processed
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@@ -1755,8 +1725,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
if (LOWORD(wParam) == WA_INACTIVE)
|
||||
window->lostMouseCapture();
|
||||
|
||||
window->updateWintab(LOWORD(wParam) != WA_INACTIVE, !::IsIconic(window->getHWND()));
|
||||
|
||||
lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
@@ -1798,6 +1766,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
/* Let DefWindowProc handle it. */
|
||||
break;
|
||||
case WM_SIZING:
|
||||
event = processWindowSizeEvent(window);
|
||||
break;
|
||||
case WM_SIZE:
|
||||
/* The WM_SIZE message is sent to a window after its size has changed.
|
||||
* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
|
||||
@@ -1805,21 +1775,13 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
|
||||
* message without calling DefWindowProc.
|
||||
*/
|
||||
/* we get first WM_SIZE before we fully init.
|
||||
* So, do not dispatch before we continuously resizing. */
|
||||
if (window->m_inLiveResize) {
|
||||
system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window));
|
||||
system->dispatchEvents();
|
||||
}
|
||||
else {
|
||||
event = processWindowEvent(GHOST_kEventWindowSize, window);
|
||||
}
|
||||
event = processWindowSizeEvent(window);
|
||||
|
||||
/* Window might be minimized while inactive. When a window is inactive but not minimized,
|
||||
* Wintab is left enabled (to catch the case where a pen is used to activate a window).
|
||||
* When an inactive window is minimized, we need to disable Wintab. */
|
||||
if (msg == WM_SIZE && wParam == SIZE_MINIMIZED) {
|
||||
window->updateWintab(false, false);
|
||||
if (wParam == SIZE_MINIMIZED) {
|
||||
window->setWintabEnabled(false);
|
||||
}
|
||||
else if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) {
|
||||
window->setWintabEnabled(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@@ -322,16 +322,9 @@ class GHOST_SystemWin32 : public GHOST_System {
|
||||
|
||||
/**
|
||||
* Creates tablet events from Wintab events.
|
||||
* \param type: The type of pointer event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param mask: The button mask of the calling event.
|
||||
* \param mousePressed: Whether the mouse is currently pressed.
|
||||
* \return True if the method handled the event.
|
||||
*/
|
||||
static GHOST_TSuccess processWintabEvent(GHOST_TEventType type,
|
||||
GHOST_WindowWin32 *window,
|
||||
GHOST_TButtonMask mask,
|
||||
bool mousePressed);
|
||||
static void processWintabEvent(GHOST_WindowWin32 *window);
|
||||
|
||||
/**
|
||||
* Creates tablet events from pointer events.
|
||||
@@ -376,6 +369,13 @@ class GHOST_SystemWin32 : public GHOST_System {
|
||||
*/
|
||||
GHOST_TKey processSpecialKey(short vKey, short scanCode) const;
|
||||
|
||||
/**
|
||||
* Creates a window size event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \return The event created.
|
||||
*/
|
||||
static GHOST_Event *processWindowSizeEvent(GHOST_WindowWin32 *window);
|
||||
|
||||
/**
|
||||
* Creates a window event.
|
||||
* \param type: The type of event to create.
|
||||
|
@@ -905,8 +905,8 @@ GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
|
||||
static void postNotification()
|
||||
{
|
||||
NSUserNotification *notification = [[NSUserNotification alloc] init];
|
||||
notification.title = @"Blender progress notification";
|
||||
notification.informativeText = @"Calculation is finished";
|
||||
notification.title = @"Blender Progress Notification";
|
||||
notification.informativeText = @"Calculation is finished.";
|
||||
notification.soundName = NSUserNotificationDefaultSoundName;
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
||||
[notification release];
|
||||
|
@@ -407,7 +407,7 @@ void GHOST_WindowWayland::setOpaque() const
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \param type The type of rendering context create.
|
||||
* \param type: The type of rendering context create.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType type)
|
||||
|
@@ -72,6 +72,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
|
||||
bool is_debug,
|
||||
bool dialog)
|
||||
: GHOST_Window(width, height, state, wantStereoVisual, false),
|
||||
m_mousePresent(false),
|
||||
m_tabletInRange(false),
|
||||
m_inLiveResize(false),
|
||||
m_system(system),
|
||||
@@ -309,8 +310,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
|
||||
(m_wintab.enable = (GHOST_WIN32_WTEnable)::GetProcAddress(m_wintab.handle, "WTEnable")) &&
|
||||
(m_wintab.overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(m_wintab.handle, "WTOverlap"))) {
|
||||
initializeWintab();
|
||||
// Determine which tablet API to use and enable it.
|
||||
updateWintab(m_system->m_windowFocus, m_system->m_windowFocus);
|
||||
setWintabEnabled(true);
|
||||
}
|
||||
|
||||
CoCreateInstance(
|
||||
@@ -326,13 +326,12 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
|
||||
}
|
||||
|
||||
if (m_wintab.handle) {
|
||||
updateWintab(false, false);
|
||||
setWintabEnabled(false);
|
||||
if (m_wintab.close && m_wintab.context) {
|
||||
m_wintab.close(m_wintab.context);
|
||||
}
|
||||
|
||||
FreeLibrary(m_wintab.handle);
|
||||
memset(&m_wintab, 0, sizeof(m_wintab));
|
||||
}
|
||||
|
||||
if (m_user32) {
|
||||
@@ -771,32 +770,6 @@ void GHOST_WindowWin32::updateMouseCapture(GHOST_MouseCaptureEventWin32 event)
|
||||
}
|
||||
}
|
||||
|
||||
bool GHOST_WindowWin32::getMousePressed() const
|
||||
{
|
||||
return m_nPressedButtons;
|
||||
}
|
||||
|
||||
bool GHOST_WindowWin32::wintabSysButPressed() const
|
||||
{
|
||||
return m_wintab.numSysButtons;
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::updateWintabSysBut(GHOST_MouseCaptureEventWin32 event)
|
||||
{
|
||||
switch (event) {
|
||||
case MousePressed:
|
||||
m_wintab.numSysButtons++;
|
||||
break;
|
||||
case MouseReleased:
|
||||
if (m_wintab.numSysButtons)
|
||||
m_wintab.numSysButtons--;
|
||||
break;
|
||||
case OperatorGrab:
|
||||
case OperatorUngrab:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const
|
||||
{
|
||||
// Convert GHOST cursor to Windows OEM cursor
|
||||
@@ -1000,28 +973,6 @@ GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorSha
|
||||
return (getStandardCursor(cursorShape)) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::updateWintab(bool active, bool visible)
|
||||
{
|
||||
if (m_wintab.enable && m_wintab.overlap && m_wintab.context) {
|
||||
bool enable = useTabletAPI(GHOST_kTabletWintab) && visible;
|
||||
bool overlap = enable && active;
|
||||
|
||||
/* Disabling context while the Window is not minimized can cause issues on receiving Wintab
|
||||
* input while changing a window for some drivers, so only disable if either Wintab had been
|
||||
* disabled or the window is minimized. */
|
||||
m_wintab.enable(m_wintab.context, enable);
|
||||
m_wintab.overlap(m_wintab.context, overlap);
|
||||
|
||||
if (!overlap) {
|
||||
/* WT_PROXIMITY event doesn't occur unless tablet's cursor leaves the proximity while the
|
||||
* window is active. */
|
||||
m_tabletInRange = false;
|
||||
m_wintab.numSysButtons = 0;
|
||||
m_wintab.sysButtonsPressed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::initializeWintab()
|
||||
{
|
||||
/* Return if wintab library handle doesn't exist or wintab is already initialized. */
|
||||
@@ -1034,8 +985,6 @@ void GHOST_WindowWin32::initializeWintab()
|
||||
if (m_wintab.open && m_wintab.info && m_wintab.queueSizeGet && m_wintab.queueSizeSet &&
|
||||
m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) {
|
||||
|
||||
/* The pressure and orientation (tilt) */
|
||||
AXIS Pressure, Orientation[3];
|
||||
lc.lcPktData = PACKETDATA;
|
||||
lc.lcPktMode = PACKETMODE;
|
||||
lc.lcMoveMask = PACKETDATA;
|
||||
@@ -1046,19 +995,7 @@ void GHOST_WindowWin32::initializeWintab()
|
||||
|
||||
m_wintab.info(WTI_INTERFACE, IFC_NDEVICES, &m_wintab.numDevices);
|
||||
|
||||
BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
|
||||
m_wintab.maxPressure = pressureSupport ? Pressure.axMax : 0;
|
||||
|
||||
BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
|
||||
/* Does the tablet support azimuth ([0]) and altitude ([1])? */
|
||||
if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
|
||||
/* All this assumes the minimum is 0. */
|
||||
m_wintab.maxAzimuth = Orientation[0].axMax;
|
||||
m_wintab.maxAltitude = Orientation[1].axMax;
|
||||
}
|
||||
else { /* No so dont do tilt stuff. */
|
||||
m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
|
||||
}
|
||||
updateWintabCursorInfo();
|
||||
|
||||
/* The Wintab spec says we must open the context disabled if we are using cursor masks. */
|
||||
m_wintab.context = m_wintab.open(m_hWnd, &lc, FALSE);
|
||||
@@ -1171,9 +1108,68 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
|
||||
}
|
||||
}
|
||||
|
||||
if (!outPointerInfo.empty()) {
|
||||
lastTabletData = outPointerInfo.back().tabletData;
|
||||
}
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::setWintabEnabled(bool enable)
|
||||
{
|
||||
if (m_wintab.enable && m_wintab.get && m_wintab.context) {
|
||||
LOGCONTEXT context = {0};
|
||||
|
||||
if (m_wintab.get(m_wintab.context, &context)) {
|
||||
if (enable && context.lcStatus & CXS_DISABLED && useTabletAPI(GHOST_kTabletWintab)) {
|
||||
m_wintab.enable(m_wintab.context, true);
|
||||
|
||||
POINT cursorPos;
|
||||
::GetCursorPos(&cursorPos);
|
||||
GHOST_Rect bounds;
|
||||
getClientBounds(bounds);
|
||||
if (bounds.isInside(cursorPos.x, cursorPos.y)) {
|
||||
setWintabOverlap(true);
|
||||
}
|
||||
}
|
||||
else if (!enable && !(context.lcStatus & CXS_DISABLED)) {
|
||||
setWintabOverlap(false);
|
||||
m_wintab.enable(m_wintab.context, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::setWintabOverlap(bool overlap)
|
||||
{
|
||||
if (m_wintab.overlap && m_wintab.get && m_wintab.packetsGet && m_wintab.context) {
|
||||
LOGCONTEXT context = {0};
|
||||
|
||||
if (m_wintab.get(m_wintab.context, &context)) {
|
||||
if (overlap && context.lcStatus & CXS_OBSCURED && useTabletAPI(GHOST_kTabletWintab)) {
|
||||
m_wintab.overlap(m_wintab.context, true);
|
||||
}
|
||||
else if (!overlap && context.lcStatus & CXS_ONTOP) {
|
||||
m_wintab.overlap(m_wintab.context, false);
|
||||
|
||||
/* If context is disabled, Windows Ink may be active and managing m_tabletInRange. Don't
|
||||
* modify it. */
|
||||
if (!(context.lcStatus & CXS_DISABLED)) {
|
||||
processWintabLeave();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::processWintabLeave()
|
||||
{
|
||||
m_tabletInRange = false;
|
||||
m_wintab.buttons = 0;
|
||||
/* Clear the packet queue. */
|
||||
m_wintab.packetsGet(m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::processWintabDisplayChangeEvent()
|
||||
{
|
||||
LOGCONTEXT lc_sys = {0}, lc_curr = {0};
|
||||
@@ -1207,48 +1203,38 @@ bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::processWintabProximityEvent(bool inRange)
|
||||
void GHOST_WindowWin32::updateWintabCursorInfo()
|
||||
{
|
||||
if (!useTabletAPI(GHOST_kTabletWintab)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's see if we can initialize tablet here
|
||||
if (m_wintab.info && m_wintab.context) {
|
||||
AXIS Pressure, Orientation[3]; /* The maximum tablet size */
|
||||
AXIS Pressure, Orientation[3];
|
||||
|
||||
BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
|
||||
m_wintab.maxPressure = pressureSupport ? Pressure.axMax : 0;
|
||||
|
||||
BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
|
||||
/* does the tablet support azimuth ([0]) and altitude ([1]) */
|
||||
/* Does the tablet support azimuth ([0]) and altitude ([1]). */
|
||||
if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
|
||||
m_wintab.maxAzimuth = Orientation[0].axMax;
|
||||
m_wintab.maxAltitude = Orientation[1].axMax;
|
||||
}
|
||||
else { /* no so dont do tilt stuff */
|
||||
else {
|
||||
m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_tabletInRange = inRange;
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::processWintabInfoChangeEvent(LPARAM lParam)
|
||||
{
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
|
||||
|
||||
// Update number of connected Wintab digitizers
|
||||
/* Update number of connected Wintab digitizers */
|
||||
if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) {
|
||||
m_wintab.info(WTI_INTERFACE, IFC_NDEVICES, &m_wintab.numDevices);
|
||||
updateWintab((GHOST_WindowWin32 *)system->getWindowManager()->getActiveWindow() == this,
|
||||
!::IsIconic(m_hWnd));
|
||||
if (useTabletAPI(GHOST_kTabletWintab)) {
|
||||
setWintabEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_WindowWin32::wintabMouseToGhost(UINT cursor,
|
||||
WORD physicalButton,
|
||||
GHOST_TButtonMask &ghostButton)
|
||||
GHOST_TButtonMask GHOST_WindowWin32::wintabMouseToGhost(UINT cursor, WORD physicalButton)
|
||||
{
|
||||
const WORD numButtons = 32;
|
||||
BYTE logicalButtons[numButtons] = {0};
|
||||
@@ -1258,183 +1244,135 @@ GHOST_TSuccess GHOST_WindowWin32::wintabMouseToGhost(UINT cursor,
|
||||
m_wintab.info(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons);
|
||||
|
||||
if (physicalButton >= numButtons) {
|
||||
return GHOST_kFailure;
|
||||
return GHOST_kButtonMaskNone;
|
||||
}
|
||||
BYTE lb = logicalButtons[physicalButton];
|
||||
|
||||
if (lb >= numButtons) {
|
||||
return GHOST_kFailure;
|
||||
return GHOST_kButtonMaskNone;
|
||||
}
|
||||
switch (systemButtons[lb]) {
|
||||
case SBN_LCLICK:
|
||||
ghostButton = GHOST_kButtonMaskLeft;
|
||||
return GHOST_kSuccess;
|
||||
return GHOST_kButtonMaskLeft;
|
||||
case SBN_RCLICK:
|
||||
ghostButton = GHOST_kButtonMaskRight;
|
||||
return GHOST_kSuccess;
|
||||
return GHOST_kButtonMaskRight;
|
||||
case SBN_MCLICK:
|
||||
ghostButton = GHOST_kButtonMaskMiddle;
|
||||
return GHOST_kSuccess;
|
||||
return GHOST_kButtonMaskMiddle;
|
||||
default:
|
||||
return GHOST_kFailure;
|
||||
return GHOST_kButtonMaskNone;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
|
||||
{
|
||||
if (!useTabletAPI(GHOST_kTabletWintab)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
if (!(m_wintab.packetsGet && m_wintab.context)) {
|
||||
if (!(useTabletAPI(GHOST_kTabletWintab) && m_wintab.packetsGet && m_wintab.context)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
|
||||
|
||||
updatePendingWintabEvents();
|
||||
const int numPackets = m_wintab.packetsGet(
|
||||
m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
|
||||
outWintabInfo.resize(numPackets);
|
||||
|
||||
auto &pendingEvents = m_wintab.pendingEvents;
|
||||
size_t pendingEventSize = pendingEvents.size();
|
||||
outWintabInfo.resize(pendingEventSize);
|
||||
for (int i = 0; i < numPackets; i++) {
|
||||
PACKET pkt = m_wintab.pkts[i];
|
||||
GHOST_WintabInfoWin32 &out = outWintabInfo[i];
|
||||
|
||||
for (int i = 0; i < pendingEventSize; i++) {
|
||||
PACKET pkt = pendingEvents.front();
|
||||
pendingEvents.pop();
|
||||
|
||||
GHOST_TabletData tabletData = GHOST_TABLET_DATA_NONE;
|
||||
switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */
|
||||
out.tabletData = GHOST_TABLET_DATA_NONE;
|
||||
/* % 3 for multiple devices ("DualTrack"). */
|
||||
switch (pkt.pkCursor % 3) {
|
||||
case 0:
|
||||
tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */
|
||||
/* Puck - processed as mouse. */
|
||||
out.tabletData.Active = GHOST_kTabletModeNone;
|
||||
break;
|
||||
case 1:
|
||||
tabletData.Active = GHOST_kTabletModeStylus; /* stylus */
|
||||
out.tabletData.Active = GHOST_kTabletModeStylus;
|
||||
break;
|
||||
case 2:
|
||||
tabletData.Active = GHOST_kTabletModeEraser; /* eraser */
|
||||
out.tabletData.Active = GHOST_kTabletModeEraser;
|
||||
break;
|
||||
}
|
||||
|
||||
out.x = pkt.pkX;
|
||||
out.y = pkt.pkY;
|
||||
|
||||
if (m_wintab.maxPressure > 0) {
|
||||
tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure;
|
||||
out.tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure;
|
||||
}
|
||||
|
||||
if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) {
|
||||
ORIENTATION ort = pkt.pkOrientation;
|
||||
float vecLen;
|
||||
float altRad, azmRad; /* in radians */
|
||||
float altRad, azmRad; /* In radians. */
|
||||
|
||||
/*
|
||||
* from the wintab spec:
|
||||
* orAzimuth Specifies the clockwise rotation of the
|
||||
* cursor about the z axis through a full circular range.
|
||||
* From the wintab spec:
|
||||
* orAzimuth: Specifies the clockwise rotation of the cursor about the z axis through a
|
||||
* full circular range.
|
||||
* orAltitude: Specifies the angle with the x-y plane through a signed, semicircular range.
|
||||
* Positive values specify an angle upward toward the positive z axis; negative values
|
||||
* specify an angle downward toward the negative z axis.
|
||||
*
|
||||
* orAltitude Specifies the angle with the x-y plane
|
||||
* through a signed, semicircular range. Positive values
|
||||
* specify an angle upward toward the positive z axis;
|
||||
* negative values specify an angle downward toward the negative z axis.
|
||||
*
|
||||
* wintab.h defines .orAltitude as a UINT but documents .orAltitude
|
||||
* as positive for upward angles and negative for downward angles.
|
||||
* WACOM uses negative altitude values to show that the pen is inverted;
|
||||
* therefore we cast .orAltitude as an (int) and then use the absolute value.
|
||||
* wintab.h defines orAltitude as a UINT but documents orAltitude as positive for upward
|
||||
* angles and negative for downward angles. WACOM uses negative altitude values to show that
|
||||
* the pen is inverted; therefore we cast orAltitude as an (int) and then use the absolute
|
||||
* value.
|
||||
*/
|
||||
|
||||
/* convert raw fixed point data to radians */
|
||||
/* Convert raw fixed point data to radians. */
|
||||
altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * M_PI / 2.0);
|
||||
azmRad = (float)(((float)ort.orAzimuth / (float)m_wintab.maxAzimuth) * M_PI * 2.0);
|
||||
|
||||
/* find length of the stylus' projected vector on the XY plane */
|
||||
/* Find length of the stylus' projected vector on the XY plane. */
|
||||
vecLen = cos(altRad);
|
||||
|
||||
/* from there calculate X and Y components based on azimuth */
|
||||
tabletData.Xtilt = sin(azmRad) * vecLen;
|
||||
tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
|
||||
/* From there calculate X and Y components based on azimuth. */
|
||||
out.tabletData.Xtilt = sin(azmRad) * vecLen;
|
||||
out.tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
|
||||
}
|
||||
|
||||
outWintabInfo[i].x = pkt.pkX;
|
||||
outWintabInfo[i].y = pkt.pkY;
|
||||
/* Some Wintab libraries don't handle relative button input, so we track button presses
|
||||
* manually. */
|
||||
out.button = GHOST_kButtonMaskNone;
|
||||
out.type = GHOST_kEventCursorMove;
|
||||
|
||||
/* Some Wintab libraries don't handle relative button input correctly, so we track button
|
||||
* presses manually. Examples include Wacom's Bamboo modifying button events in the queue when
|
||||
* peeked, or missing events when entering the window when the context is not on top. */
|
||||
DWORD buttonsChanged = m_wintab.sysButtonsPressed ^ pkt.pkButtons;
|
||||
|
||||
/* Find the index for the changed button from the button map. */
|
||||
WORD physicalButton = 0;
|
||||
for (DWORD diff = (unsigned)buttonsChanged >> 1; diff > 0; diff = (unsigned)diff >> 1) {
|
||||
physicalButton++;
|
||||
}
|
||||
|
||||
if (buttonsChanged &&
|
||||
wintabMouseToGhost(pkt.pkCursor, physicalButton, outWintabInfo[i].button)) {
|
||||
if (buttonsChanged & pkt.pkButtons) {
|
||||
outWintabInfo[i].type = GHOST_kEventButtonDown;
|
||||
DWORD buttonsChanged = m_wintab.buttons ^ pkt.pkButtons;
|
||||
if (buttonsChanged) {
|
||||
/* Find the index for the changed button from the button map. */
|
||||
WORD physicalButton = 0;
|
||||
for (DWORD diff = (unsigned)buttonsChanged >> 1; diff > 0; diff = (unsigned)diff >> 1) {
|
||||
physicalButton++;
|
||||
}
|
||||
else {
|
||||
outWintabInfo[i].type = GHOST_kEventButtonUp;
|
||||
|
||||
out.button = wintabMouseToGhost(pkt.pkCursor, physicalButton);
|
||||
|
||||
if (out.button != GHOST_kButtonMaskNone) {
|
||||
if (buttonsChanged & pkt.pkButtons) {
|
||||
out.type = GHOST_kEventButtonDown;
|
||||
}
|
||||
else {
|
||||
out.type = GHOST_kEventButtonUp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
outWintabInfo[i].type = GHOST_kEventCursorMove;
|
||||
|
||||
/* Only update handled button, in case multiple button events arrived simultaneously. */
|
||||
m_wintab.buttons ^= 1 << physicalButton;
|
||||
}
|
||||
|
||||
m_wintab.sysButtonsPressed = pkt.pkButtons;
|
||||
out.time = system->tickCountToMillis(pkt.pkTime);
|
||||
}
|
||||
|
||||
outWintabInfo[i].time = system->millisSinceStart(pkt.pkTime);
|
||||
outWintabInfo[i].tabletData = tabletData;
|
||||
if (!outWintabInfo.empty()) {
|
||||
lastTabletData = outWintabInfo.back().tabletData;
|
||||
}
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/* Wintab (per documentation but may vary with implementation) does not update when its event
|
||||
* buffer is full. This is an issue because we need some synchronization point between Wintab
|
||||
* events and Win32 events, so we can't drain and process the queue immediately. We need to
|
||||
* associate Wintab mouse events to Win32 mouse events because Wintab buttons are modal (a button
|
||||
* associated to left click is not always a left click) and there's no way to reconstruct their
|
||||
* mode from the Wintab API alone. There is no guaranteed ordering between Wintab and Win32 mouse
|
||||
* events and no documented time stamp shared between the two, so we synchronize on mouse button
|
||||
* events. */
|
||||
void GHOST_WindowWin32::updatePendingWintabEvents()
|
||||
GHOST_TabletData GHOST_WindowWin32::getLastTabletData()
|
||||
{
|
||||
if (!(m_wintab.packetsGet && m_wintab.context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &pendingEvents = m_wintab.pendingEvents;
|
||||
|
||||
/* Clear outdated events from queue. */
|
||||
DWORD currTime = ::GetTickCount();
|
||||
DWORD millisTimeout = 500;
|
||||
while (!pendingEvents.empty()) {
|
||||
DWORD pkTime = pendingEvents.front().pkTime;
|
||||
|
||||
if (currTime > pkTime + millisTimeout) {
|
||||
pendingEvents.pop();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get new packets. */
|
||||
const int numPackets = m_wintab.packetsGet(
|
||||
m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
|
||||
|
||||
int i = 0;
|
||||
/* Don't queue outdated packets, such events can include packets that occurred before the current
|
||||
* window lost and regained focus. */
|
||||
for (; i < numPackets; i++) {
|
||||
DWORD pkTime = m_wintab.pkts[i].pkTime;
|
||||
|
||||
if (currTime < pkTime + millisTimeout) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; i < numPackets; i++) {
|
||||
pendingEvents.push(m_wintab.pkts[i]);
|
||||
}
|
||||
return lastTabletData;
|
||||
}
|
||||
|
||||
GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
|
||||
|
@@ -437,13 +437,6 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
HCURSOR getStandardCursor(GHOST_TStandardCursor shape) const;
|
||||
void loadCursor(bool visible, GHOST_TStandardCursor cursorShape) const;
|
||||
|
||||
/**
|
||||
* Handle setup and switch between Wintab and Pointer APIs.
|
||||
* \param active: Whether the window is or will be in an active state.
|
||||
* \param visible: Whether the window is currently (or will be) visible).
|
||||
*/
|
||||
void updateWintab(bool active, bool visible);
|
||||
|
||||
/**
|
||||
* Query whether given tablet API should be used.
|
||||
* \param api: Tablet API to test.
|
||||
@@ -461,16 +454,32 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
WPARAM wParam,
|
||||
LPARAM lParam);
|
||||
|
||||
/**
|
||||
* Enables or disables Wintab context.
|
||||
* \param enable: Whether the context should be enabled.
|
||||
*/
|
||||
void setWintabEnabled(bool enable);
|
||||
|
||||
/**
|
||||
* Changes Wintab context overlap.
|
||||
* \param overlap: Whether context should be brought to top of overlap order.
|
||||
*/
|
||||
void setWintabOverlap(bool overlap);
|
||||
|
||||
/**
|
||||
* Resets Wintab state.
|
||||
*/
|
||||
void processWintabLeave();
|
||||
|
||||
/**
|
||||
* Handle Wintab coordinate changes when DisplayChange events occur.
|
||||
*/
|
||||
void processWintabDisplayChangeEvent();
|
||||
|
||||
/**
|
||||
* Set tablet details when a cursor enters range.
|
||||
* \param inRange: Whether the Wintab device is in tracking range.
|
||||
* Updates cached Wintab properties for current cursor.
|
||||
*/
|
||||
void processWintabProximityEvent(bool inRange);
|
||||
void updateWintabCursorInfo();
|
||||
|
||||
/**
|
||||
* Handle Wintab info changes such as change in number of connected tablets.
|
||||
@@ -486,9 +495,10 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
GHOST_TSuccess getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo);
|
||||
|
||||
/**
|
||||
* Updates stored pending Wintab events.
|
||||
* Get the most recent tablet data.
|
||||
* \return Most recent tablet data.
|
||||
*/
|
||||
void updatePendingWintabEvents();
|
||||
GHOST_TabletData getLastTabletData();
|
||||
|
||||
GHOST_TSuccess beginFullScreen() const
|
||||
{
|
||||
@@ -502,24 +512,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
|
||||
GHOST_TUns16 getDPIHint() override;
|
||||
|
||||
/**
|
||||
* Get whether there are currently any mouse buttons pressed.
|
||||
* \return True if there are any currently pressed mouse buttons.
|
||||
*/
|
||||
bool getMousePressed() const;
|
||||
|
||||
/**
|
||||
* Get if there are currently pressed Wintab buttons associated to a Windows mouse button press.
|
||||
* \return True if there are currently any pressed Wintab buttons associated to a Windows
|
||||
* mouse button press.
|
||||
*/
|
||||
bool wintabSysButPressed() const;
|
||||
|
||||
/**
|
||||
* Register a Wintab button has been associated to a Windows mouse button press.
|
||||
* \param event: Whether the button was pressed or released.
|
||||
*/
|
||||
void updateWintabSysBut(GHOST_MouseCaptureEventWin32 event);
|
||||
/** Whether the mouse is either over or captured by the window. */
|
||||
bool m_mousePresent;
|
||||
|
||||
/** Whether a tablet stylus is being tracked. */
|
||||
bool m_tabletInRange;
|
||||
@@ -577,28 +571,28 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
int hotY,
|
||||
bool canInvertColor);
|
||||
|
||||
/** Pointer to system */
|
||||
/** Pointer to system. */
|
||||
GHOST_SystemWin32 *m_system;
|
||||
/** Pointer to COM IDropTarget implementor */
|
||||
/** Pointer to COM IDropTarget implementor. */
|
||||
GHOST_DropTargetWin32 *m_dropTarget;
|
||||
/** Window handle. */
|
||||
HWND m_hWnd;
|
||||
/** Device context handle. */
|
||||
HDC m_hDC;
|
||||
|
||||
/** Flag for if window has captured the mouse */
|
||||
/** Flag for if window has captured the mouse. */
|
||||
bool m_hasMouseCaptured;
|
||||
/** Flag if an operator grabs the mouse with WM_cursor_grab_enable/ungrab()
|
||||
* Multiple grabs must be released with a single ungrab */
|
||||
/** Flag if an operator grabs the mouse with WM_cursor_grab_enable/ungrab().
|
||||
* Multiple grabs must be released with a single ungrab. */
|
||||
bool m_hasGrabMouse;
|
||||
/** Count of number of pressed buttons */
|
||||
/** Count of number of pressed buttons. */
|
||||
int m_nPressedButtons;
|
||||
/** HCURSOR structure of the custom cursor */
|
||||
/** HCURSOR structure of the custom cursor. */
|
||||
HCURSOR m_customCursor;
|
||||
/** request GL context aith alpha channel */
|
||||
/** Request GL context aith alpha channel. */
|
||||
bool m_wantAlphaBackground;
|
||||
|
||||
/** ITaskbarList3 structure for progress bar*/
|
||||
/** ITaskbarList3 structure for progress bar. */
|
||||
ITaskbarList3 *m_Bar;
|
||||
|
||||
static const wchar_t *s_windowClassName;
|
||||
@@ -621,22 +615,21 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
GHOST_WIN32_WTEnable enable = NULL;
|
||||
GHOST_WIN32_WTOverlap overlap = NULL;
|
||||
|
||||
/** Stores the Tablet context if detected Tablet features using WinTab.dll */
|
||||
/** Stores the Tablet context if detected Tablet features using WinTab.dll. */
|
||||
HCTX context = NULL;
|
||||
/** Number of connected Wintab digitizers */
|
||||
/** Number of connected Wintab digitizers. */
|
||||
UINT numDevices = 0;
|
||||
/** Number of cursors currently in contact mapped to system buttons */
|
||||
GHOST_TUns8 numSysButtons = 0;
|
||||
/** Cursors currently in contact mapped to system buttons */
|
||||
DWORD sysButtonsPressed = 0;
|
||||
/** Pressed button map. */
|
||||
GHOST_TUns8 buttons = 0;
|
||||
LONG maxPressure = 0;
|
||||
LONG maxAzimuth = 0, maxAltitude = 0;
|
||||
/** Reusable buffer to read in Wintab Packets. */
|
||||
std::vector<PACKET> pkts;
|
||||
/** Queue of packets to process. */
|
||||
std::queue<PACKET> pendingEvents;
|
||||
} m_wintab;
|
||||
|
||||
/** Most recent tablet data. */
|
||||
GHOST_TabletData lastTabletData = GHOST_TABLET_DATA_NONE;
|
||||
|
||||
/**
|
||||
* Wintab setup.
|
||||
*/
|
||||
@@ -646,12 +639,9 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
||||
* Convert Wintab system mapped (mouse) buttons into Ghost button mask.
|
||||
* \param cursor: The Wintab cursor associated to the button.
|
||||
* \param physicalButton: The physical button ID to inspect.
|
||||
* \param buttonMask: Return pointer for button found.
|
||||
* \return Whether an associated button was found.
|
||||
* \return The system mapped button.
|
||||
*/
|
||||
GHOST_TSuccess wintabMouseToGhost(UINT cursor,
|
||||
WORD physicalButton,
|
||||
GHOST_TButtonMask &buttonMask);
|
||||
GHOST_TButtonMask wintabMouseToGhost(UINT cursor, WORD physicalButton);
|
||||
|
||||
GHOST_TWindowState m_normal_state;
|
||||
|
||||
|
@@ -118,27 +118,27 @@ void MEM_lockfree_freeN(void *vmemh);
|
||||
void *MEM_lockfree_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
void *MEM_lockfree_reallocN_id(void *vmemh,
|
||||
size_t len,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(2);
|
||||
void *MEM_lockfree_recallocN_id(void *vmemh,
|
||||
size_t len,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(2);
|
||||
void *MEM_lockfree_callocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
void *MEM_lockfree_callocN(size_t len, const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
|
||||
void *MEM_lockfree_calloc_arrayN(size_t len,
|
||||
size_t size,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
||||
void *MEM_lockfree_mallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
void *MEM_lockfree_mallocN(size_t len, const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
|
||||
void *MEM_lockfree_malloc_arrayN(size_t len,
|
||||
size_t size,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
||||
void *MEM_lockfree_mallocN_aligned(size_t len,
|
||||
size_t alignment,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
|
||||
void MEM_lockfree_printmemlist_pydict(void);
|
||||
void MEM_lockfree_printmemlist(void);
|
||||
@@ -161,27 +161,27 @@ void MEM_guarded_freeN(void *vmemh);
|
||||
void *MEM_guarded_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
void *MEM_guarded_reallocN_id(void *vmemh,
|
||||
size_t len,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(2);
|
||||
void *MEM_guarded_recallocN_id(void *vmemh,
|
||||
size_t len,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(2);
|
||||
void *MEM_guarded_callocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
void *MEM_guarded_callocN(size_t len, const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
|
||||
void *MEM_guarded_calloc_arrayN(size_t len,
|
||||
size_t size,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
||||
void *MEM_guarded_mallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
void *MEM_guarded_mallocN(size_t len, const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
|
||||
void *MEM_guarded_malloc_arrayN(size_t len,
|
||||
size_t size,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
||||
void *MEM_guarded_mallocN_aligned(size_t len,
|
||||
size_t alignment,
|
||||
const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
|
||||
void MEM_guarded_printmemlist_pydict(void);
|
||||
void MEM_guarded_printmemlist(void);
|
||||
|
@@ -72,6 +72,7 @@ MANTA::MANTA(int *res, FluidModifierData *fmd)
|
||||
mUsingFractions = (fds->flags & FLUID_DOMAIN_USE_FRACTIONS) && mUsingLiquid;
|
||||
mUsingMesh = (fds->flags & FLUID_DOMAIN_USE_MESH) && mUsingLiquid;
|
||||
mUsingDiffusion = (fds->flags & FLUID_DOMAIN_USE_DIFFUSION) && mUsingLiquid;
|
||||
mUsingViscosity = (fds->flags & FLUID_DOMAIN_USE_VISCOSITY) && mUsingLiquid;
|
||||
mUsingMVel = (fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) && mUsingLiquid;
|
||||
mUsingGuiding = (fds->flags & FLUID_DOMAIN_USE_GUIDE);
|
||||
mUsingDrops = (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && mUsingLiquid;
|
||||
@@ -221,6 +222,10 @@ MANTA::MANTA(int *res, FluidModifierData *fmd)
|
||||
initSuccess &= initLiquidMesh();
|
||||
}
|
||||
|
||||
if (mUsingViscosity) {
|
||||
initSuccess &= initLiquidViscosity();
|
||||
}
|
||||
|
||||
if (mUsingDiffusion) {
|
||||
initSuccess &= initCurvature();
|
||||
}
|
||||
@@ -440,6 +445,17 @@ bool MANTA::initLiquidMesh(FluidModifierData *fmd)
|
||||
return runPythonString(pythonCommands);
|
||||
}
|
||||
|
||||
bool MANTA::initLiquidViscosity(FluidModifierData *fmd)
|
||||
{
|
||||
vector<string> pythonCommands;
|
||||
string tmpString = fluid_variables_viscosity + fluid_solver_viscosity + liquid_alloc_viscosity;
|
||||
string finalString = parseScript(tmpString, fmd);
|
||||
pythonCommands.push_back(finalString);
|
||||
|
||||
mUsingViscosity = true;
|
||||
return runPythonString(pythonCommands);
|
||||
}
|
||||
|
||||
bool MANTA::initCurvature(FluidModifierData *fmd)
|
||||
{
|
||||
std::vector<std::string> pythonCommands;
|
||||
@@ -871,8 +887,10 @@ void MANTA::initializeRNAMap(FluidModifierData *fmd)
|
||||
mRNAMap["CACHE_DIR"] = cacheDirectory;
|
||||
mRNAMap["COMPRESSION_OPENVDB"] = vdbCompressionMethod;
|
||||
mRNAMap["PRECISION_OPENVDB"] = vdbPrecisionHalf;
|
||||
mRNAMap["CLIP_OPENVDB"] = to_string(fds->clipping);
|
||||
mRNAMap["CLIP_OPENVDB"] = to_string(fds->clipping);
|
||||
mRNAMap["PP_PARTICLE_MAXIMUM"] = to_string(fds->sys_particle_maximum);
|
||||
mRNAMap["USING_VISCOSITY"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_VISCOSITY);
|
||||
mRNAMap["VISCOSITY_VALUE"] = to_string(fds->viscosity_value);
|
||||
|
||||
/* Fluid object names. */
|
||||
mRNAMap["NAME_FLAGS"] = FLUID_NAME_FLAGS;
|
||||
@@ -1376,15 +1394,14 @@ bool MANTA::readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain)
|
||||
if (with_debug)
|
||||
cout << "MANTA::readGuiding()" << endl;
|
||||
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
|
||||
if (!mUsingGuiding)
|
||||
return false;
|
||||
if (!fds)
|
||||
if (!fmd)
|
||||
return false;
|
||||
|
||||
ostringstream ss;
|
||||
vector<string> pythonCommands;
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
|
||||
string directory = (sourceDomain) ? getDirectory(fmd, FLUID_DOMAIN_DIR_DATA) :
|
||||
getDirectory(fmd, FLUID_DOMAIN_DIR_GUIDE);
|
||||
@@ -1728,6 +1745,7 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd)
|
||||
bool guiding = fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE;
|
||||
bool invel = fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL;
|
||||
bool outflow = fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW;
|
||||
bool viscosity = fds->flags & FLUID_DOMAIN_USE_VISCOSITY;
|
||||
|
||||
string manta_script;
|
||||
|
||||
@@ -1742,6 +1760,8 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd)
|
||||
manta_script += fluid_variables_particles + liquid_variables_particles;
|
||||
if (guiding)
|
||||
manta_script += fluid_variables_guiding;
|
||||
if (viscosity)
|
||||
manta_script += fluid_variables_viscosity;
|
||||
|
||||
/* Solvers. */
|
||||
manta_script += header_solvers + fluid_solver;
|
||||
@@ -1751,6 +1771,8 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd)
|
||||
manta_script += fluid_solver_particles;
|
||||
if (guiding)
|
||||
manta_script += fluid_solver_guiding;
|
||||
if (viscosity)
|
||||
manta_script += fluid_solver_viscosity;
|
||||
|
||||
/* Grids. */
|
||||
manta_script += header_grids + fluid_alloc + liquid_alloc;
|
||||
@@ -1768,6 +1790,8 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd)
|
||||
manta_script += fluid_alloc_invel;
|
||||
if (outflow)
|
||||
manta_script += fluid_alloc_outflow;
|
||||
if (viscosity)
|
||||
manta_script += liquid_alloc_viscosity;
|
||||
|
||||
/* Domain init. */
|
||||
manta_script += header_gridinit + liquid_init_phi;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user