Compare commits
215 Commits
temp-geome
...
temp-node-
Author | SHA1 | Date | |
---|---|---|---|
8300ba81f2 | |||
2db2afeeb6 | |||
482a42c0f6 | |||
23bd6cff81 | |||
d55d4fee30 | |||
10272ca788 | |||
94079ffc7f | |||
23c1e48e19 | |||
5f787b290c | |||
bc837ad14e | |||
1a7b94236e | |||
14901e3774 | |||
d97dca5106 | |||
8d45a96789 | |||
f02e1a77c9 | |||
4d856ecb11 | |||
e9616c82bd | |||
645fc0c767 | |||
b128ffd539 | |||
43369ca80e | |||
d5f2043ab3 | |||
1e4c35d910 | |||
b547ac32d9 | |||
e7f890aa59 | |||
1beca76934 | |||
c59a7f44a1 | |||
5678e6c2bc | |||
87aa514611 | |||
1425411249 | |||
da1b002c8d | |||
54d7dea283 | |||
67b40f829e | |||
![]() |
6ddd280b21 | ||
6ea09db7a1 | |||
e5f0d176d4 | |||
9f323e9bf7 | |||
1f58a0ea3c | |||
b1380101df | |||
a4b6c222fa | |||
88b5d7f5f3 | |||
094c950915 | |||
88e0ed3288 | |||
b48a573adb | |||
04a92297dd | |||
d4e76712d4 | |||
e125c9329d | |||
3ead9b2b36 | |||
5a6d5d20de | |||
88d94d89fa | |||
f1fe42d912 | |||
7fd3b07da1 | |||
681a7d724b | |||
5da5a1cc2d | |||
05fa5ca337 | |||
![]() |
74d5a93b2b | ||
0d65d27386 | |||
![]() |
39bead4d51 | ||
fd10c21f51 | |||
563d513e37 | |||
89e46f578f | |||
52d09dad9c | |||
73b5afd352 | |||
d037fef3bd | |||
f9eaf93d37 | |||
6c33d3d01b | |||
ffbe803505 | |||
6c6f3baaa8 | |||
7d467915a4 | |||
91c44fe885 | |||
661e6e0966 | |||
![]() |
d0dd85a820 | ||
a702ca3faa | |||
fbe2c3f422 | |||
241f05d53c | |||
77ac67dae4 | |||
2db2b7de55 | |||
fe60062a99 | |||
ca516e78c4 | |||
f99e703df3 | |||
81fae56cb9 | |||
bc872e0c8e | |||
2e7e13442c | |||
51f8dbe081 | |||
10e05bb413 | |||
fa8269cc4e | |||
4d3a2ba6a9 | |||
8f38534300 | |||
e0ce76f1c5 | |||
![]() |
1ea5157051 | ||
35cf34de6d | |||
![]() |
ae9d61e7fe | ||
075a19049f | |||
ac6d8241fb | |||
![]() |
19f1c52269 | ||
f56fddbf9a | |||
3681a619de | |||
8034b276ba | |||
fa1569a072 | |||
4e34baddb9 | |||
87f9405c9a | |||
27fa2507a1 | |||
240f15c1b6 | |||
b9734f7716 | |||
80cbbd2843 | |||
3659340571 | |||
8994f09a4a | |||
fc889615f7 | |||
ab26be8ff7 | |||
25c02ea703 | |||
9975af5ffa | |||
![]() |
805d947810 | ||
6af4163a3f | |||
27a7b2e27a | |||
e47f5cf197 | |||
9289c358fb | |||
afcfc6eb08 | |||
3944560b4a | |||
0a6bca1f75 | |||
46d980228b | |||
5b1980859a | |||
a9e7d503dd | |||
![]() |
e7c4c9e538 | ||
dc873c4a7b | |||
c4ab1891cc | |||
![]() |
99dabc156a | ||
![]() |
9f3d41d4ee | ||
a6ec2de96a | |||
![]() |
8e6e8dd871 | ||
414017ac86 | |||
ee367084a7 | |||
10cfa75e1d | |||
da160dc32d | |||
![]() |
9b87d3f029 | ||
0ec82477af | |||
ad31b13f91 | |||
35d5cc8982 | |||
![]() |
12193035ed | ||
8408a8fda2 | |||
80530edcae | |||
![]() |
e684c170f9 | ||
![]() |
70061c69b7 | ||
1e855149b2 | |||
f560bc90c7 | |||
689d6032ff | |||
![]() |
dcd90bf188 | ||
![]() |
97e212f52e | ||
![]() |
63a6268e83 | ||
3d25669486 | |||
![]() |
252c87b9e8 | ||
39ecf90185 | |||
921d0851b5 | |||
2371454b2d | |||
6524ceb8cf | |||
930b8a3932 | |||
9d80b3a69c | |||
f725f42af5 | |||
e5fb7eac85 | |||
23b1872d6e | |||
acc6e5c315 | |||
4bb70e0ba5 | |||
1614795ae2 | |||
847002e1f8 | |||
![]() |
43455f3857 | ||
e867f40611 | |||
fb6c29f59c | |||
ea12df51b8 | |||
d982ea9a9e | |||
fda50625cd | |||
4425bacec4 | |||
64d4e722a0 | |||
8adeab4b0e | |||
ce528d02e1 | |||
![]() |
dda02a448a | ||
e18091650b | |||
ff017df318 | |||
6fd799c72c | |||
758c2210ae | |||
62f5a6bfa7 | |||
![]() |
2f77252086 | ||
de1eeaa3d2 | |||
2281db72b0 | |||
64ca286540 | |||
926f7612fd | |||
25c4118651 | |||
0f13bded46 | |||
5ebe74e779 | |||
1d7adb6d8a | |||
9b42626918 | |||
56df673be2 | |||
a9cd90135a | |||
![]() |
5ffbe439c6 | ||
543becac36 | |||
0083a7615e | |||
9947f20956 | |||
51b316dbc2 | |||
f7fa0d3974 | |||
28cf851a5c | |||
150e0553da | |||
3b0531154e | |||
9ac7946dd0 | |||
43c48965d7 | |||
e2d362757f | |||
fb7d203d21 | |||
bcc6b87881 | |||
1faf31f62d | |||
0f69327140 | |||
497167d3a0 | |||
6a82b2f4c6 | |||
9d20624760 | |||
b9aa7f3ce8 | |||
9a7f0b7fec | |||
47006dbdce | |||
d98e05f4b5 | |||
64d2881650 | |||
acef1af28d |
@@ -255,7 +255,6 @@ ForEachMacros:
|
||||
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN
|
||||
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN
|
||||
- SEQ_ALL_BEGIN
|
||||
- SEQ_CURRENT_BEGIN
|
||||
- SURFACE_QUAD_ITER_BEGIN
|
||||
- foreach
|
||||
- ED_screen_areas_iter
|
||||
|
@@ -43,7 +43,6 @@ Checks: >
|
||||
# the windows compiler currently.
|
||||
-modernize-raw-string-literal
|
||||
|
||||
WarningsAsErrors: '*'
|
||||
CheckOptions:
|
||||
- key: modernize-use-default-member-init.UseAssignment
|
||||
value: 1
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -46,3 +46,6 @@ Desktop.ini
|
||||
|
||||
# smoke simulation noise tile (generated)
|
||||
waveletNoiseTile.bin
|
||||
|
||||
# testing environment
|
||||
/Testing
|
||||
|
@@ -236,9 +236,7 @@ option(WITH_SYSTEM_AUDASPACE "Build with external audaspace library installed on
|
||||
mark_as_advanced(WITH_AUDASPACE)
|
||||
mark_as_advanced(WITH_SYSTEM_AUDASPACE)
|
||||
|
||||
if(NOT WITH_AUDASPACE)
|
||||
set(WITH_SYSTEM_AUDASPACE OFF)
|
||||
endif()
|
||||
set_and_warn_dependency(WITH_AUDASPACE WITH_SYSTEM_AUDASPACE OFF)
|
||||
|
||||
option(WITH_OPENMP "Enable OpenMP (has to be supported by the compiler)" ON)
|
||||
if(UNIX AND NOT APPLE)
|
||||
@@ -522,10 +520,10 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
||||
mark_as_advanced(WITH_LINKER_LLD)
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
option(WITH_COMPILER_ASAN "Build and link against address sanitizer (only for Debug & RelWithDebInfo targets)." OFF)
|
||||
mark_as_advanced(WITH_COMPILER_ASAN)
|
||||
option(WITH_COMPILER_ASAN "Build and link against address sanitizer (only for Debug & RelWithDebInfo targets)." OFF)
|
||||
mark_as_advanced(WITH_COMPILER_ASAN)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if(WITH_COMPILER_ASAN)
|
||||
set(_asan_defaults "\
|
||||
-fsanitize=address \
|
||||
@@ -704,13 +702,11 @@ if(WITH_PYTHON_MODULE AND WITH_PYTHON_INSTALL)
|
||||
message(FATAL_ERROR "WITH_PYTHON_MODULE requires WITH_PYTHON_INSTALL to be OFF")
|
||||
endif()
|
||||
|
||||
if(NOT WITH_PYTHON)
|
||||
set(WITH_CYCLES OFF)
|
||||
set(WITH_DRACO OFF)
|
||||
endif()
|
||||
set_and_warn_dependency(WITH_PYTHON WITH_CYCLES OFF)
|
||||
set_and_warn_dependency(WITH_PYTHON WITH_DRACO OFF)
|
||||
|
||||
if(WITH_DRACO AND NOT WITH_PYTHON_INSTALL)
|
||||
message(STATUS "WITH_DRACO requires WITH_PYTHON_INSTALL to be ON, disabling WITH_DRACO for now")
|
||||
message(WARNING "WITH_DRACO requires WITH_PYTHON_INSTALL to be ON, disabling WITH_DRACO for now")
|
||||
set(WITH_DRACO OFF)
|
||||
endif()
|
||||
|
||||
@@ -729,7 +725,7 @@ set_and_warn_dependency(WITH_PUGIXML WITH_OPENIMAGEIO OFF)
|
||||
|
||||
if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR
|
||||
WITH_OPENVDB OR WITH_OPENCOLORIO OR WITH_USD OR WITH_ALEMBIC))
|
||||
message(STATUS "No dependencies need 'WITH_BOOST' forcing WITH_BOOST=OFF")
|
||||
message(WARNING "No dependencies need 'WITH_BOOST' forcing WITH_BOOST=OFF")
|
||||
set(WITH_BOOST OFF)
|
||||
endif()
|
||||
|
||||
@@ -779,6 +775,7 @@ if(WITH_INSTALL_PORTABLE)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_SDL OR WITH_HEADLESS)
|
||||
message(WARNING "Disabling Ghost Wayland, X11, Input IME, and OpenXR")
|
||||
set(WITH_GHOST_WAYLAND OFF)
|
||||
set(WITH_GHOST_X11 OFF)
|
||||
set(WITH_X11_XINPUT OFF)
|
||||
@@ -809,7 +806,7 @@ endif()
|
||||
if(NOT WITH_CUDA_DYNLOAD)
|
||||
find_package(CUDA)
|
||||
if(NOT CUDA_FOUND)
|
||||
message("CUDA toolkit not found, using dynamic runtime loading of libraries instead")
|
||||
message(WARNING "CUDA toolkit not found, using dynamic runtime loading of libraries (WITH_CUDA_DYNLOAD) instead")
|
||||
set(WITH_CUDA_DYNLOAD ON)
|
||||
endif()
|
||||
endif()
|
||||
@@ -1235,6 +1232,7 @@ if(WITH_OPENMP)
|
||||
string(APPEND CMAKE_C_FLAGS " ${OpenMP_C_FLAGS}")
|
||||
string(APPEND CMAKE_CXX_FLAGS " ${OpenMP_CXX_FLAGS}")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${OpenMP_LINKER_FLAGS}")
|
||||
string(APPEND CMAKE_MODULE_LINKER_FLAGS " ${OpenMP_LINKER_FLAGS}")
|
||||
else()
|
||||
# Typically avoid adding flags as defines but we can't
|
||||
# pass OpenMP flags to the linker for static builds, meaning
|
||||
@@ -1245,6 +1243,7 @@ if(WITH_OPENMP)
|
||||
find_library_static(OpenMP_LIBRARIES gomp ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "OpenMP not found, disabling WITH_OPENMP")
|
||||
set(WITH_OPENMP OFF)
|
||||
endif()
|
||||
|
||||
@@ -1320,6 +1319,7 @@ list(APPEND GL_DEFINITIONS -DGLEW_NO_GLU)
|
||||
if(WITH_BULLET AND WITH_SYSTEM_BULLET)
|
||||
find_package(Bullet)
|
||||
if(NOT BULLET_FOUND)
|
||||
message(WARNING "Bullet not found, disabling WITH_BULLET")
|
||||
set(WITH_BULLET OFF)
|
||||
endif()
|
||||
else()
|
||||
|
@@ -129,7 +129,10 @@ Utilities
|
||||
Create a compressed archive of the source code.
|
||||
|
||||
* update:
|
||||
updates git and all submodules
|
||||
Updates git and all submodules and svn.
|
||||
|
||||
* update_code:
|
||||
Updates git and all submodules but not svn.
|
||||
|
||||
* format:
|
||||
Format source code using clang (uses PATHS if passed in). For example::
|
||||
@@ -522,6 +525,9 @@ icons_geom: .FORCE
|
||||
update: .FORCE
|
||||
$(PYTHON) ./build_files/utils/make_update.py
|
||||
|
||||
update_code: .FORCE
|
||||
$(PYTHON) ./build_files/utils/make_update.py --no-libraries
|
||||
|
||||
format: .FORCE
|
||||
PATH="../lib/${OS_NCASE}_${CPU}/llvm/bin/:../lib/${OS_NCASE}_centos7_${CPU}/llvm/bin/:../lib/${OS_NCASE}/llvm/bin/:$(PATH)" \
|
||||
$(PYTHON) source/tools/utils_maintenance/clang_format_paths.py $(PATHS)
|
||||
|
@@ -21,7 +21,8 @@ if(WIN32)
|
||||
endif()
|
||||
option(WITH_WEBP "Enable building of oiio with webp support" OFF)
|
||||
option(WITH_BOOST_PYTHON "Enable building of boost with python support" OFF)
|
||||
set(MAKE_THREADS 1 CACHE STRING "Number of threads to run make with")
|
||||
cmake_host_system_information(RESULT NUM_CORES QUERY NUMBER_OF_LOGICAL_CORES)
|
||||
set(MAKE_THREADS ${NUM_CORES} CACHE STRING "Number of threads to run make with")
|
||||
|
||||
if(NOT BUILD_MODE)
|
||||
set(BUILD_MODE "Release")
|
||||
|
@@ -56,6 +56,7 @@ list(APPEND ZLIB_LIBRARIES ${BZIP2_LIBRARIES})
|
||||
if(WITH_OPENAL)
|
||||
find_package(OpenAL)
|
||||
if(NOT OPENAL_FOUND)
|
||||
message(WARNING "OpenAL not found, disabling WITH_OPENAL")
|
||||
set(WITH_OPENAL OFF)
|
||||
endif()
|
||||
endif()
|
||||
@@ -65,6 +66,7 @@ if(WITH_JACK)
|
||||
NAMES jackmp
|
||||
)
|
||||
if(NOT JACK_FRAMEWORK)
|
||||
message(WARNING "JACK not found, disabling WITH_JACK")
|
||||
set(WITH_JACK OFF)
|
||||
else()
|
||||
set(JACK_INCLUDE_DIRS ${JACK_FRAMEWORK}/headers)
|
||||
@@ -146,7 +148,7 @@ if(WITH_PYTHON)
|
||||
|
||||
set(PYTHON_INCLUDE_DIR "${_py_framework}/include/python${PYTHON_VERSION}")
|
||||
set(PYTHON_EXECUTABLE "${_py_framework}/bin/python${PYTHON_VERSION}")
|
||||
set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}/config-${PYTHON_VERSION}")
|
||||
set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}")
|
||||
# set(PYTHON_LIBRARY python${PYTHON_VERSION})
|
||||
# set(PYTHON_LINKFLAGS "-u _PyMac_Error -framework Python") # won't build with this enabled
|
||||
|
||||
@@ -357,7 +359,7 @@ if(WITH_CYCLES_OSL)
|
||||
if(OSL_INCLUDE_DIR AND OSL_LIBRARIES AND OSL_COMPILER AND OSL_SHADER_DIR)
|
||||
set(OSL_FOUND TRUE)
|
||||
else()
|
||||
message(STATUS "OSL not found")
|
||||
message(WARNING "OSL not found, disabling WITH_CYCLES_OSL")
|
||||
set(WITH_CYCLES_OSL OFF)
|
||||
endif()
|
||||
endif()
|
||||
@@ -413,7 +415,7 @@ if(WITH_OPENMP)
|
||||
set(OpenMP_LINKER_FLAGS "-L'${LIBDIR}/openmp/lib' -lomp")
|
||||
|
||||
# Copy libomp.dylib to allow executables like datatoc and tests to work.
|
||||
# `@executable_path/../Resources/lib/` `LC_ID_DYLIB`is added by the deps builder.
|
||||
# `@executable_path/../Resources/lib/` `LC_ID_DYLIB` is added by the deps builder.
|
||||
# For single config generator datatoc, tests etc.
|
||||
execute_process(
|
||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/Resources/lib
|
||||
|
@@ -149,11 +149,6 @@ add_definitions(-D_WIN32_WINNT=0x601)
|
||||
include(build_files/cmake/platform/platform_win32_bundle_crt.cmake)
|
||||
remove_cc_flag("/MDd" "/MD" "/Zi")
|
||||
|
||||
if(WITH_WINDOWS_PDB)
|
||||
set(PDB_INFO_OVERRIDE_FLAGS "/Z7")
|
||||
set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
|
||||
endif()
|
||||
|
||||
if(MSVC_CLANG) # Clangs version of cl doesn't support all flags
|
||||
string(APPEND CMAKE_CXX_FLAGS " ${CXX_WARN_FLAGS} /nologo /J /Gd /EHsc -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference ")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference")
|
||||
@@ -162,6 +157,21 @@ else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd /MP /bigobj")
|
||||
endif()
|
||||
|
||||
# X64 ASAN is available and usable on MSVC 16.9 preview 4 and up)
|
||||
if(WITH_COMPILER_ASAN AND MSVC AND NOT MSVC_CLANG)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28.29828)
|
||||
#set a flag so we don't have to do this comparison all the time
|
||||
SET(MSVC_ASAN On)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " /INCREMENTAL:NO")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG " /INCREMENTAL:NO")
|
||||
else()
|
||||
message("-- ASAN not supported on MSVC ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# C++ standards conformace (/permissive-) is available on msvc 15.5 (1912) and up
|
||||
if(MSVC_VERSION GREATER 1911 AND NOT MSVC_CLANG)
|
||||
string(APPEND CMAKE_CXX_FLAGS " /permissive-")
|
||||
@@ -174,14 +184,41 @@ if(WITH_WINDOWS_SCCACHE AND CMAKE_VS_MSBUILD_COMMAND)
|
||||
set(WITH_WINDOWS_SCCACHE Off)
|
||||
endif()
|
||||
|
||||
# Debug Symbol format
|
||||
# sccache # MSVC_ASAN # format # why
|
||||
# On # On # Z7 # sccache will only play nice with Z7
|
||||
# On # Off # Z7 # sccache will only play nice with Z7
|
||||
# Off # On # Zi # Asan will not play nice with Edit and Continue
|
||||
# Off # Off # ZI # Neither asan nor sscache is enabled Edit and Continue is available
|
||||
|
||||
# Release Symbol format
|
||||
# sccache # MSVC_ASAN # format # why
|
||||
# On # On # Z7 # sccache will only play nice with Z7
|
||||
# On # Off # Z7 # sccache will only play nice with Z7
|
||||
# Off # On # Zi # Asan will not play nice with Edit and Continue
|
||||
# Off # Off # Zi # Edit and Continue disables some optimizations
|
||||
|
||||
|
||||
if(WITH_WINDOWS_SCCACHE)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER sccache)
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER sccache)
|
||||
set(SYMBOL_FORMAT /Z7)
|
||||
set(SYMBOL_FORMAT_RELEASE /Z7)
|
||||
else()
|
||||
unset(CMAKE_C_COMPILER_LAUNCHER)
|
||||
unset(CMAKE_CXX_COMPILER_LAUNCHER)
|
||||
set(SYMBOL_FORMAT /ZI)
|
||||
if(MSVC_ASAN)
|
||||
set(SYMBOL_FORMAT /Z7)
|
||||
set(SYMBOL_FORMAT_RELEASE /Z7)
|
||||
else()
|
||||
set(SYMBOL_FORMAT /ZI)
|
||||
set(SYMBOL_FORMAT_RELEASE /Zi)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_WINDOWS_PDB)
|
||||
set(PDB_INFO_OVERRIDE_FLAGS "${SYMBOL_FORMAT_RELEASE}")
|
||||
set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
|
||||
endif()
|
||||
|
||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " /MDd ${SYMBOL_FORMAT}")
|
||||
@@ -190,9 +227,11 @@ string(APPEND CMAKE_CXX_FLAGS_RELEASE " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
string(APPEND CMAKE_C_FLAGS_RELEASE " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
string(APPEND CMAKE_C_FLAGS_MINSIZEREL " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT}")
|
||||
string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT}")
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT_RELEASE}")
|
||||
string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT_RELEASE}")
|
||||
unset(SYMBOL_FORMAT)
|
||||
unset(SYMBOL_FORMAT_RELEASE)
|
||||
|
||||
# JMC is available on msvc 15.8 (1915) and up
|
||||
if(MSVC_VERSION GREATER 1914 AND NOT MSVC_CLANG)
|
||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " /JMC")
|
||||
|
@@ -9,14 +9,10 @@ if "%BUILD_WITH_SCCACHE%"=="1" (
|
||||
|
||||
if "%WITH_CLANG%"=="1" (
|
||||
set CLANG_CMAKE_ARGS=-T"llvm"
|
||||
if "%WITH_ASAN%"=="1" (
|
||||
)
|
||||
|
||||
if "%WITH_ASAN%"=="1" (
|
||||
set ASAN_CMAKE_ARGS=-DWITH_COMPILER_ASAN=On
|
||||
)
|
||||
) else (
|
||||
if "%WITH_ASAN%"=="1" (
|
||||
echo ASAN is only supported with clang.
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
if "%WITH_PYDEBUG%"=="1" (
|
||||
|
@@ -46,16 +46,10 @@ set LLVM_DIR=
|
||||
set CFLAGS=-m64 -fmsc-version=1914
|
||||
set CXXFLAGS=-m64 -fmsc-version=1914
|
||||
)
|
||||
if "%WITH_ASAN%"=="1" (
|
||||
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -DWITH_COMPILER_ASAN=On
|
||||
)
|
||||
)
|
||||
|
||||
if "%WITH_ASAN%"=="1" (
|
||||
if "%WITH_CLANG%" == "" (
|
||||
echo ASAN is only supported with clang.
|
||||
exit /b 1
|
||||
)
|
||||
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -DWITH_COMPILER_ASAN=On
|
||||
)
|
||||
|
||||
if NOT "%verbose%" == "" (
|
||||
|
@@ -553,7 +553,7 @@ def example_extract_docstring(filepath):
|
||||
line_no += 1
|
||||
else:
|
||||
file.close()
|
||||
return "", 0
|
||||
return "", 0, False
|
||||
|
||||
for line in file:
|
||||
line_no += 1
|
||||
@@ -563,15 +563,17 @@ def example_extract_docstring(filepath):
|
||||
text.append(line.rstrip())
|
||||
|
||||
line_no += 1
|
||||
line_no_has_content = False
|
||||
|
||||
# Skip over blank lines so the Python code doesn't have blank lines at the top.
|
||||
for line in file:
|
||||
if line.strip():
|
||||
line_no_has_content = True
|
||||
break
|
||||
line_no += 1
|
||||
|
||||
file.close()
|
||||
return "\n".join(text), line_no
|
||||
return "\n".join(text), line_no, line_no_has_content
|
||||
|
||||
|
||||
def title_string(text, heading_char, double=False):
|
||||
@@ -590,16 +592,18 @@ def write_example_ref(ident, fw, example_id, ext="py"):
|
||||
filepath = os.path.join("..", "examples", "%s.%s" % (example_id, ext))
|
||||
filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
|
||||
|
||||
text, line_no = example_extract_docstring(filepath_full)
|
||||
text, line_no, line_no_has_content = example_extract_docstring(filepath_full)
|
||||
|
||||
for line in text.split("\n"):
|
||||
fw("%s\n" % (ident + line).rstrip())
|
||||
fw("\n")
|
||||
|
||||
fw("%s.. literalinclude:: %s\n" % (ident, filepath))
|
||||
if line_no > 0:
|
||||
fw("%s :lines: %d-\n" % (ident, line_no))
|
||||
fw("\n")
|
||||
# Some files only contain a doc-string.
|
||||
if line_no_has_content:
|
||||
fw("%s.. literalinclude:: %s\n" % (ident, filepath))
|
||||
if line_no > 0:
|
||||
fw("%s :lines: %d-\n" % (ident, line_no))
|
||||
fw("\n")
|
||||
EXAMPLE_SET_USED.add(example_id)
|
||||
else:
|
||||
if bpy.app.debug:
|
||||
@@ -1408,7 +1412,8 @@ def pyrna2sphinx(basepath):
|
||||
else:
|
||||
fw(" .. attribute:: %s\n\n" % prop.identifier)
|
||||
if prop.description:
|
||||
fw(" %s\n\n" % prop.description)
|
||||
write_indented_lines(" ", fw, prop.description, False)
|
||||
fw("\n")
|
||||
|
||||
# special exception, can't use generic code here for enums
|
||||
if prop.type == "enum":
|
||||
|
4
extern/CMakeLists.txt
vendored
4
extern/CMakeLists.txt
vendored
@@ -109,3 +109,7 @@ endif()
|
||||
if(WITH_MOD_FLUID)
|
||||
add_subdirectory(mantaflow)
|
||||
endif()
|
||||
|
||||
if (WITH_COMPOSITOR)
|
||||
add_subdirectory(smaa_areatex)
|
||||
endif()
|
||||
|
2
extern/audaspace/CMakeLists.txt
vendored
2
extern/audaspace/CMakeLists.txt
vendored
@@ -42,6 +42,7 @@ set(SRC
|
||||
src/devices/NULLDevice.cpp
|
||||
src/devices/ReadDevice.cpp
|
||||
src/devices/SoftwareDevice.cpp
|
||||
src/devices/ThreadedDevice.cpp
|
||||
src/Exception.cpp
|
||||
src/file/File.cpp
|
||||
src/file/FileManager.cpp
|
||||
@@ -148,6 +149,7 @@ set(PUBLIC_HDR
|
||||
include/devices/NULLDevice.h
|
||||
include/devices/ReadDevice.h
|
||||
include/devices/SoftwareDevice.h
|
||||
include/devices/ThreadedDevice.h
|
||||
include/Exception.h
|
||||
include/file/File.h
|
||||
include/file/FileManager.h
|
||||
|
@@ -255,6 +255,7 @@ protected:
|
||||
/**
|
||||
* This function tells the device, to start or pause playback.
|
||||
* \param playing True if device should playback.
|
||||
* \note This method is only called when the device is locked.
|
||||
*/
|
||||
virtual void playing(bool playing)=0;
|
||||
|
||||
|
95
extern/audaspace/include/devices/ThreadedDevice.h
vendored
Normal file
95
extern/audaspace/include/devices/ThreadedDevice.h
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file ThreadedDevice.h
|
||||
* @ingroup plugin
|
||||
* The ThreadedDevice class.
|
||||
*/
|
||||
|
||||
#include "devices/SoftwareDevice.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* This device extends the SoftwareDevice with code for running mixing in a separate thread.
|
||||
*/
|
||||
class AUD_PLUGIN_API ThreadedDevice : public SoftwareDevice
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Whether there is currently playback.
|
||||
*/
|
||||
bool m_playing;
|
||||
|
||||
/**
|
||||
* Whether the current playback should stop.
|
||||
*/
|
||||
bool m_stop;
|
||||
|
||||
/**
|
||||
* The streaming thread.
|
||||
*/
|
||||
std::thread m_thread;
|
||||
|
||||
/**
|
||||
* Starts the streaming thread.
|
||||
*/
|
||||
AUD_LOCAL void start();
|
||||
|
||||
/**
|
||||
* Streaming thread main function.
|
||||
*/
|
||||
AUD_LOCAL virtual void runMixingThread()=0;
|
||||
|
||||
// delete copy constructor and operator=
|
||||
ThreadedDevice(const ThreadedDevice&) = delete;
|
||||
ThreadedDevice& operator=(const ThreadedDevice&) = delete;
|
||||
|
||||
protected:
|
||||
virtual void playing(bool playing);
|
||||
|
||||
/**
|
||||
* Empty default constructor. To setup the device call the function create()
|
||||
* and to uninitialize call destroy().
|
||||
*/
|
||||
ThreadedDevice();
|
||||
|
||||
/**
|
||||
* Indicates that the mixing thread should be stopped.
|
||||
* \return Whether the mixing thread should be stopping.
|
||||
* \warning For thread safety, the device needs to be locked, when this method is called.
|
||||
*/
|
||||
inline bool shouldStop() { return m_stop; }
|
||||
|
||||
/**
|
||||
* This method needs to be called when the mixing thread is stopping.
|
||||
* \warning For thread safety, the device needs to be locked, when this method is called.
|
||||
*/
|
||||
inline void doStop() { m_stop = m_playing = false; }
|
||||
|
||||
/**
|
||||
* Stops all playback and notifies the mixing thread to stop.
|
||||
* \warning The device has to be unlocked to not run into a deadlock.
|
||||
*/
|
||||
void stopMixingThread();
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
@@ -27,9 +27,9 @@ void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data
|
||||
{
|
||||
PulseAudioDevice* device = (PulseAudioDevice*)data;
|
||||
|
||||
device->m_state = AUD_pa_context_get_state(context);
|
||||
std::lock_guard<ILockable> lock(*device);
|
||||
|
||||
AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0);
|
||||
device->m_state = AUD_pa_context_get_state(context);
|
||||
}
|
||||
|
||||
void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t num_bytes, void *data)
|
||||
@@ -68,29 +68,40 @@ void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
void PulseAudioDevice::playing(bool playing)
|
||||
void PulseAudioDevice::runMixingThread()
|
||||
{
|
||||
m_playback = playing;
|
||||
for(;;)
|
||||
{
|
||||
{
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr);
|
||||
if(shouldStop())
|
||||
{
|
||||
AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr);
|
||||
doStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(AUD_pa_stream_is_corked(m_stream))
|
||||
AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr);
|
||||
|
||||
AUD_pa_mainloop_iterate(m_mainloop, true, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) :
|
||||
m_playback(false),
|
||||
m_state(PA_CONTEXT_UNCONNECTED),
|
||||
m_buffersize(buffersize),
|
||||
m_underflows(0)
|
||||
{
|
||||
m_mainloop = AUD_pa_threaded_mainloop_new();
|
||||
m_mainloop = AUD_pa_mainloop_new();
|
||||
|
||||
AUD_pa_threaded_mainloop_lock(m_mainloop);
|
||||
|
||||
m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str());
|
||||
m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str());
|
||||
|
||||
if(!m_context)
|
||||
{
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
|
||||
}
|
||||
@@ -99,26 +110,21 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
|
||||
|
||||
AUD_pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
|
||||
|
||||
AUD_pa_threaded_mainloop_start(m_mainloop);
|
||||
|
||||
while(m_state != PA_CONTEXT_READY)
|
||||
{
|
||||
switch(m_state)
|
||||
{
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
|
||||
break;
|
||||
default:
|
||||
AUD_pa_threaded_mainloop_wait(m_mainloop);
|
||||
AUD_pa_mainloop_iterate(m_mainloop, true, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -166,13 +172,10 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
|
||||
|
||||
if(!m_stream)
|
||||
{
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not create PulseAudio stream.");
|
||||
}
|
||||
@@ -188,32 +191,27 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
|
||||
buffer_attr.prebuf = -1U;
|
||||
buffer_attr.tlength = buffersize;
|
||||
|
||||
if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0)
|
||||
if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0)
|
||||
{
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
AUD_THROW(DeviceException, "Could not connect PulseAudio stream.");
|
||||
}
|
||||
|
||||
AUD_pa_threaded_mainloop_unlock(m_mainloop);
|
||||
|
||||
create();
|
||||
}
|
||||
|
||||
PulseAudioDevice::~PulseAudioDevice()
|
||||
{
|
||||
AUD_pa_threaded_mainloop_stop(m_mainloop);
|
||||
stopMixingThread();
|
||||
|
||||
AUD_pa_context_disconnect(m_context);
|
||||
AUD_pa_context_unref(m_context);
|
||||
|
||||
AUD_pa_threaded_mainloop_free(m_mainloop);
|
||||
AUD_pa_mainloop_free(m_mainloop);
|
||||
|
||||
destroy();
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@
|
||||
* The PulseAudioDevice class.
|
||||
*/
|
||||
|
||||
#include "devices/SoftwareDevice.h"
|
||||
#include "devices/ThreadedDevice.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
@@ -35,15 +35,10 @@ AUD_NAMESPACE_BEGIN
|
||||
/**
|
||||
* This device plays back through PulseAudio, the simple direct media layer.
|
||||
*/
|
||||
class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice
|
||||
class AUD_PLUGIN_API PulseAudioDevice : public ThreadedDevice
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Whether there is currently playback.
|
||||
*/
|
||||
volatile bool m_playback;
|
||||
|
||||
pa_threaded_mainloop* m_mainloop;
|
||||
pa_mainloop* m_mainloop;
|
||||
pa_context* m_context;
|
||||
pa_stream* m_stream;
|
||||
pa_context_state_t m_state;
|
||||
@@ -74,13 +69,15 @@ private:
|
||||
*/
|
||||
AUD_LOCAL static void PulseAudio_underflow(pa_stream* stream, void* data);
|
||||
|
||||
/**
|
||||
* Streaming thread main function.
|
||||
*/
|
||||
AUD_LOCAL void runMixingThread();
|
||||
|
||||
// delete copy constructor and operator=
|
||||
PulseAudioDevice(const PulseAudioDevice&) = delete;
|
||||
PulseAudioDevice& operator=(const PulseAudioDevice&) = delete;
|
||||
|
||||
protected:
|
||||
virtual void playing(bool playing);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Opens the PulseAudio audio device for playback.
|
||||
|
@@ -24,18 +24,14 @@ PULSEAUDIO_SYMBOL(pa_context_unref);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_begin_write);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_connect_playback);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_cork);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_is_corked);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_new);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_set_underflow_callback);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_set_write_callback);
|
||||
PULSEAUDIO_SYMBOL(pa_stream_write);
|
||||
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_free);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_get_api);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_lock);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_new);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_signal);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_start);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_stop);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_unlock);
|
||||
PULSEAUDIO_SYMBOL(pa_threaded_mainloop_wait);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_free);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_get_api);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_new);
|
||||
PULSEAUDIO_SYMBOL(pa_mainloop_iterate);
|
||||
|
193
extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
vendored
193
extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
vendored
@@ -31,159 +31,83 @@ template <class T> void SafeRelease(T **ppT)
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPIDevice::start()
|
||||
{
|
||||
lock();
|
||||
|
||||
// thread is still running, we can abort stopping it
|
||||
if(m_stop)
|
||||
m_stop = false;
|
||||
// thread is not running, let's start it
|
||||
else if(!m_playing)
|
||||
{
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
|
||||
m_playing = true;
|
||||
|
||||
m_thread = std::thread(&WASAPIDevice::updateStream, this);
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
void WASAPIDevice::updateStream()
|
||||
void WASAPIDevice::runMixingThread()
|
||||
{
|
||||
UINT32 buffer_size;
|
||||
UINT32 padding;
|
||||
UINT32 length;
|
||||
data_t* buffer;
|
||||
|
||||
lock();
|
||||
|
||||
if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
|
||||
{
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
IAudioRenderClient* render_client = nullptr;
|
||||
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
||||
|
||||
if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
|
||||
{
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
||||
|
||||
if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
|
||||
goto init_error;
|
||||
|
||||
if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
|
||||
goto init_error;
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
goto init_error;
|
||||
|
||||
length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
goto init_error;
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
{
|
||||
init_error:
|
||||
SafeRelease(&render_client);
|
||||
doStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 padding;
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
{
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
UINT32 length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
{
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
{
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
m_audio_client->Start();
|
||||
|
||||
auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
lock();
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
|
||||
goto stop_thread;
|
||||
|
||||
length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
goto stop_thread;
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
goto stop_thread;
|
||||
|
||||
// stop thread
|
||||
if(shouldStop())
|
||||
{
|
||||
stop_thread:
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
doStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
length = buffer_size - padding;
|
||||
|
||||
if(FAILED(render_client->GetBuffer(length, &buffer)))
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
mix((data_t*)buffer, length);
|
||||
|
||||
if(FAILED(render_client->ReleaseBuffer(length, 0)))
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// stop thread
|
||||
if(m_stop)
|
||||
{
|
||||
m_audio_client->Stop();
|
||||
SafeRelease(&render_client);
|
||||
m_playing = false;
|
||||
m_stop = false;
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
std::this_thread::sleep_for(sleepDuration);
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPIDevice::playing(bool playing)
|
||||
{
|
||||
if((!m_playing || m_stop) && playing)
|
||||
start();
|
||||
else
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
|
||||
m_playing(false),
|
||||
m_stop(false),
|
||||
|
||||
m_imm_device_enumerator(nullptr),
|
||||
m_imm_device(nullptr),
|
||||
m_audio_client(nullptr),
|
||||
@@ -361,14 +285,7 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
|
||||
|
||||
WASAPIDevice::~WASAPIDevice()
|
||||
{
|
||||
lock();
|
||||
|
||||
stopAll();
|
||||
|
||||
unlock();
|
||||
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
stopMixingThread();
|
||||
|
||||
SafeRelease(&m_audio_client);
|
||||
SafeRelease(&m_imm_device);
|
||||
|
29
extern/audaspace/plugins/wasapi/WASAPIDevice.h
vendored
29
extern/audaspace/plugins/wasapi/WASAPIDevice.h
vendored
@@ -26,7 +26,7 @@
|
||||
* The WASAPIDevice class.
|
||||
*/
|
||||
|
||||
#include "devices/SoftwareDevice.h"
|
||||
#include "devices/ThreadedDevice.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
@@ -40,46 +40,23 @@ AUD_NAMESPACE_BEGIN
|
||||
/**
|
||||
* This device plays back through WASAPI, the Windows audio API.
|
||||
*/
|
||||
class AUD_PLUGIN_API WASAPIDevice : public SoftwareDevice
|
||||
class AUD_PLUGIN_API WASAPIDevice : public ThreadedDevice
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Whether there is currently playback.
|
||||
*/
|
||||
bool m_playing;
|
||||
|
||||
/**
|
||||
* Whether the current playback should stop.
|
||||
*/
|
||||
bool m_stop;
|
||||
|
||||
IMMDeviceEnumerator* m_imm_device_enumerator;
|
||||
IMMDevice* m_imm_device;
|
||||
IAudioClient* m_audio_client;
|
||||
WAVEFORMATEXTENSIBLE m_wave_format_extensible;
|
||||
|
||||
/**
|
||||
* The streaming thread.
|
||||
*/
|
||||
std::thread m_thread;
|
||||
|
||||
/**
|
||||
* Starts the streaming thread.
|
||||
*/
|
||||
AUD_LOCAL void start();
|
||||
|
||||
/**
|
||||
* Streaming thread main function.
|
||||
*/
|
||||
AUD_LOCAL void updateStream();
|
||||
AUD_LOCAL void runMixingThread();
|
||||
|
||||
// delete copy constructor and operator=
|
||||
WASAPIDevice(const WASAPIDevice&) = delete;
|
||||
WASAPIDevice& operator=(const WASAPIDevice&) = delete;
|
||||
|
||||
protected:
|
||||
virtual void playing(bool playing);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Opens the WASAPI audio device for playback.
|
||||
|
@@ -737,7 +737,7 @@ void SoftwareDevice::mix(data_t* buffer, int length)
|
||||
{
|
||||
m_buffer.assureSize(length * AUD_SAMPLE_SIZE(m_specs));
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
{
|
||||
std::shared_ptr<SoftwareDevice::SoftwareHandle> sound;
|
||||
@@ -880,7 +880,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<IReader> reader, b
|
||||
// play sound
|
||||
std::shared_ptr<SoftwareDevice::SoftwareHandle> sound = std::shared_ptr<SoftwareDevice::SoftwareHandle>(new SoftwareDevice::SoftwareHandle(this, reader, pitch, resampler, mapper, keep));
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
m_playingSounds.push_back(sound);
|
||||
|
||||
@@ -897,7 +897,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<ISound> sound, boo
|
||||
|
||||
void SoftwareDevice::stopAll()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
while(!m_playingSounds.empty())
|
||||
m_playingSounds.front()->stop();
|
||||
|
65
extern/audaspace/src/devices/ThreadedDevice.cpp
vendored
Normal file
65
extern/audaspace/src/devices/ThreadedDevice.cpp
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
*
|
||||
* 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 "devices/ThreadedDevice.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
void ThreadedDevice::start()
|
||||
{
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
// thread is still running, we can abort stopping it
|
||||
if(m_stop)
|
||||
m_stop = false;
|
||||
|
||||
// thread is not running, let's start it
|
||||
else if(!m_playing)
|
||||
{
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
|
||||
m_playing = true;
|
||||
|
||||
m_thread = std::thread(&ThreadedDevice::runMixingThread, this);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadedDevice::playing(bool playing)
|
||||
{
|
||||
if((!m_playing || m_stop) && playing)
|
||||
start();
|
||||
else
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
ThreadedDevice::ThreadedDevice() :
|
||||
m_playing(false),
|
||||
m_stop(false)
|
||||
{
|
||||
}
|
||||
|
||||
void aud::ThreadedDevice::stopMixingThread()
|
||||
{
|
||||
stopAll();
|
||||
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
AUD_NAMESPACE_END
|
26
extern/smaa_areatex/CMakeLists.txt
vendored
Normal file
26
extern/smaa_areatex/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# The Original Code is Copyright (C) 2017, Blender Foundation
|
||||
# All rights reserved.
|
||||
#
|
||||
# The Original Code is: all of this file.
|
||||
#
|
||||
# Contributor(s): IRIE Shinsuke
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
add_executable(smaa_areatex smaa_areatex.cpp)
|
5
extern/smaa_areatex/README.blender
vendored
Normal file
5
extern/smaa_areatex/README.blender
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
Project: smaa-cpp
|
||||
URL: https://github.com/iRi-E/smaa-cpp
|
||||
License: MIT
|
||||
Upstream version: 0.4.0
|
||||
Local modifications:
|
1208
extern/smaa_areatex/smaa_areatex.cpp
vendored
Normal file
1208
extern/smaa_areatex/smaa_areatex.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ BlenderImageLoader::BlenderImageLoader(BL::Image b_image, int frame)
|
||||
{
|
||||
}
|
||||
|
||||
bool BlenderImageLoader::load_metadata(ImageMetaData &metadata)
|
||||
bool BlenderImageLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
|
||||
{
|
||||
metadata.width = b_image.size()[0];
|
||||
metadata.height = b_image.size()[1];
|
||||
@@ -171,7 +171,7 @@ BlenderPointDensityLoader::BlenderPointDensityLoader(BL::Depsgraph b_depsgraph,
|
||||
{
|
||||
}
|
||||
|
||||
bool BlenderPointDensityLoader::load_metadata(ImageMetaData &metadata)
|
||||
bool BlenderPointDensityLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
|
||||
{
|
||||
metadata.channels = 4;
|
||||
metadata.width = b_node.resolution();
|
||||
|
@@ -27,7 +27,7 @@ class BlenderImageLoader : public ImageLoader {
|
||||
public:
|
||||
BlenderImageLoader(BL::Image b_image, int frame);
|
||||
|
||||
bool load_metadata(ImageMetaData &metadata) override;
|
||||
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
|
||||
bool load_pixels(const ImageMetaData &metadata,
|
||||
void *pixels,
|
||||
const size_t pixels_size,
|
||||
@@ -44,7 +44,7 @@ class BlenderPointDensityLoader : public ImageLoader {
|
||||
public:
|
||||
BlenderPointDensityLoader(BL::Depsgraph depsgraph, BL::ShaderNodeTexPointDensity b_node);
|
||||
|
||||
bool load_metadata(ImageMetaData &metadata) override;
|
||||
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
|
||||
bool load_pixels(const ImageMetaData &metadata,
|
||||
void *pixels,
|
||||
const size_t pixels_size,
|
||||
|
@@ -375,7 +375,7 @@ static void attr_create_generic(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool
|
||||
case BL::Attribute::domain_POINT:
|
||||
element = ATTR_ELEMENT_VERTEX;
|
||||
break;
|
||||
case BL::Attribute::domain_POLYGON:
|
||||
case BL::Attribute::domain_FACE:
|
||||
element = ATTR_ELEMENT_FACE;
|
||||
break;
|
||||
default:
|
||||
|
@@ -41,7 +41,7 @@ class BlenderSmokeLoader : public ImageLoader {
|
||||
mesh_texture_space(b_mesh, texspace_loc, texspace_size);
|
||||
}
|
||||
|
||||
bool load_metadata(ImageMetaData &metadata) override
|
||||
bool load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata) override
|
||||
{
|
||||
if (!b_domain) {
|
||||
return false;
|
||||
|
@@ -619,6 +619,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
|
||||
info.num = 0;
|
||||
|
||||
info.has_half_images = true;
|
||||
info.has_nanovdb = true;
|
||||
info.has_volume_decoupled = true;
|
||||
info.has_branched_path = true;
|
||||
info.has_adaptive_stop_per_sample = true;
|
||||
@@ -665,6 +666,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
|
||||
|
||||
/* Accumulate device info. */
|
||||
info.has_half_images &= device.has_half_images;
|
||||
info.has_nanovdb &= device.has_nanovdb;
|
||||
info.has_volume_decoupled &= device.has_volume_decoupled;
|
||||
info.has_branched_path &= device.has_branched_path;
|
||||
info.has_adaptive_stop_per_sample &= device.has_adaptive_stop_per_sample;
|
||||
|
@@ -78,6 +78,7 @@ class DeviceInfo {
|
||||
int num;
|
||||
bool display_device; /* GPU is used as a display device. */
|
||||
bool has_half_images; /* Support half-float textures. */
|
||||
bool has_nanovdb; /* Support NanoVDB volumes. */
|
||||
bool has_volume_decoupled; /* Decoupled volume shading. */
|
||||
bool has_branched_path; /* Supports branched path tracing. */
|
||||
bool has_adaptive_stop_per_sample; /* Per-sample adaptive sampling stopping. */
|
||||
@@ -99,6 +100,7 @@ class DeviceInfo {
|
||||
cpu_threads = 0;
|
||||
display_device = false;
|
||||
has_half_images = false;
|
||||
has_nanovdb = false;
|
||||
has_volume_decoupled = false;
|
||||
has_branched_path = true;
|
||||
has_adaptive_stop_per_sample = false;
|
||||
|
@@ -1654,6 +1654,7 @@ void device_cpu_info(vector<DeviceInfo> &devices)
|
||||
info.has_adaptive_stop_per_sample = true;
|
||||
info.has_osl = true;
|
||||
info.has_half_images = true;
|
||||
info.has_nanovdb = true;
|
||||
info.has_profiling = true;
|
||||
info.denoisers = DENOISER_NLM;
|
||||
if (openimagedenoise_supported()) {
|
||||
|
@@ -128,6 +128,7 @@ void device_cuda_info(vector<DeviceInfo> &devices)
|
||||
info.num = num;
|
||||
|
||||
info.has_half_images = (major >= 3);
|
||||
info.has_nanovdb = true;
|
||||
info.has_volume_decoupled = false;
|
||||
info.has_adaptive_stop_per_sample = false;
|
||||
info.denoisers = DENOISER_NLM;
|
||||
|
@@ -46,10 +46,13 @@ class MultiDevice : public Device {
|
||||
list<SubDevice> devices, denoising_devices;
|
||||
device_ptr unique_key;
|
||||
vector<vector<SubDevice *>> peer_islands;
|
||||
bool use_denoising;
|
||||
bool matching_rendering_and_denoising_devices;
|
||||
|
||||
MultiDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_)
|
||||
: Device(info, stats, profiler, background_), unique_key(1)
|
||||
: Device(info, stats, profiler, background_),
|
||||
unique_key(1),
|
||||
use_denoising(!info.denoising_devices.empty())
|
||||
{
|
||||
foreach (DeviceInfo &subinfo, info.multi_devices) {
|
||||
/* Always add CPU devices at the back since GPU devices can change
|
||||
@@ -194,6 +197,7 @@ class MultiDevice : public Device {
|
||||
if (!sub.device->load_kernels(requested_features))
|
||||
return false;
|
||||
|
||||
use_denoising = requested_features.use_denoising;
|
||||
if (requested_features.use_denoising) {
|
||||
/* Only need denoising feature, everything else is unused. */
|
||||
DeviceRequestedFeatures denoising_features;
|
||||
@@ -400,7 +404,7 @@ class MultiDevice : public Device {
|
||||
size_t existing_size = mem.device_size;
|
||||
|
||||
/* The tile buffers are allocated on each device (see below), so copy to all of them */
|
||||
if (strcmp(mem.name, "RenderBuffers") == 0) {
|
||||
if (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising) {
|
||||
foreach (SubDevice &sub, devices) {
|
||||
mem.device = sub.device;
|
||||
mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0;
|
||||
@@ -466,7 +470,7 @@ class MultiDevice : public Device {
|
||||
/* This is a hack to only allocate the tile buffers on denoising devices
|
||||
* Similarly the tile buffers also need to be allocated separately on all devices so any
|
||||
* overlap rendered for denoising does not interfere with each other */
|
||||
if (strcmp(mem.name, "RenderBuffers") == 0) {
|
||||
if (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising) {
|
||||
vector<device_ptr> device_pointers;
|
||||
device_pointers.reserve(devices.size());
|
||||
|
||||
@@ -518,7 +522,7 @@ class MultiDevice : public Device {
|
||||
size_t existing_size = mem.device_size;
|
||||
|
||||
/* Free memory that was allocated for all devices (see above) on each device */
|
||||
if (strcmp(mem.name, "RenderBuffers") == 0 || mem.type == MEM_PIXELS) {
|
||||
if (mem.type == MEM_PIXELS || (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising)) {
|
||||
foreach (SubDevice &sub, devices) {
|
||||
mem.device = sub.device;
|
||||
mem.device_pointer = sub.ptr_map[key];
|
||||
|
@@ -126,6 +126,9 @@ void device_opencl_info(vector<DeviceInfo> &devices)
|
||||
/* Check OpenCL extensions */
|
||||
info.has_half_images = platform_device.device_extensions.find("cl_khr_fp16") != string::npos;
|
||||
|
||||
/* Disabled for now due to apparent AMD driver bug. */
|
||||
info.has_nanovdb = platform_name != "AMD Accelerated Parallel Processing";
|
||||
|
||||
devices.push_back(info);
|
||||
num_devices++;
|
||||
}
|
||||
|
@@ -2036,7 +2036,9 @@ string OpenCLDevice::kernel_build_options(const string *debug_src)
|
||||
# endif
|
||||
|
||||
# ifdef WITH_NANOVDB
|
||||
build_options += "-DWITH_NANOVDB ";
|
||||
if (info.has_nanovdb) {
|
||||
build_options += "-DWITH_NANOVDB ";
|
||||
}
|
||||
# endif
|
||||
|
||||
return build_options;
|
||||
|
@@ -242,12 +242,15 @@ ccl_device float3 svm_math_blackbody_color(float t)
|
||||
return make_float3(4.70366907f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/* Manually align for readability. */
|
||||
/* clang-format off */
|
||||
int i = (t >= 6365.0f) ? 5 :
|
||||
(t >= 3315.0f) ? 4 :
|
||||
(t >= 1902.0f) ? 3 :
|
||||
(t >= 1449.0f) ? 2 :
|
||||
(t >= 1167.0f) ? 1 :
|
||||
0;
|
||||
/* clang-format on */
|
||||
|
||||
ccl_constant float *r = blackbody_table_r[i];
|
||||
ccl_constant float *g = blackbody_table_g[i];
|
||||
|
@@ -130,6 +130,14 @@ void Background::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
|
||||
|
||||
void Background::tag_update(Scene *scene)
|
||||
{
|
||||
Shader *bg_shader = get_shader(scene);
|
||||
if (bg_shader && bg_shader->is_modified()) {
|
||||
/* Tag as modified to update the KernelBackground visibility information.
|
||||
* We only tag the use_shader socket as modified as it is related to the shader
|
||||
* and to avoid doing unnecessary updates anywhere else. */
|
||||
tag_use_shader_modified();
|
||||
}
|
||||
|
||||
if (ao_factor_is_modified() || use_ao_is_modified()) {
|
||||
scene->integrator->tag_update(scene, Integrator::BACKGROUND_AO_MODIFIED);
|
||||
}
|
||||
|
@@ -1584,7 +1584,6 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
|
||||
dscene->tri_vnormal.tag_realloc();
|
||||
dscene->tri_vindex.tag_realloc();
|
||||
dscene->tri_patch.tag_realloc();
|
||||
dscene->tri_vnormal.tag_realloc();
|
||||
dscene->tri_patch_uv.tag_realloc();
|
||||
dscene->tri_shader.tag_realloc();
|
||||
dscene->patches.tag_realloc();
|
||||
|
@@ -303,7 +303,8 @@ ImageManager::ImageManager(const DeviceInfo &info)
|
||||
animation_frame = 0;
|
||||
|
||||
/* Set image limits */
|
||||
has_half_images = info.has_half_images;
|
||||
features.has_half_float = info.has_half_images;
|
||||
features.has_nanovdb = info.has_nanovdb;
|
||||
}
|
||||
|
||||
ImageManager::~ImageManager()
|
||||
@@ -347,7 +348,7 @@ void ImageManager::load_image_metadata(Image *img)
|
||||
metadata = ImageMetaData();
|
||||
metadata.colorspace = img->params.colorspace;
|
||||
|
||||
if (img->loader->load_metadata(metadata)) {
|
||||
if (img->loader->load_metadata(features, metadata)) {
|
||||
assert(metadata.type != IMAGE_DATA_NUM_TYPES);
|
||||
}
|
||||
else {
|
||||
@@ -356,15 +357,10 @@ void ImageManager::load_image_metadata(Image *img)
|
||||
|
||||
metadata.detect_colorspace();
|
||||
|
||||
/* No half textures on OpenCL, use full float instead. */
|
||||
if (!has_half_images) {
|
||||
if (metadata.type == IMAGE_DATA_TYPE_HALF4) {
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT4;
|
||||
}
|
||||
else if (metadata.type == IMAGE_DATA_TYPE_HALF) {
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT;
|
||||
}
|
||||
}
|
||||
assert(features.has_half_float ||
|
||||
(metadata.type != IMAGE_DATA_TYPE_HALF4 && metadata.type != IMAGE_DATA_TYPE_HALF));
|
||||
assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT ||
|
||||
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3));
|
||||
|
||||
img->need_metadata = false;
|
||||
}
|
||||
|
@@ -97,6 +97,13 @@ class ImageMetaData {
|
||||
void detect_colorspace();
|
||||
};
|
||||
|
||||
/* Information about supported features that Image loaders can use. */
|
||||
class ImageDeviceFeatures {
|
||||
public:
|
||||
bool has_half_float;
|
||||
bool has_nanovdb;
|
||||
};
|
||||
|
||||
/* Image loader base class, that can be subclassed to load image data
|
||||
* from custom sources (file, memory, procedurally generated, etc). */
|
||||
class ImageLoader {
|
||||
@@ -105,7 +112,7 @@ class ImageLoader {
|
||||
virtual ~ImageLoader(){};
|
||||
|
||||
/* Load metadata without actual image yet, should be fast. */
|
||||
virtual bool load_metadata(ImageMetaData &metadata) = 0;
|
||||
virtual bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) = 0;
|
||||
|
||||
/* Load actual image contents. */
|
||||
virtual bool load_pixels(const ImageMetaData &metadata,
|
||||
@@ -212,7 +219,8 @@ class ImageManager {
|
||||
|
||||
private:
|
||||
bool need_update_;
|
||||
bool has_half_images;
|
||||
|
||||
ImageDeviceFeatures features;
|
||||
|
||||
thread_mutex device_mutex;
|
||||
thread_mutex images_mutex;
|
||||
|
@@ -30,7 +30,7 @@ OIIOImageLoader::~OIIOImageLoader()
|
||||
{
|
||||
}
|
||||
|
||||
bool OIIOImageLoader::load_metadata(ImageMetaData &metadata)
|
||||
bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata)
|
||||
{
|
||||
/* Perform preliminary checks, with meaningful logging. */
|
||||
if (!path_exists(filepath.string())) {
|
||||
@@ -76,7 +76,7 @@ bool OIIOImageLoader::load_metadata(ImageMetaData &metadata)
|
||||
}
|
||||
|
||||
/* check if it's half float */
|
||||
if (spec.format == TypeDesc::HALF) {
|
||||
if (spec.format == TypeDesc::HALF && features.has_half_float) {
|
||||
is_half = true;
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ class OIIOImageLoader : public ImageLoader {
|
||||
OIIOImageLoader(const string &filepath);
|
||||
~OIIOImageLoader();
|
||||
|
||||
bool load_metadata(ImageMetaData &metadata) override;
|
||||
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
|
||||
|
||||
bool load_pixels(const ImageMetaData &metadata,
|
||||
void *pixels,
|
||||
|
@@ -40,7 +40,7 @@ SkyLoader::SkyLoader(float sun_elevation,
|
||||
|
||||
SkyLoader::~SkyLoader(){};
|
||||
|
||||
bool SkyLoader::load_metadata(ImageMetaData &metadata)
|
||||
bool SkyLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
|
||||
{
|
||||
metadata.width = 512;
|
||||
metadata.height = 128;
|
||||
|
@@ -34,7 +34,7 @@ class SkyLoader : public ImageLoader {
|
||||
float ozone_density);
|
||||
~SkyLoader();
|
||||
|
||||
bool load_metadata(ImageMetaData &metadata) override;
|
||||
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
|
||||
|
||||
bool load_pixels(const ImageMetaData &metadata,
|
||||
void *pixels,
|
||||
|
@@ -34,7 +34,7 @@ VDBImageLoader::~VDBImageLoader()
|
||||
{
|
||||
}
|
||||
|
||||
bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
|
||||
bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata)
|
||||
{
|
||||
#ifdef WITH_OPENVDB
|
||||
if (!grid) {
|
||||
@@ -56,55 +56,71 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
|
||||
if (grid->isType<openvdb::FloatGrid>()) {
|
||||
metadata.channels = 1;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3fGrid>()) {
|
||||
metadata.channels = 3;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::BoolGrid>()) {
|
||||
metadata.channels = 1;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid)));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid)));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::DoubleGrid>()) {
|
||||
metadata.channels = 1;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid)));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid)));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::Int32Grid>()) {
|
||||
metadata.channels = 1;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid)));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid)));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::Int64Grid>()) {
|
||||
metadata.channels = 1;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid)));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid)));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3IGrid>()) {
|
||||
metadata.channels = 3;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid)));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid)));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3dGrid>()) {
|
||||
metadata.channels = 3;
|
||||
# ifdef WITH_NANOVDB
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid)));
|
||||
if (features.has_nanovdb) {
|
||||
nanogrid = nanovdb::openToNanoVDB(
|
||||
openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid)));
|
||||
}
|
||||
# endif
|
||||
}
|
||||
else if (grid->isType<openvdb::MaskGrid>()) {
|
||||
@@ -118,21 +134,25 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
|
||||
}
|
||||
|
||||
# ifdef WITH_NANOVDB
|
||||
metadata.byte_size = nanogrid.size();
|
||||
if (metadata.channels == 1) {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
|
||||
}
|
||||
else {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3;
|
||||
}
|
||||
# else
|
||||
if (metadata.channels == 1) {
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT;
|
||||
}
|
||||
else {
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT4;
|
||||
if (nanogrid) {
|
||||
metadata.byte_size = nanogrid.size();
|
||||
if (metadata.channels == 1) {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
|
||||
}
|
||||
else {
|
||||
metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3;
|
||||
}
|
||||
}
|
||||
else
|
||||
# endif
|
||||
{
|
||||
if (metadata.channels == 1) {
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT;
|
||||
}
|
||||
else {
|
||||
metadata.type = IMAGE_DATA_TYPE_FLOAT4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set transform from object space to voxel index. */
|
||||
openvdb::math::Mat4f grid_matrix = grid->transform().baseMap()->getAffineMap()->getMat4();
|
||||
@@ -143,13 +163,18 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
|
||||
}
|
||||
}
|
||||
|
||||
Transform texture_to_index;
|
||||
# ifdef WITH_NANOVDB
|
||||
Transform texture_to_index = transform_identity();
|
||||
# else
|
||||
openvdb::Coord min = bbox.min();
|
||||
Transform texture_to_index = transform_translate(min.x(), min.y(), min.z()) *
|
||||
transform_scale(dim.x(), dim.y(), dim.z());
|
||||
if (nanogrid) {
|
||||
texture_to_index = transform_identity();
|
||||
}
|
||||
else
|
||||
# endif
|
||||
{
|
||||
openvdb::Coord min = bbox.min();
|
||||
texture_to_index = transform_translate(min.x(), min.y(), min.z()) *
|
||||
transform_scale(dim.x(), dim.y(), dim.z());
|
||||
}
|
||||
|
||||
metadata.transform_3d = transform_inverse(index_to_object * texture_to_index);
|
||||
metadata.use_transform_3d = true;
|
||||
@@ -165,48 +190,52 @@ bool VDBImageLoader::load_pixels(const ImageMetaData &, void *pixels, const size
|
||||
{
|
||||
#ifdef WITH_OPENVDB
|
||||
# ifdef WITH_NANOVDB
|
||||
memcpy(pixels, nanogrid.data(), nanogrid.size());
|
||||
# else
|
||||
if (grid->isType<openvdb::FloatGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3fGrid>()) {
|
||||
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
|
||||
bbox, (openvdb::Vec3f *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::BoolGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::DoubleGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Int32Grid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Int64Grid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3IGrid>()) {
|
||||
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
|
||||
bbox, (openvdb::Vec3f *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3dGrid>()) {
|
||||
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
|
||||
bbox, (openvdb::Vec3f *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::MaskGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense);
|
||||
if (nanogrid) {
|
||||
memcpy(pixels, nanogrid.data(), nanogrid.size());
|
||||
}
|
||||
else
|
||||
# endif
|
||||
{
|
||||
if (grid->isType<openvdb::FloatGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3fGrid>()) {
|
||||
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
|
||||
bbox, (openvdb::Vec3f *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::BoolGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::DoubleGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Int32Grid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Int64Grid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3IGrid>()) {
|
||||
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
|
||||
bbox, (openvdb::Vec3f *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::Vec3dGrid>()) {
|
||||
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
|
||||
bbox, (openvdb::Vec3f *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense);
|
||||
}
|
||||
else if (grid->isType<openvdb::MaskGrid>()) {
|
||||
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
|
||||
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
(void)pixels;
|
||||
|
@@ -33,7 +33,8 @@ class VDBImageLoader : public ImageLoader {
|
||||
VDBImageLoader(const string &grid_name);
|
||||
~VDBImageLoader();
|
||||
|
||||
virtual bool load_metadata(ImageMetaData &metadata) override;
|
||||
virtual bool load_metadata(const ImageDeviceFeatures &features,
|
||||
ImageMetaData &metadata) override;
|
||||
|
||||
virtual bool load_pixels(const ImageMetaData &metadata,
|
||||
void *pixels,
|
||||
|
@@ -153,10 +153,6 @@ void Object::update_motion()
|
||||
|
||||
void Object::compute_bounds(bool motion_blur)
|
||||
{
|
||||
if (!is_modified() && !geometry->is_modified()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BoundBox mbounds = geometry->bounds;
|
||||
|
||||
if (motion_blur && use_motion()) {
|
||||
|
@@ -24,7 +24,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
/**
|
||||
* \code{.py}
|
||||
* import bpy
|
||||
* import textwrap
|
||||
*
|
||||
@@ -42,6 +43,7 @@
|
||||
* print("%d,%d," % (w, h))
|
||||
* text = ", ".join(["0x%x" % p for p in pixels])
|
||||
* print(textwrap.fill(text, width=120), end=",\n")
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@@ -533,11 +533,18 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
|
||||
wp.showCmd = SW_SHOWMAXIMIZED;
|
||||
wp.ptMaxPosition.x = 0;
|
||||
wp.ptMaxPosition.y = 0;
|
||||
style &= ~WS_CAPTION;
|
||||
style &= ~(WS_CAPTION | WS_MAXIMIZE);
|
||||
break;
|
||||
case GHOST_kWindowStateNormal:
|
||||
default:
|
||||
wp.showCmd = SW_SHOWNORMAL;
|
||||
if (curstate == GHOST_kWindowStateFullScreen &&
|
||||
m_normal_state == GHOST_kWindowStateMaximized) {
|
||||
wp.showCmd = SW_SHOWMAXIMIZED;
|
||||
m_normal_state = GHOST_kWindowStateNormal;
|
||||
}
|
||||
else {
|
||||
wp.showCmd = SW_SHOWNORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
::SetWindowLongPtr(m_hWnd, GWL_STYLE, style);
|
||||
|
Submodule release/scripts/addons updated: bcd08a9506...63492d3d03
@@ -349,6 +349,10 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
||||
# 1) try import
|
||||
try:
|
||||
mod = __import__(module_name)
|
||||
if mod.__file__ is None:
|
||||
# This can happen when the addon has been removed but there are
|
||||
# residual `.pyc` files left behind.
|
||||
raise ImportError(name=module_name)
|
||||
mod.__time__ = os.path.getmtime(mod.__file__)
|
||||
mod.__addon_enabled__ = False
|
||||
except Exception as ex:
|
||||
|
@@ -32,12 +32,6 @@ import bpy
|
||||
IS_TESTING = False
|
||||
|
||||
|
||||
def drepr(string):
|
||||
# is there a less crappy way to do this in python?, re.escape also escapes
|
||||
# single quotes strings so can't use it.
|
||||
return '"%s"' % repr(string)[1:-1].replace("\"", "\\\"").replace("\\'", "'")
|
||||
|
||||
|
||||
def classes_recursive(base_type, clss=None):
|
||||
if clss is None:
|
||||
clss = [base_type]
|
||||
@@ -66,7 +60,7 @@ class DataPathBuilder:
|
||||
if type(key) is int:
|
||||
str_value = '[%d]' % key
|
||||
elif type(key) is str:
|
||||
str_value = '[%s]' % drepr(key)
|
||||
str_value = '["%s"]' % bpy.utils.escape_identifier(key)
|
||||
else:
|
||||
raise Exception("unsupported accessor %r of type %r (internal error)" % (key, type(key)))
|
||||
return DataPathBuilder(self.data_path + (str_value, ))
|
||||
|
0
release/scripts/modules/bl_rna_utils/__init__.py
Normal file
0
release/scripts/modules/bl_rna_utils/__init__.py
Normal file
91
release/scripts/modules/bl_rna_utils/data_path.py
Normal file
91
release/scripts/modules/bl_rna_utils/data_path.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
__all__ = (
|
||||
"property_definition_from_data_path",
|
||||
"decompose_data_path",
|
||||
)
|
||||
|
||||
class _TokenizeDataPath:
|
||||
"""
|
||||
Class to split up tokens of a data-path.
|
||||
|
||||
Note that almost all access generates new objects with additional paths,
|
||||
with the exception of iteration which is the intended way to access the resulting data."""
|
||||
__slots__ = (
|
||||
"data_path",
|
||||
)
|
||||
|
||||
def __init__(self, attrs):
|
||||
self.data_path = attrs
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return _TokenizeDataPath(self.data_path + ((".%s" % attr),))
|
||||
|
||||
def __getitem__(self, key):
|
||||
return _TokenizeDataPath(self.data_path + (("[%r]" % (key,)),))
|
||||
|
||||
def __call__(self, *args, **kw):
|
||||
value_str = ", ".join([
|
||||
val for val in (
|
||||
", ".join(repr(value) for value in args),
|
||||
", ".join(["%s=%r" % (key, value) for key, value in kw.items()]),
|
||||
) if val])
|
||||
return _TokenizeDataPath(self.data_path + ('(%s)' % value_str, ))
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.data_path)
|
||||
|
||||
|
||||
def decompose_data_path(data_path):
|
||||
"""
|
||||
Return the components of a data path split into a list.
|
||||
"""
|
||||
ns = {"base": _TokenizeDataPath(())}
|
||||
return list(eval("base" + data_path, ns, ns))
|
||||
|
||||
|
||||
def property_definition_from_data_path(base, data_path):
|
||||
"""
|
||||
Return an RNA property definition from an object and a data path.
|
||||
|
||||
In Blender this is often used with ``context`` as the base and a
|
||||
path that it references, for example ``.space_data.lock_camera``.
|
||||
"""
|
||||
data = decompose_data_path(data_path)
|
||||
while data and (not data[-1].startswith(".")):
|
||||
data.pop()
|
||||
|
||||
if (not data) or (not data[-1].startswith(".")) or (len(data) < 2):
|
||||
return None
|
||||
|
||||
data_path_head = "".join(data[:-1])
|
||||
data_path_tail = data[-1]
|
||||
|
||||
value_head = eval("base" + data_path_head)
|
||||
value_head_rna = getattr(value_head, "bl_rna", None)
|
||||
if value_head_rna is None:
|
||||
return None
|
||||
|
||||
value_tail = value_head.bl_rna.properties.get(data_path_tail[1:])
|
||||
if not value_tail:
|
||||
return None
|
||||
|
||||
return value_tail
|
@@ -1694,6 +1694,7 @@ def km_image(params):
|
||||
("image.view_all", {"type": 'HOME', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("fit_view", True)]}),
|
||||
("image.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
|
||||
("image.view_cursor_center", {"type": 'C', "value": 'PRESS', "shift": True}, None),
|
||||
("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
|
||||
("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
|
||||
("image.view_pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
|
||||
@@ -1830,6 +1831,7 @@ def km_node_editor(params):
|
||||
])
|
||||
|
||||
items.extend([
|
||||
("node.follow_portal", {"type": 'TAB', "value": 'PRESS'}, None),
|
||||
("node.select_box", {"type": params.select_tweak, "value": 'ANY'},
|
||||
{"properties": [("tweak", True)]}),
|
||||
("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True, "alt": True},
|
||||
|
@@ -355,7 +355,8 @@ class UpdateAnimatedTransformConstraint(Operator):
|
||||
|
||||
use_convert_to_radians: BoolProperty(
|
||||
name="Convert to Radians",
|
||||
description="Convert fcurves/drivers affecting rotations to radians (Warning: use this only once!)",
|
||||
description="Convert f-curves/drivers affecting rotations to radians.\n"
|
||||
"Warning: Use this only once",
|
||||
default=True,
|
||||
)
|
||||
|
||||
@@ -430,22 +431,9 @@ class UpdateAnimatedTransformConstraint(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class ANIM_OT_show_group_colors_deprecated(Operator):
|
||||
"""This option moved to Preferences > Animation"""
|
||||
|
||||
bl_idname = "anim.show_group_colors_deprecated"
|
||||
bl_label = "Show Group Colors"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, _context):
|
||||
return False
|
||||
|
||||
|
||||
classes = (
|
||||
ANIM_OT_keying_set_export,
|
||||
NLA_OT_bake,
|
||||
ClearUselessActions,
|
||||
UpdateAnimatedTransformConstraint,
|
||||
ANIM_OT_show_group_colors_deprecated,
|
||||
)
|
||||
|
@@ -21,6 +21,7 @@ from __future__ import annotations
|
||||
|
||||
import bpy
|
||||
import nodeitems_utils
|
||||
from mathutils import Vector
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
PropertyGroup,
|
||||
@@ -303,6 +304,187 @@ class NODE_OT_tree_path_parent(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class NODE_OT_follow_portal(Operator):
|
||||
'''Follow portal'''
|
||||
bl_idname = "node.follow_portal"
|
||||
bl_label = "Follow Portal"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
return (space.type == 'NODE_EDITOR'
|
||||
and space.node_tree
|
||||
and space.node_tree.nodes.active.bl_idname in ('NodePortalIn', 'NodePortalOut'))
|
||||
|
||||
def execute(self, context):
|
||||
space = context.space_data
|
||||
ntree = space.node_tree
|
||||
old_active_node = ntree.nodes.active
|
||||
|
||||
portal_id = old_active_node.portal_id
|
||||
if old_active_node.bl_idname == 'NodePortalIn':
|
||||
out_nodes = [n for n in ntree.nodes if n.bl_idname == 'NodePortalOut' and n.portal_id == portal_id]
|
||||
if len(out_nodes) != 1:
|
||||
return {'CANCELLED'}
|
||||
out_node = out_nodes[0]
|
||||
for node in ntree.nodes:
|
||||
node.select = False
|
||||
out_node.select = True
|
||||
ntree.nodes.active = out_node
|
||||
if old_active_node.bl_idname == 'NodePortalOut':
|
||||
in_nodes = [n for n in ntree.nodes if n.bl_idname == 'NodePortalIn' and n.portal_id == portal_id]
|
||||
if len(in_nodes) != 1:
|
||||
return {'CANCELLED'}
|
||||
in_node = in_nodes[0]
|
||||
for node in ntree.nodes:
|
||||
node.select = False
|
||||
in_node.select = True
|
||||
ntree.nodes.active = in_node
|
||||
|
||||
bpy.ops.node.view_selected()
|
||||
return {'FINISHED'}
|
||||
|
||||
PAGE_SIZE = 20000
|
||||
|
||||
def get_page_center(page):
|
||||
return Vector(((page - 1) * PAGE_SIZE, 0))
|
||||
|
||||
def position_is_on_page(pos, page):
|
||||
page_center = get_page_center(page)
|
||||
return (abs(page_center.x - pos[0]) < PAGE_SIZE / 2
|
||||
and abs(page_center.y - pos[1]) < PAGE_SIZE / 2)
|
||||
|
||||
def node_is_on_page(node, page):
|
||||
return position_is_on_page(node.location, page)
|
||||
|
||||
def get_nodes_on_page(ntree, page):
|
||||
return [n for n in ntree.nodes if node_is_on_page(n, page)]
|
||||
|
||||
def get_current_page(context):
|
||||
for region in context.area.regions:
|
||||
if region.type != 'WINDOW':
|
||||
continue
|
||||
pos = region.view2d.region_to_view(0, 0)
|
||||
for page in range(10):
|
||||
if position_is_on_page(pos, page):
|
||||
return page
|
||||
return None
|
||||
|
||||
class NODE_OT_view_page(Operator):
|
||||
'''View page'''
|
||||
bl_idname = "node.view_page"
|
||||
bl_label = "View Page"
|
||||
|
||||
page: bpy.props.IntProperty(default=0)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
return space.type == 'NODE_EDITOR'
|
||||
|
||||
def invoke(self, context, event):
|
||||
if event.ctrl:
|
||||
bpy.ops.node.move_to_page('INVOKE_DEFAULT', page=self.page)
|
||||
return {'FINISHED'}
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
space = context.space_data
|
||||
ntree = space.node_tree
|
||||
for node in ntree.nodes:
|
||||
node.select = False
|
||||
|
||||
nodes_on_page = get_nodes_on_page(ntree, self.page)
|
||||
if len(nodes_on_page) > 0:
|
||||
for node in nodes_on_page:
|
||||
node.select = True
|
||||
bpy.ops.node.view_selected()
|
||||
for node in nodes_on_page:
|
||||
node.select = False
|
||||
ntree.nodes.active = None
|
||||
else:
|
||||
page_center = get_page_center(self.page)
|
||||
new_node = ntree.nodes.new('NodeReroute')
|
||||
new_node.select = True
|
||||
new_node.location = page_center
|
||||
|
||||
context_copy = context.copy()
|
||||
def update_after_draw():
|
||||
bpy.ops.node.view_selected(context_copy)
|
||||
ntree.nodes.remove(new_node)
|
||||
bpy.app.timers.register(update_after_draw, first_interval=0.01)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class NODE_OT_move_to_page(Operator):
|
||||
'''Move to page'''
|
||||
bl_idname = "node.move_to_page"
|
||||
bl_label = "Move to Page"
|
||||
|
||||
page: IntProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
return space.type == 'NODE_EDITOR'
|
||||
|
||||
def execute(self, context):
|
||||
ntree = context.space_data.node_tree
|
||||
nodes_to_move = [n for n in ntree.nodes if n.select]
|
||||
if len(nodes_to_move) == 0:
|
||||
return {'CANCELLED'}
|
||||
|
||||
old_center = sum((n.location for n in nodes_to_move), Vector((0, 0))) / len(nodes_to_move)
|
||||
new_center = get_page_center(self.page)
|
||||
offset = new_center - old_center
|
||||
|
||||
view2d = context.region.view2d
|
||||
old_center_region = Vector(view2d.view_to_region(old_center.x, old_center.y, clip=False))
|
||||
new_center_region = Vector(view2d.view_to_region(new_center.x, new_center.y, clip=False))
|
||||
offset_region = new_center_region - old_center_region
|
||||
|
||||
for node in nodes_to_move:
|
||||
node.location += offset
|
||||
|
||||
bpy.ops.view2d.pan(deltax=offset_region.x, deltay=offset_region.y)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class NODE_OT_add_portal(Operator):
|
||||
'''Add portal'''
|
||||
bl_idname = "node.add_portal"
|
||||
bl_label = "Add Portal"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.space_data.type != 'NODE_EDITOR':
|
||||
return False
|
||||
ntree = context.space_data.node_tree
|
||||
if ntree is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
ntree = context.space_data.node_tree
|
||||
bpy.ops.node.add_and_link_node(type="NodePortalIn")
|
||||
portal_in = ntree.nodes[-1]
|
||||
bpy.ops.node.add_and_link_node(type="NodePortalOut")
|
||||
portal_out = ntree.nodes[-1]
|
||||
|
||||
portal_in.location.x -= 200
|
||||
portal_out.location.x += 60
|
||||
|
||||
for node in ntree.nodes:
|
||||
node.select = False
|
||||
|
||||
portal_in.select = True
|
||||
portal_out.select = True
|
||||
|
||||
portal_out.portal_id = portal_in.portal_id
|
||||
|
||||
bpy.ops.node.translate_attach("INVOKE_DEFAULT")
|
||||
return {'FINISHED'}
|
||||
|
||||
classes = (
|
||||
NodeSetting,
|
||||
|
||||
@@ -311,4 +493,8 @@ classes = (
|
||||
NODE_OT_add_search,
|
||||
NODE_OT_collapse_hide_unused_toggle,
|
||||
NODE_OT_tree_path_parent,
|
||||
NODE_OT_follow_portal,
|
||||
NODE_OT_view_page,
|
||||
NODE_OT_add_portal,
|
||||
NODE_OT_move_to_page,
|
||||
)
|
||||
|
@@ -95,6 +95,76 @@ def context_path_validate(context, data_path):
|
||||
return value
|
||||
|
||||
|
||||
def context_path_to_rna_property(context, data_path):
|
||||
from bl_rna_utils.data_path import property_definition_from_data_path
|
||||
rna_prop = property_definition_from_data_path(context, "." + data_path)
|
||||
if rna_prop is not None:
|
||||
return rna_prop
|
||||
return None
|
||||
|
||||
|
||||
def context_path_decompose(data_path):
|
||||
# Decompose a data_path into 3 components:
|
||||
# base_path, prop_attr, prop_item, where:
|
||||
# `"foo.bar["baz"].fiz().bob.buz[10][2]"`, returns...
|
||||
# `("foo.bar["baz"].fiz().bob", "buz", "[10][2]")`
|
||||
#
|
||||
# This is useful as we often want the base and the property, ignoring any item access.
|
||||
# Note that item access includes function calls since these aren't properties.
|
||||
#
|
||||
# Note that the `.` is removed from the start of the first and second values,
|
||||
# this is done because `.attr` isn't convenient to use as an argument,
|
||||
# also the convention is not to include this within the data paths or the operator logic for `bpy.ops.wm.*`.
|
||||
from bl_rna_utils.data_path import decompose_data_path
|
||||
path_split = decompose_data_path("." + data_path)
|
||||
|
||||
# Find the last property that isn't a function call.
|
||||
value_prev = ""
|
||||
i = len(path_split)
|
||||
while (i := i - 1) >= 0:
|
||||
value = path_split[i]
|
||||
if value.startswith("."):
|
||||
if not value_prev.startswith("("):
|
||||
break
|
||||
value_prev = value
|
||||
|
||||
if i != -1:
|
||||
base_path = "".join(path_split[:i])
|
||||
prop_attr = path_split[i]
|
||||
prop_item = "".join(path_split[i + 1:])
|
||||
|
||||
if base_path:
|
||||
assert(base_path.startswith("."))
|
||||
base_path= base_path[1:]
|
||||
if prop_attr:
|
||||
assert(prop_attr.startswith("."))
|
||||
prop_attr = prop_attr[1:]
|
||||
else:
|
||||
# If there are no properties, everything is an item.
|
||||
# Note that should not happen in practice with values which are added onto `context`,
|
||||
# include since it's correct to account for this case and not doing so will create a confusing exception.
|
||||
base_path = ""
|
||||
prop_attr = ""
|
||||
prop_item = "".join(path_split)
|
||||
|
||||
return (base_path, prop_attr, prop_item)
|
||||
|
||||
|
||||
def description_from_data_path(base, data_path, *, prefix, value=Ellipsis):
|
||||
if context_path_validate(base, data_path) is Ellipsis:
|
||||
return None
|
||||
|
||||
if (
|
||||
(rna_prop := context_path_to_rna_property(base, data_path)) and
|
||||
(description := rna_prop.description)
|
||||
):
|
||||
description = "%s: %s" % (prefix, description)
|
||||
if value != Ellipsis:
|
||||
description = "%s\n%s: %s" % (description, iface_("Value"), str(value))
|
||||
return description
|
||||
return None
|
||||
|
||||
|
||||
def operator_value_is_undo(value):
|
||||
if value in {None, Ellipsis}:
|
||||
return False
|
||||
@@ -120,12 +190,9 @@ def operator_value_is_undo(value):
|
||||
|
||||
|
||||
def operator_path_is_undo(context, data_path):
|
||||
# note that if we have data paths that use strings this could fail
|
||||
# luckily we don't do this!
|
||||
#
|
||||
# When we can't find the data owner assume no undo is needed.
|
||||
data_path_head = data_path.rpartition(".")[0]
|
||||
data_path_head, _, _ = context_path_decompose(data_path)
|
||||
|
||||
# When we can't find the data owner assume no undo is needed.
|
||||
if not data_path_head:
|
||||
return False
|
||||
|
||||
@@ -168,6 +235,10 @@ class WM_OT_context_set_boolean(Operator):
|
||||
default=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
||||
|
||||
execute = execute_context_assign
|
||||
|
||||
|
||||
@@ -185,6 +256,10 @@ class WM_OT_context_set_int(Operator): # same as enum
|
||||
)
|
||||
relative: rna_relative_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
|
||||
|
||||
execute = execute_context_assign
|
||||
|
||||
|
||||
@@ -201,6 +276,10 @@ class WM_OT_context_scale_float(Operator):
|
||||
default=1.0,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
if context_path_validate(context, data_path) is Ellipsis:
|
||||
@@ -235,6 +314,10 @@ class WM_OT_context_scale_int(Operator):
|
||||
options={'SKIP_SAVE'},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
if context_path_validate(context, data_path) is Ellipsis:
|
||||
@@ -274,6 +357,10 @@ class WM_OT_context_set_float(Operator): # same as enum
|
||||
)
|
||||
relative: rna_relative_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
|
||||
|
||||
execute = execute_context_assign
|
||||
|
||||
|
||||
@@ -290,6 +377,10 @@ class WM_OT_context_set_string(Operator): # same as enum
|
||||
maxlen=1024,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
||||
|
||||
execute = execute_context_assign
|
||||
|
||||
|
||||
@@ -306,6 +397,10 @@ class WM_OT_context_set_enum(Operator):
|
||||
maxlen=1024,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
||||
|
||||
execute = execute_context_assign
|
||||
|
||||
|
||||
@@ -322,6 +417,10 @@ class WM_OT_context_set_value(Operator):
|
||||
maxlen=1024,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
if context_path_validate(context, data_path) is Ellipsis:
|
||||
@@ -339,6 +438,13 @@ class WM_OT_context_toggle(Operator):
|
||||
data_path: rna_path_prop
|
||||
module: rna_module_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
# Currently unsupported, it might be possible to extract this.
|
||||
if props.module:
|
||||
return None
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"))
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
|
||||
@@ -375,6 +481,11 @@ class WM_OT_context_toggle_enum(Operator):
|
||||
maxlen=1024,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
value = "(%r, %r)" % (props.value_1, props.value_2)
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"), value=value)
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
|
||||
@@ -406,6 +517,10 @@ class WM_OT_context_cycle_int(Operator):
|
||||
reverse: rna_reverse_prop
|
||||
wrap: rna_wrap_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
value = context_path_validate(context, data_path)
|
||||
@@ -442,6 +557,10 @@ class WM_OT_context_cycle_enum(Operator):
|
||||
reverse: rna_reverse_prop
|
||||
wrap: rna_wrap_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
value = context_path_validate(context, data_path)
|
||||
@@ -450,22 +569,11 @@ class WM_OT_context_cycle_enum(Operator):
|
||||
|
||||
orig_value = value
|
||||
|
||||
# Have to get rna enum values
|
||||
rna_struct_str, rna_prop_str = data_path.rsplit('.', 1)
|
||||
i = rna_prop_str.find('[')
|
||||
|
||||
# just in case we get "context.foo.bar[0]"
|
||||
if i != -1:
|
||||
rna_prop_str = rna_prop_str[0:i]
|
||||
|
||||
rna_struct = eval("context.%s.rna_type" % rna_struct_str)
|
||||
|
||||
rna_prop = rna_struct.properties[rna_prop_str]
|
||||
|
||||
rna_prop = context_path_to_rna_property(context, data_path)
|
||||
if type(rna_prop) != bpy.types.EnumProperty:
|
||||
raise Exception("expected an enum property")
|
||||
|
||||
enums = rna_struct.properties[rna_prop_str].enum_items.keys()
|
||||
enums = rna_prop.enum_items.keys()
|
||||
orig_index = enums.index(orig_value)
|
||||
|
||||
# Have the info we need, advance to the next item.
|
||||
@@ -498,6 +606,10 @@ class WM_OT_context_cycle_array(Operator):
|
||||
data_path: rna_path_prop
|
||||
reverse: rna_reverse_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
value = context_path_validate(context, data_path)
|
||||
@@ -523,6 +635,10 @@ class WM_OT_context_menu_enum(Operator):
|
||||
|
||||
data_path: rna_path_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Menu"))
|
||||
|
||||
def execute(self, context):
|
||||
data_path = self.data_path
|
||||
value = context_path_validate(context, data_path)
|
||||
@@ -530,15 +646,15 @@ class WM_OT_context_menu_enum(Operator):
|
||||
if value is Ellipsis:
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
base_path, prop_string = data_path.rsplit(".", 1)
|
||||
base_path, prop_attr, _ = context_path_decompose(data_path)
|
||||
value_base = context_path_validate(context, base_path)
|
||||
prop = value_base.bl_rna.properties[prop_string]
|
||||
rna_prop = context_path_to_rna_property(context, data_path)
|
||||
|
||||
def draw_cb(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(value_base, prop_string, expand=True)
|
||||
layout.prop(value_base, prop_attr, expand=True)
|
||||
|
||||
context.window_manager.popup_menu(draw_func=draw_cb, title=prop.name, icon=prop.icon)
|
||||
context.window_manager.popup_menu(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -550,6 +666,10 @@ class WM_OT_context_pie_enum(Operator):
|
||||
|
||||
data_path: rna_path_prop
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
data_path = self.data_path
|
||||
@@ -558,15 +678,15 @@ class WM_OT_context_pie_enum(Operator):
|
||||
if value is Ellipsis:
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
base_path, prop_string = data_path.rsplit(".", 1)
|
||||
base_path, prop_attr, _ = context_path_decompose(data_path)
|
||||
value_base = context_path_validate(context, base_path)
|
||||
prop = value_base.bl_rna.properties[prop_string]
|
||||
rna_prop = context_path_to_rna_property(context, data_path)
|
||||
|
||||
def draw_cb(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(value_base, prop_string, expand=True)
|
||||
layout.prop(value_base, prop_attr, expand=True)
|
||||
|
||||
wm.popup_menu_pie(draw_func=draw_cb, title=prop.name, icon=prop.icon, event=event)
|
||||
wm.popup_menu_pie(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon, event=event)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -587,11 +707,15 @@ class WM_OT_operator_pie_enum(Operator):
|
||||
maxlen=1024,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, props):
|
||||
return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
|
||||
data_path = self.data_path
|
||||
prop_string = self.prop_string
|
||||
prop_attr = self.prop_string
|
||||
|
||||
# same as eval("bpy.ops." + data_path)
|
||||
op_mod_str, ob_id_str = data_path.split(".", 1)
|
||||
@@ -607,7 +731,7 @@ class WM_OT_operator_pie_enum(Operator):
|
||||
def draw_cb(self, context):
|
||||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
pie.operator_enum(data_path, prop_string)
|
||||
pie.operator_enum(data_path, prop_attr)
|
||||
|
||||
wm.popup_menu_pie(draw_func=draw_cb, title=op_rna.name, event=event)
|
||||
|
||||
@@ -631,17 +755,17 @@ class WM_OT_context_set_id(Operator):
|
||||
value = self.value
|
||||
data_path = self.data_path
|
||||
|
||||
# match the pointer type from the target property to bpy.data.*
|
||||
# Match the pointer type from the target property to `bpy.data.*`
|
||||
# so we lookup the correct list.
|
||||
data_path_base, data_path_prop = data_path.rsplit(".", 1)
|
||||
data_prop_rna = eval("context.%s" % data_path_base).rna_type.properties[data_path_prop]
|
||||
data_prop_rna_type = data_prop_rna.fixed_type
|
||||
|
||||
rna_prop = context_path_to_rna_property(context, data_path)
|
||||
rna_prop_fixed_type = rna_prop.fixed_type
|
||||
|
||||
id_iter = None
|
||||
|
||||
for prop in bpy.data.rna_type.properties:
|
||||
if prop.rna_type.identifier == "CollectionProperty":
|
||||
if prop.fixed_type == data_prop_rna_type:
|
||||
if prop.fixed_type == rna_prop_fixed_type:
|
||||
id_iter = prop.identifier
|
||||
break
|
||||
|
||||
|
@@ -77,6 +77,7 @@ class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
|
||||
|
||||
class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
|
||||
bl_label = "Line Art"
|
||||
bl_order = 10
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
@@ -86,12 +86,19 @@ class DATA_PT_display(ArmatureButtonsPanel, Panel):
|
||||
|
||||
col = layout.column(heading="Show")
|
||||
col.prop(arm, "show_names", text="Names")
|
||||
col.prop(arm, "show_axes", text="Axes")
|
||||
col.prop(arm, "show_bone_custom_shapes", text="Shapes")
|
||||
col.prop(arm, "show_group_colors", text="Group Colors")
|
||||
|
||||
if ob:
|
||||
col.prop(ob, "show_in_front", text="In Front")
|
||||
|
||||
col = layout.column(align=False, heading="Axes")
|
||||
row = col.row(align=True)
|
||||
row.prop(arm, "show_axes", text="")
|
||||
sub = row.row(align=True)
|
||||
sub.active = arm.show_axes
|
||||
sub.prop(arm, "axes_position", text="Position")
|
||||
|
||||
|
||||
class DATA_MT_bone_group_context_menu(Menu):
|
||||
bl_label = "Bone Group Specials"
|
||||
|
@@ -277,6 +277,7 @@ class MATERIAL_PT_viewport(MaterialButtonsPanel, Panel):
|
||||
class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
|
||||
bl_label = "Line Art"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_order = 10
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@@ -311,6 +311,7 @@ class OBJECT_PT_instancing_size(ObjectButtonsPanel, Panel):
|
||||
class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
|
||||
bl_label = "Line Art"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_order = 10
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@@ -74,10 +74,6 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'LEFT'
|
||||
row.label(text="Enable physics for:")
|
||||
|
||||
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
|
||||
|
||||
obj = context.object
|
||||
|
@@ -348,8 +348,6 @@ class DOPESHEET_MT_view(Menu):
|
||||
col.active = context.space_data.mode != 'SHAPEKEY'
|
||||
col.prop(st, "show_sliders")
|
||||
|
||||
if bpy.app.version < (2, 93):
|
||||
layout.operator("anim.show_group_colors_deprecated", icon='CHECKBOX_HLT')
|
||||
layout.prop(st, "show_interpolation")
|
||||
layout.prop(st, "show_extremes")
|
||||
layout.prop(st, "use_auto_merge_keyframes")
|
||||
|
@@ -18,7 +18,6 @@
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
from bpy.types import Header, Menu, Panel
|
||||
from bl_ui.space_dopesheet import (
|
||||
DopesheetFilterPopoverBase,
|
||||
@@ -120,10 +119,6 @@ class GRAPH_MT_view(Menu):
|
||||
layout.prop(st, "use_realtime_update")
|
||||
layout.prop(st, "show_cursor")
|
||||
layout.prop(st, "show_sliders")
|
||||
|
||||
if bpy.app.version < (2, 93):
|
||||
layout.operator("anim.show_group_colors_deprecated", icon='CHECKBOX_HLT')
|
||||
|
||||
layout.prop(st, "use_auto_merge_keyframes")
|
||||
|
||||
if st.mode != 'DRIVERS':
|
||||
|
@@ -171,6 +171,16 @@ class NODE_HT_header(Header):
|
||||
else:
|
||||
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
|
||||
|
||||
import bl_operators.node as node_module
|
||||
current_page = node_module.get_current_page(context)
|
||||
|
||||
row = layout.row(align=True)
|
||||
for page in range(1, 6):
|
||||
subrow = row.row(align=True)
|
||||
subrow.enabled = page != current_page
|
||||
props = subrow.operator("node.view_page", text=str(page))
|
||||
props.page = page
|
||||
|
||||
else:
|
||||
# Custom node tree is edited as independent ID block
|
||||
NODE_MT_editor_menus.draw_collapsible(context, layout)
|
||||
@@ -238,6 +248,8 @@ class NODE_MT_add(bpy.types.Menu):
|
||||
# actual node submenus are defined by draw functions from node categories
|
||||
nodeitems_utils.draw_node_categories_menu(self, context)
|
||||
|
||||
layout.operator("node.add_portal")
|
||||
|
||||
|
||||
class NODE_MT_view(Menu):
|
||||
bl_label = "View"
|
||||
@@ -481,7 +493,7 @@ class NODE_MT_context_menu(Menu):
|
||||
class NODE_PT_active_node_generic(Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Item"
|
||||
bl_category = "Node"
|
||||
bl_label = "Node"
|
||||
|
||||
@classmethod
|
||||
@@ -499,7 +511,7 @@ class NODE_PT_active_node_generic(Panel):
|
||||
class NODE_PT_active_node_color(Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Item"
|
||||
bl_category = "Node"
|
||||
bl_label = "Color"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_parent_id = 'NODE_PT_active_node_generic'
|
||||
@@ -529,7 +541,7 @@ class NODE_PT_active_node_color(Panel):
|
||||
class NODE_PT_active_node_properties(Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Item"
|
||||
bl_category = "Node"
|
||||
bl_label = "Properties"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@@ -570,7 +582,7 @@ class NODE_PT_active_node_properties(Panel):
|
||||
class NODE_PT_texture_mapping(Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Item"
|
||||
bl_category = "Node"
|
||||
bl_label = "Texture Mapping"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
@@ -2244,6 +2244,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
|
||||
({"property": "use_switch_object_operator"}, "T80402"),
|
||||
({"property": "use_sculpt_tools_tilt"}, "T82877"),
|
||||
({"property": "use_asset_browser"}, ("project/profile/124/", "Milestone 1")),
|
||||
({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
|
||||
),
|
||||
)
|
||||
|
||||
|
@@ -622,9 +622,15 @@ class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
settings = cls.paint_settings(context)
|
||||
return (settings and settings.brush and
|
||||
(context.sculpt_object or context.image_paint_object or context.vertex_paint_object))
|
||||
if (
|
||||
(settings := cls.paint_settings(context)) and
|
||||
(brush := settings.brush)
|
||||
):
|
||||
if context.sculpt_object or context.vertex_paint_object:
|
||||
return True
|
||||
elif context.image_paint_object:
|
||||
return (brush.image_tool == 'DRAW')
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
@@ -368,6 +368,7 @@ compositor_node_categories = [
|
||||
NodeItem("CompositorNodePixelate"),
|
||||
NodeItem("CompositorNodeSunBeams"),
|
||||
NodeItem("CompositorNodeDenoise"),
|
||||
NodeItem("CompositorNodeAntiAliasing"),
|
||||
]),
|
||||
CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[
|
||||
NodeItem("CompositorNodeNormal"),
|
||||
@@ -484,6 +485,7 @@ geometry_node_categories = [
|
||||
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
|
||||
NodeItem("GeometryNodeAttributeRandomize"),
|
||||
NodeItem("GeometryNodeAttributeMath"),
|
||||
NodeItem("GeometryNodeAttributeClamp"),
|
||||
NodeItem("GeometryNodeAttributeCompare"),
|
||||
NodeItem("GeometryNodeAttributeConvert"),
|
||||
NodeItem("GeometryNodeAttributeFill"),
|
||||
@@ -495,7 +497,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeAttributeCombineXYZ"),
|
||||
NodeItem("GeometryNodeAttributeSeparateXYZ"),
|
||||
NodeItem("GeometryNodeAttributeRemove"),
|
||||
NodeItem("FunctionNodeGroup"),
|
||||
NodeItem("GeometryNodeAttributeMapRange"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
||||
NodeItem("ShaderNodeValToRGB"),
|
||||
@@ -521,7 +523,16 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeEdgeSplit"),
|
||||
NodeItem("GeometryNodeSubdivisionSurface"),
|
||||
NodeItem("GeometryNodeSubdivide"),
|
||||
|
||||
]),
|
||||
GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[
|
||||
NodeItem("GeometryNodeMeshCircle"),
|
||||
NodeItem("GeometryNodeMeshCone"),
|
||||
NodeItem("GeometryNodeMeshCube"),
|
||||
NodeItem("GeometryNodeMeshCylinder"),
|
||||
NodeItem("GeometryNodeMeshGrid"),
|
||||
NodeItem("GeometryNodeMeshIcoSphere"),
|
||||
NodeItem("GeometryNodeMeshLine"),
|
||||
NodeItem("GeometryNodeMeshUVSphere"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_POINT", "Point", items=[
|
||||
NodeItem("GeometryNodePointDistribute"),
|
||||
@@ -532,20 +543,6 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeRotatePoints"),
|
||||
NodeItem("GeometryNodeAlignRotationToVector"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
|
||||
NodeItem("GeometryNodePointsToVolume"),
|
||||
NodeItem("GeometryNodeVolumeToMesh"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[
|
||||
NodeItem("GeometryNodeMeshCube"),
|
||||
NodeItem("GeometryNodeMeshCircle"),
|
||||
NodeItem("GeometryNodeMeshUVSphere"),
|
||||
NodeItem("GeometryNodeMeshIcoSphere"),
|
||||
NodeItem("GeometryNodeMeshCylinder"),
|
||||
NodeItem("GeometryNodeMeshCone"),
|
||||
NodeItem("GeometryNodeMeshLine"),
|
||||
NodeItem("GeometryNodeMeshPlane"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
|
||||
NodeItem("ShaderNodeMapRange"),
|
||||
NodeItem("ShaderNodeClamp"),
|
||||
@@ -559,10 +556,16 @@ geometry_node_categories = [
|
||||
NodeItem("ShaderNodeVectorMath"),
|
||||
NodeItem("ShaderNodeVectorRotate"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
|
||||
NodeItem("GeometryNodePointsToVolume"),
|
||||
NodeItem("GeometryNodeVolumeToMesh"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
|
||||
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
|
||||
NodeItem("NodeFrame"),
|
||||
NodeItem("NodeReroute"),
|
||||
NodeItem("NodePortalIn"),
|
||||
NodeItem("NodePortalOut"),
|
||||
]),
|
||||
]
|
||||
|
||||
|
10
release/scripts/templates_osl/basic_shader.osl
Normal file
10
release/scripts/templates_osl/basic_shader.osl
Normal file
@@ -0,0 +1,10 @@
|
||||
shader basic_shader(
|
||||
float in_float = 1.0,
|
||||
color in_color = color(1.0, 1.0, 1.0),
|
||||
output float out_float = 0.0,
|
||||
output color out_color = color(0.0, 0.0, 0.0)
|
||||
)
|
||||
{
|
||||
out_float = in_float * 2.0;
|
||||
out_color = in_color * 2.0;
|
||||
}
|
35
release/scripts/templates_py/image_processing.py
Normal file
35
release/scripts/templates_py/image_processing.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# This sample shows the an efficient way of doing image processing
|
||||
# over Blender's images using Python.
|
||||
|
||||
import bpy
|
||||
import numpy as np
|
||||
|
||||
|
||||
input_image_name = "Image"
|
||||
output_image_name = "NewImage"
|
||||
|
||||
# Retrieve input image.
|
||||
input_image = bpy.data.images[input_image_name]
|
||||
w, h = input_image.size
|
||||
|
||||
# Allocate a numpy array to manipulate pixel data.
|
||||
pixel_data = np.zeros((w, h, 4), 'f')
|
||||
|
||||
# Fast copy of pixel data from bpy.data to numpy array.
|
||||
input_image.pixels.foreach_get(pixel_data.ravel())
|
||||
|
||||
# Do whatever image processing you want using numpy here:
|
||||
# Example 1: Inverse red green and blue channels.
|
||||
pixel_data[:,:,:3] = 1.0 - pixel_data[:,:,:3]
|
||||
# Example 2: Change gamma on the red channel.
|
||||
pixel_data[:,:,0] = np.power(pixel_data[:,:,0], 1.5)
|
||||
|
||||
# Create output image.
|
||||
if output_image_name in bpy.data.images:
|
||||
output_image = bpy.data.images[output_image_name]
|
||||
else:
|
||||
output_image = bpy.data.images.new(output_image_name, width=w, height=h)
|
||||
|
||||
# Copy of pixel data from numpy array back to the output image.
|
||||
output_image.pixels.foreach_set(pixel_data.ravel())
|
||||
output_image.update()
|
@@ -31,6 +31,7 @@ set(SRC
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG " /nodefaultlib:MSVCRT.lib")
|
||||
|
||||
add_library(BlendThumb SHARED ${SRC})
|
||||
setup_platform_linker_flags(BlendThumb)
|
||||
target_link_libraries(BlendThumb ${ZLIB_LIBRARIES})
|
||||
|
||||
install(
|
||||
|
@@ -188,10 +188,6 @@ void BKE_pose_itasc_init(struct bItasc *itasc);
|
||||
/* Checks if a bone is part of an IK chain or not */
|
||||
bool BKE_pose_channel_in_IK_chain(struct Object *ob, struct bPoseChannel *pchan);
|
||||
|
||||
/* clears BONE_UNKEYED flags for frame changing */
|
||||
// XXX to be deprecated for a more general solution in animsys...
|
||||
void framechange_poses_clear_unkeyed(struct Main *bmain);
|
||||
|
||||
/* Bone Groups API --------------------- */
|
||||
|
||||
/* Adds a new bone-group */
|
||||
@@ -227,6 +223,9 @@ void BKE_pose_blend_read_data(struct BlendDataReader *reader, struct bPose *pose
|
||||
void BKE_pose_blend_read_lib(struct BlendLibReader *reader, struct Object *ob, struct bPose *pose);
|
||||
void BKE_pose_blend_read_expand(struct BlendExpander *expander, struct bPose *pose);
|
||||
|
||||
/* action_mirror.c */
|
||||
void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
@@ -342,6 +342,8 @@ void BKE_pchan_bbone_deform_segment_index(const struct bPoseChannel *pchan,
|
||||
#define PBONE_SELECTABLE(arm, bone) \
|
||||
(PBONE_VISIBLE(arm, bone) && !((bone)->flag & BONE_UNSELECTABLE))
|
||||
|
||||
#define PBONE_SELECTED(arm, bone) (((bone)->flag & BONE_SELECTED) & PBONE_VISIBLE(arm, bone))
|
||||
|
||||
/* context.selected_pose_bones */
|
||||
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN(_ob, _pchan) \
|
||||
for (bPoseChannel *_pchan = (_ob)->pose->chanbase.first; _pchan; _pchan = _pchan->next) { \
|
||||
|
@@ -39,12 +39,12 @@ struct ReportList;
|
||||
|
||||
/* Attribute.domain */
|
||||
typedef enum AttributeDomain {
|
||||
ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */
|
||||
ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */
|
||||
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
|
||||
ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */
|
||||
ATTR_DOMAIN_POLYGON = 3, /* Mesh Polygon */
|
||||
ATTR_DOMAIN_CURVE = 4, /* Hair Curve */
|
||||
ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */
|
||||
ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */
|
||||
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
|
||||
ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */
|
||||
ATTR_DOMAIN_FACE = 3, /* Mesh Face */
|
||||
ATTR_DOMAIN_CURVE = 4, /* Hair Curve */
|
||||
|
||||
ATTR_DOMAIN_NUM
|
||||
} AttributeDomain;
|
||||
|
@@ -39,7 +39,7 @@ extern "C" {
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 14
|
||||
#define BLENDER_FILE_SUBVERSION 15
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
@@ -232,6 +232,19 @@ int BKE_fcurve_bezt_binarysearch_index(struct BezTriple array[],
|
||||
int arraylen,
|
||||
bool *r_replace);
|
||||
|
||||
/* fcurve_cache.c */
|
||||
/* Cached f-curve look-ups, use when this needs to be done many times. */
|
||||
struct FCurvePathCache;
|
||||
struct FCurvePathCache *BKE_fcurve_pathcache_create(ListBase *list);
|
||||
void BKE_fcurve_pathcache_destroy(struct FCurvePathCache *fcache);
|
||||
struct FCurve *BKE_fcurve_pathcache_find(struct FCurvePathCache *fcache,
|
||||
const char rna_path[],
|
||||
const int array_index);
|
||||
int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache,
|
||||
const char *rna_path,
|
||||
struct FCurve **fcurve_result,
|
||||
int fcurve_result_len);
|
||||
|
||||
/* get the time extents for F-Curve */
|
||||
bool BKE_fcurve_calc_range(
|
||||
struct FCurve *fcu, float *min, float *max, const bool do_sel_only, const bool do_min_length);
|
||||
@@ -245,6 +258,14 @@ bool BKE_fcurve_calc_bounds(struct FCurve *fcu,
|
||||
const bool do_sel_only,
|
||||
const bool include_handles);
|
||||
|
||||
float *BKE_fcurves_calc_keyed_frames_ex(struct FCurve **fcurve_array,
|
||||
const int fcurve_array_len,
|
||||
const float interval,
|
||||
int *r_frames_len);
|
||||
float *BKE_fcurves_calc_keyed_frames(struct FCurve **fcurve_array,
|
||||
const int fcurve_array_len,
|
||||
int *r_frames_len);
|
||||
|
||||
void BKE_fcurve_active_keyframe_set(struct FCurve *fcu, const struct BezTriple *active_bezt);
|
||||
int BKE_fcurve_active_keyframe_index(const struct FCurve *fcu);
|
||||
|
||||
|
@@ -313,6 +313,8 @@ struct GeometrySet {
|
||||
friend bool operator==(const GeometrySet &a, const GeometrySet &b);
|
||||
uint64_t hash() const;
|
||||
|
||||
void clear();
|
||||
|
||||
/* Utility methods for creation. */
|
||||
static GeometrySet create_with_mesh(
|
||||
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
|
@@ -39,7 +39,8 @@ struct GeometryInstanceGroup {
|
||||
Vector<float4x4> transforms;
|
||||
};
|
||||
|
||||
Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set);
|
||||
void geometry_set_gather_instances(const GeometrySet &geometry_set,
|
||||
Vector<GeometryInstanceGroup> &r_instance_groups);
|
||||
|
||||
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set);
|
||||
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
|
||||
|
@@ -74,6 +74,7 @@ bool BKE_lib_override_library_create(struct Main *bmain,
|
||||
struct ViewLayer *view_layer,
|
||||
struct ID *id_root,
|
||||
struct ID *id_reference);
|
||||
bool BKE_lib_override_library_template_create(struct ID *id);
|
||||
bool BKE_lib_override_library_proxy_convert(struct Main *bmain,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer,
|
||||
|
@@ -483,14 +483,14 @@ void ntreeBlendReadExpand(struct BlendExpander *expander, struct bNodeTree *ntre
|
||||
/** \name Node Tree Interface
|
||||
* \{ */
|
||||
struct bNodeSocket *ntreeFindSocketInterface(struct bNodeTree *ntree,
|
||||
int in_out,
|
||||
eNodeSocketInOut in_out,
|
||||
const char *identifier);
|
||||
struct bNodeSocket *ntreeAddSocketInterface(struct bNodeTree *ntree,
|
||||
int in_out,
|
||||
eNodeSocketInOut in_out,
|
||||
const char *idname,
|
||||
const char *name);
|
||||
struct bNodeSocket *ntreeInsertSocketInterface(struct bNodeTree *ntree,
|
||||
int in_out,
|
||||
eNodeSocketInOut in_out,
|
||||
const char *idname,
|
||||
struct bNodeSocket *next_sock,
|
||||
const char *name);
|
||||
@@ -556,30 +556,32 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype);
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
struct bNodeSocket *nodeFindSocket(const struct bNode *node, int in_out, const char *identifier);
|
||||
struct bNodeSocket *nodeFindSocket(const struct bNode *node,
|
||||
eNodeSocketInOut in_out,
|
||||
const char *identifier);
|
||||
struct bNodeSocket *nodeAddSocket(struct bNodeTree *ntree,
|
||||
struct bNode *node,
|
||||
int in_out,
|
||||
eNodeSocketInOut in_out,
|
||||
const char *idname,
|
||||
const char *identifier,
|
||||
const char *name);
|
||||
struct bNodeSocket *nodeInsertSocket(struct bNodeTree *ntree,
|
||||
struct bNode *node,
|
||||
int in_out,
|
||||
eNodeSocketInOut in_out,
|
||||
const char *idname,
|
||||
struct bNodeSocket *next_sock,
|
||||
const char *identifier,
|
||||
const char *name);
|
||||
struct bNodeSocket *nodeAddStaticSocket(struct bNodeTree *ntree,
|
||||
struct bNode *node,
|
||||
int in_out,
|
||||
eNodeSocketInOut in_out,
|
||||
int type,
|
||||
int subtype,
|
||||
const char *identifier,
|
||||
const char *name);
|
||||
struct bNodeSocket *nodeInsertStaticSocket(struct bNodeTree *ntree,
|
||||
struct bNode *node,
|
||||
int in_out,
|
||||
eNodeSocketInOut in_out,
|
||||
int type,
|
||||
int subtype,
|
||||
struct bNodeSocket *next_sock,
|
||||
@@ -858,6 +860,8 @@ bool BKE_node_is_connected_to_output(struct bNodeTree *ntree, struct bNode *node
|
||||
#define NODE_GROUP_INPUT 7
|
||||
#define NODE_GROUP_OUTPUT 8
|
||||
#define NODE_CUSTOM_GROUP 9
|
||||
#define NODE_PORTAL_IN 10
|
||||
#define NODE_PORTAL_OUT 11
|
||||
|
||||
void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree);
|
||||
|
||||
@@ -1188,6 +1192,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
|
||||
#define CMP_NODE_TRACKPOS 271
|
||||
#define CMP_NODE_INPAINT 272
|
||||
#define CMP_NODE_DESPECKLE 273
|
||||
#define CMP_NODE_ANTIALIASING 274
|
||||
|
||||
#define CMP_NODE_GLARE 301
|
||||
#define CMP_NODE_TONEMAP 302
|
||||
@@ -1393,7 +1398,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1036
|
||||
#define GEO_NODE_MESH_PRIMITIVE_CONE 1037
|
||||
#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
|
||||
#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039
|
||||
#define GEO_NODE_MESH_PRIMITIVE_GRID 1039
|
||||
#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
|
||||
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -58,9 +58,7 @@ class NodeTreeEvaluationContext {
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
const uint64_t hash1 = blender::DefaultHash<std::string>{}(object_name_);
|
||||
const uint64_t hash2 = BLI_session_uuid_hash_uint64(&modifier_session_uuid_);
|
||||
return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */
|
||||
return blender::get_default_hash_2(object_name_, modifier_session_uuid_);
|
||||
}
|
||||
|
||||
friend bool operator==(const NodeTreeEvaluationContext &a, const NodeTreeEvaluationContext &b)
|
||||
|
@@ -336,6 +336,12 @@ struct Mesh *BKE_object_get_evaluated_mesh(struct Object *object);
|
||||
struct Mesh *BKE_object_get_pre_modified_mesh(struct Object *object);
|
||||
struct Mesh *BKE_object_get_original_mesh(struct Object *object);
|
||||
|
||||
/* Lattice accessors.
|
||||
* These functions return either the regular lattice, or the edit-mode lattice,
|
||||
* whichever is currently in use. */
|
||||
struct Lattice *BKE_object_get_lattice(const struct Object *object);
|
||||
struct Lattice *BKE_object_get_evaluated_lattice(const struct Object *object);
|
||||
|
||||
int BKE_object_insert_ptcache(struct Object *ob);
|
||||
void BKE_object_delete_ptcache(struct Object *ob, int index);
|
||||
struct KeyBlock *BKE_object_shapekey_insert(struct Main *bmain,
|
||||
|
@@ -69,6 +69,7 @@ set(SRC
|
||||
intern/CCGSubSurf_util.c
|
||||
intern/DerivedMesh.cc
|
||||
intern/action.c
|
||||
intern/action_mirror.c
|
||||
intern/addon.c
|
||||
intern/anim_data.c
|
||||
intern/anim_path.c
|
||||
@@ -126,6 +127,7 @@ set(SRC
|
||||
intern/editmesh_tangent.c
|
||||
intern/effect.c
|
||||
intern/fcurve.c
|
||||
intern/fcurve_cache.c
|
||||
intern/fcurve_driver.c
|
||||
intern/fluid.c
|
||||
intern/fmodifier.c
|
||||
@@ -385,6 +387,7 @@ set(SRC
|
||||
BKE_multires.h
|
||||
BKE_nla.h
|
||||
BKE_node.h
|
||||
BKE_node_ui_storage.hh
|
||||
BKE_object.h
|
||||
BKE_object_deform.h
|
||||
BKE_object_facemap.h
|
||||
@@ -434,10 +437,10 @@ set(SRC
|
||||
nla_private.h
|
||||
particle_private.h
|
||||
tracking_private.h
|
||||
intern/attribute_access_intern.hh
|
||||
intern/CCGSubSurf.h
|
||||
intern/CCGSubSurf_inline.h
|
||||
intern/CCGSubSurf_intern.h
|
||||
intern/attribute_access_intern.hh
|
||||
intern/data_transfer_intern.h
|
||||
intern/lib_intern.h
|
||||
intern/multires_inline.h
|
||||
@@ -742,7 +745,7 @@ if(WITH_GMP)
|
||||
list(APPEND INC_SYS
|
||||
${GMP_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# # Warnings as errors, this is too strict!
|
||||
# if(MSVC)
|
||||
|
@@ -1316,30 +1316,6 @@ void BKE_pose_tag_update_constraint_flags(bPose *pose)
|
||||
pose->flag |= POSE_CONSTRAINTS_NEED_UPDATE_FLAGS;
|
||||
}
|
||||
|
||||
/* Clears all BONE_UNKEYED flags for every pose channel in every pose
|
||||
* This should only be called on frame changing, when it is acceptable to
|
||||
* do this. Otherwise, these flags should not get cleared as poses may get lost.
|
||||
*/
|
||||
void framechange_poses_clear_unkeyed(Main *bmain)
|
||||
{
|
||||
Object *ob;
|
||||
bPose *pose;
|
||||
bPoseChannel *pchan;
|
||||
|
||||
/* This needs to be done for each object that has a pose */
|
||||
/* TODO: proxies may/may not be correctly handled here... (this needs checking) */
|
||||
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
|
||||
/* we only need to do this on objects with a pose */
|
||||
if ((pose = ob->pose)) {
|
||||
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
if (pchan->bone) {
|
||||
pchan->bone->flag &= ~BONE_UNKEYED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************** Bone Groups ************************** */
|
||||
|
||||
/* Adds a new bone-group (name may be NULL) */
|
||||
|
457
source/blender/blenkernel/intern/action_mirror.c
Normal file
457
source/blender/blenkernel/intern/action_mirror.c
Normal file
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* Mirror/Symmetry functions applying to actions.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_fcurve.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Flip the Action (Armature/Pose Objects)
|
||||
*
|
||||
* This flips the action using the rest pose (not the evaluated pose).
|
||||
*
|
||||
* Details:
|
||||
*
|
||||
* - Key-frames are modified in-place, creating new key-frames is not yet supported.
|
||||
* That could be useful if a user for example only has 2x rotation channels set.
|
||||
* In practice users typically keyframe all rotation channels or none.
|
||||
*
|
||||
* - F-curve modifiers are disabled for evaluation,
|
||||
* so the values written back to the keyframes don't include modifier offsets.
|
||||
*
|
||||
* - Sub-frame key-frames aren't supported,
|
||||
* this could be added if needed without much trouble.
|
||||
*
|
||||
* - F-curves must have a #FCurve.bezt array (sampled curves aren't supported).
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* This structure is created for each pose channels F-curve,
|
||||
* an action be evaluated and stored in `fcurve_eval`,
|
||||
* with the mirrored values written into `bezt_array`.
|
||||
*
|
||||
* Store F-curve evaluated values, constructed with a sorted array of rounded keyed-frames,
|
||||
* passed to #action_flip_pchan_cache_init.
|
||||
*/
|
||||
struct FCurve_KeyCache {
|
||||
/**
|
||||
* When NULL, ignore this channel.
|
||||
*/
|
||||
FCurve *fcurve;
|
||||
/**
|
||||
* Cached evaluated F-curve values (without modifiers).
|
||||
*/
|
||||
float *fcurve_eval;
|
||||
/**
|
||||
* Cached #FCurve.bezt values, NULL when no key-frame exists on this frame.
|
||||
*
|
||||
* \note The case where two keyframes round to the same frame isn't supported.
|
||||
* In this case only the first will be used.
|
||||
*/
|
||||
BezTriple **bezt_array;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign `fkc` path, using a `path` lookup for a single value.
|
||||
*/
|
||||
static void action_flip_pchan_cache_fcurve_assign_value(struct FCurve_KeyCache *fkc,
|
||||
int index,
|
||||
const char *path,
|
||||
struct FCurvePathCache *fcache)
|
||||
{
|
||||
FCurve *fcu = BKE_fcurve_pathcache_find(fcache, path, index);
|
||||
if (fcu && fcu->bezt) {
|
||||
fkc->fcurve = fcu;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign #FCurve_KeyCache.fcurve path, using a `path` lookup for an array.
|
||||
*/
|
||||
static void action_flip_pchan_cache_fcurve_assign_array(struct FCurve_KeyCache *fkc,
|
||||
int fkc_len,
|
||||
const char *path,
|
||||
struct FCurvePathCache *fcache)
|
||||
{
|
||||
FCurve **fcurves = alloca(sizeof(*fcurves) * fkc_len);
|
||||
if (BKE_fcurve_pathcache_find_array(fcache, path, fcurves, fkc_len)) {
|
||||
for (int i = 0; i < fkc_len; i++) {
|
||||
if (fcurves[i] && fcurves[i]->bezt) {
|
||||
fkc[i].fcurve = fcurves[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in pose channel cache for each frame in `keyed_frames`.
|
||||
*
|
||||
* \param keyed_frames: An array of keyed_frames to evaluate,
|
||||
* note that each frame is rounded to the nearest int.
|
||||
* \param keyed_frames_len: The length of the `keyed_frames` array.
|
||||
*/
|
||||
static void action_flip_pchan_cache_init(struct FCurve_KeyCache *fkc,
|
||||
const float *keyed_frames,
|
||||
int keyed_frames_len)
|
||||
{
|
||||
BLI_assert(fkc->fcurve != NULL);
|
||||
|
||||
/* Cache the F-curve values for `keyed_frames`. */
|
||||
const int fcurve_flag = fkc->fcurve->flag;
|
||||
fkc->fcurve->flag |= FCURVE_MOD_OFF;
|
||||
fkc->fcurve_eval = MEM_mallocN(sizeof(float) * keyed_frames_len, __func__);
|
||||
for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
|
||||
const float evaltime = keyed_frames[frame_index];
|
||||
fkc->fcurve_eval[frame_index] = evaluate_fcurve_only_curve(fkc->fcurve, evaltime);
|
||||
}
|
||||
fkc->fcurve->flag = fcurve_flag;
|
||||
|
||||
/* Cache the #BezTriple for `keyed_frames`, or leave as NULL. */
|
||||
fkc->bezt_array = MEM_mallocN(sizeof(*fkc->bezt_array) * keyed_frames_len, __func__);
|
||||
BezTriple *bezt = fkc->fcurve->bezt;
|
||||
BezTriple *bezt_end = fkc->fcurve->bezt + fkc->fcurve->totvert;
|
||||
|
||||
int frame_index = 0;
|
||||
while (frame_index < keyed_frames_len) {
|
||||
const float evaltime = keyed_frames[frame_index];
|
||||
const float bezt_time = roundf(bezt->vec[1][0]);
|
||||
if (bezt_time > evaltime) {
|
||||
fkc->bezt_array[frame_index++] = NULL;
|
||||
}
|
||||
else {
|
||||
if (bezt_time == evaltime) {
|
||||
fkc->bezt_array[frame_index++] = bezt;
|
||||
}
|
||||
bezt++;
|
||||
if (bezt == bezt_end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Clear remaining unset keyed_frames (if-any). */
|
||||
while (frame_index < keyed_frames_len) {
|
||||
fkc->bezt_array[frame_index++] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
static void action_flip_pchan(Object *ob_arm,
|
||||
const bPoseChannel *pchan,
|
||||
struct FCurvePathCache *fcache)
|
||||
{
|
||||
/* Begin F-Curve pose channel value extraction. */
|
||||
/* Use a fixed buffer size as it's known this can only be at most:
|
||||
* `pose.bones["{MAXBONENAME}"].rotation_quaternion`. */
|
||||
char path_xform[256];
|
||||
char pchan_name_esc[sizeof(((bActionChannel *)NULL)->name) * 2];
|
||||
BLI_str_escape(pchan_name_esc, pchan->name, sizeof(pchan_name_esc));
|
||||
const int path_xform_prefix_len = SNPRINTF(path_xform, "pose.bones[\"%s\"]", pchan_name_esc);
|
||||
char *path_xform_suffix = path_xform + path_xform_prefix_len;
|
||||
const int path_xform_suffix_len = sizeof(path_xform) - path_xform_prefix_len;
|
||||
|
||||
/* Lookup and assign all available #FCurve channels,
|
||||
* unavailable channels are left NULL. */
|
||||
|
||||
/**
|
||||
* Structure to store transformation F-curves corresponding to a pose bones transformation.
|
||||
* Match struct member names from #bPoseChannel so macros avoid repetition.
|
||||
*
|
||||
* \note There is no need to read values unless they influence the 4x4 transform matrix,
|
||||
* and no need to write values back unless they would be changed by a modified matrix.
|
||||
* So `rotmode` needs to be read, but doesn't need to be written back to.
|
||||
*
|
||||
* Most bendy-bone settings don't need to be included either, flipping their RNA paths is enough.
|
||||
* Although the X/Y settings could make sense to transform, in practice it would only
|
||||
* work well if the rotation happened to swap X/Y alignment, leave this for now.
|
||||
*/
|
||||
struct {
|
||||
struct FCurve_KeyCache loc[3], eul[3], quat[4], rotAxis[3], rotAngle, size[3], rotmode;
|
||||
} fkc_pchan = {{{NULL}}};
|
||||
|
||||
#define FCURVE_ASSIGN_VALUE(id, path_test_suffix, index) \
|
||||
BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
|
||||
action_flip_pchan_cache_fcurve_assign_value(&fkc_pchan.id, index, path_xform, fcache)
|
||||
|
||||
#define FCURVE_ASSIGN_ARRAY(id, path_test_suffix) \
|
||||
BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
|
||||
action_flip_pchan_cache_fcurve_assign_array( \
|
||||
fkc_pchan.id, ARRAY_SIZE(fkc_pchan.id), path_xform, fcache)
|
||||
|
||||
FCURVE_ASSIGN_ARRAY(loc, ".location");
|
||||
FCURVE_ASSIGN_ARRAY(eul, ".rotation_euler");
|
||||
FCURVE_ASSIGN_ARRAY(quat, ".rotation_quaternion");
|
||||
FCURVE_ASSIGN_ARRAY(rotAxis, ".rotation_axis_angle");
|
||||
FCURVE_ASSIGN_VALUE(rotAngle, ".rotation_axis_angle", 3);
|
||||
FCURVE_ASSIGN_ARRAY(size, ".scale");
|
||||
FCURVE_ASSIGN_VALUE(rotmode, ".rotation_mode", 0);
|
||||
|
||||
#undef FCURVE_ASSIGN_VALUE
|
||||
#undef FCURVE_ASSIGN_ARRAY
|
||||
|
||||
/* Array of F-curves, for convenient access. */
|
||||
#define FCURVE_CHANNEL_LEN (sizeof(fkc_pchan) / sizeof(struct FCurve_KeyCache))
|
||||
FCurve *fcurve_array[FCURVE_CHANNEL_LEN];
|
||||
int fcurve_array_len = 0;
|
||||
|
||||
for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
|
||||
struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
|
||||
if (fkc->fcurve != NULL) {
|
||||
fcurve_array[fcurve_array_len++] = fkc->fcurve;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this pose has no transform channels, there is nothing to do. */
|
||||
if (fcurve_array_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate an array of frames used by any of the key-frames in `fcurve_array`. */
|
||||
int keyed_frames_len;
|
||||
const float *keyed_frames = BKE_fcurves_calc_keyed_frames(
|
||||
fcurve_array, fcurve_array_len, &keyed_frames_len);
|
||||
|
||||
/* Initialize the pose channel curve cache from the F-curve. */
|
||||
for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
|
||||
struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
|
||||
if (fkc->fcurve == NULL) {
|
||||
continue;
|
||||
}
|
||||
action_flip_pchan_cache_init(fkc, keyed_frames, keyed_frames_len);
|
||||
}
|
||||
|
||||
/* X-axis flipping matrix. */
|
||||
float flip_mtx[4][4];
|
||||
unit_m4(flip_mtx);
|
||||
flip_mtx[0][0] = -1;
|
||||
|
||||
bPoseChannel *pchan_flip = NULL;
|
||||
char pchan_name_flip[MAXBONENAME];
|
||||
BLI_string_flip_side_name(pchan_name_flip, pchan->name, false, sizeof(pchan_name_flip));
|
||||
if (!STREQ(pchan_name_flip, pchan->name)) {
|
||||
pchan_flip = BKE_pose_channel_find_name(ob_arm->pose, pchan_name_flip);
|
||||
}
|
||||
|
||||
float arm_mat_inv[4][4];
|
||||
invert_m4_m4(arm_mat_inv, pchan_flip ? pchan_flip->bone->arm_mat : pchan->bone->arm_mat);
|
||||
|
||||
/* Now flip the transformation & write it back to the F-curves in `fkc_pchan`. */
|
||||
|
||||
for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
|
||||
|
||||
/* Temporary pose channel to write values into,
|
||||
* using the `fkc_pchan` values, falling back to the values in the pose channel. */
|
||||
bPoseChannel pchan_temp = *pchan;
|
||||
|
||||
/* Load the values into the channel. */
|
||||
#define READ_VALUE_FLT(id) \
|
||||
if (fkc_pchan.id.fcurve_eval != NULL) { \
|
||||
pchan_temp.id = fkc_pchan.id.fcurve_eval[frame_index]; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define READ_VALUE_INT(id) \
|
||||
if (fkc_pchan.id.fcurve_eval != NULL) { \
|
||||
pchan_temp.id = floorf(fkc_pchan.id.fcurve_eval[frame_index] + 0.5f); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define READ_ARRAY_FLT(id) \
|
||||
for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
|
||||
READ_VALUE_FLT(id[i]); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
READ_ARRAY_FLT(loc);
|
||||
READ_ARRAY_FLT(eul);
|
||||
READ_ARRAY_FLT(quat);
|
||||
READ_ARRAY_FLT(rotAxis);
|
||||
READ_VALUE_FLT(rotAngle);
|
||||
READ_ARRAY_FLT(size);
|
||||
READ_VALUE_INT(rotmode);
|
||||
|
||||
#undef READ_ARRAY_FLT
|
||||
#undef READ_VALUE_FLT
|
||||
#undef READ_VALUE_INT
|
||||
|
||||
float chan_mat[4][4];
|
||||
BKE_pchan_to_mat4(&pchan_temp, chan_mat);
|
||||
|
||||
/* Move to the pose-space. */
|
||||
mul_m4_m4m4(chan_mat, pchan->bone->arm_mat, chan_mat);
|
||||
|
||||
/* Flip the matrix. */
|
||||
mul_m4_m4m4(chan_mat, chan_mat, flip_mtx);
|
||||
mul_m4_m4m4(chan_mat, flip_mtx, chan_mat);
|
||||
|
||||
/* Move back to bone-space space, using the flipped bone if it exists. */
|
||||
mul_m4_m4m4(chan_mat, arm_mat_inv, chan_mat);
|
||||
|
||||
BKE_pchan_apply_mat4(&pchan_temp, chan_mat, false);
|
||||
|
||||
/* Write the values back to the F-curves. */
|
||||
#define WRITE_VALUE_FLT(id) \
|
||||
if (fkc_pchan.id.fcurve_eval != NULL) { \
|
||||
BezTriple *bezt = fkc_pchan.id.bezt_array[frame_index]; \
|
||||
if (bezt != NULL) { \
|
||||
const float delta = pchan_temp.id - bezt->vec[1][1]; \
|
||||
bezt->vec[0][1] += delta; \
|
||||
bezt->vec[1][1] += delta; \
|
||||
bezt->vec[2][1] += delta; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define WRITE_ARRAY_FLT(id) \
|
||||
for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
|
||||
WRITE_VALUE_FLT(id[i]); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* Write the values back the the F-curves. */
|
||||
WRITE_ARRAY_FLT(loc);
|
||||
WRITE_ARRAY_FLT(eul);
|
||||
WRITE_ARRAY_FLT(quat);
|
||||
WRITE_ARRAY_FLT(rotAxis);
|
||||
WRITE_VALUE_FLT(rotAngle);
|
||||
WRITE_ARRAY_FLT(size);
|
||||
/* No need to write back 'rotmode' as it can't be transformed. */
|
||||
|
||||
#undef WRITE_ARRAY_FLT
|
||||
#undef WRITE_VALUE_FLT
|
||||
}
|
||||
|
||||
/* Recalculate handles. */
|
||||
for (int i = 0; i < fcurve_array_len; i++) {
|
||||
calchandles_fcurve_ex(fcurve_array[i], 0);
|
||||
}
|
||||
|
||||
MEM_freeN((void *)keyed_frames);
|
||||
|
||||
for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
|
||||
struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
|
||||
if (fkc->fcurve_eval) {
|
||||
MEM_freeN(fkc->fcurve_eval);
|
||||
}
|
||||
if (fkc->bezt_array) {
|
||||
MEM_freeN(fkc->bezt_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap all RNA paths left/right.
|
||||
*/
|
||||
static void action_flip_pchan_rna_paths(struct bAction *act)
|
||||
{
|
||||
const char *path_pose_prefix = "pose.bones[\"";
|
||||
const int path_pose_prefix_len = strlen(path_pose_prefix);
|
||||
|
||||
/* Tag curves that have renamed f-curves. */
|
||||
LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
|
||||
agrp->flag &= ~AGRP_TEMP;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
|
||||
if (!STRPREFIX(fcu->rna_path, path_pose_prefix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *name_esc = fcu->rna_path + path_pose_prefix_len;
|
||||
const char *name_esc_end = BLI_str_escape_find_quote(name_esc);
|
||||
|
||||
/* While unlikely, an RNA path could be malformed. */
|
||||
if (UNLIKELY(name_esc_end == NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char name[MAXBONENAME];
|
||||
const size_t name_esc_len = (size_t)(name_esc_end - name_esc);
|
||||
const size_t name_len = BLI_str_unescape(name, name_esc, name_esc_len);
|
||||
|
||||
/* While unlikely, data paths could be constructed that have longer names than
|
||||
* are currently supported. */
|
||||
if (UNLIKELY(name_len >= sizeof(name))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* When the flipped name differs, perform the rename. */
|
||||
char name_flip[MAXBONENAME];
|
||||
BLI_string_flip_side_name(name_flip, name, false, sizeof(name_flip));
|
||||
if (!STREQ(name_flip, name)) {
|
||||
char name_flip_esc[MAXBONENAME * 2];
|
||||
BLI_str_escape(name_flip_esc, name_flip, sizeof(name_flip_esc));
|
||||
char *path_flip = BLI_sprintfN("pose.bones[\"%s%s", name_flip_esc, name_esc_end);
|
||||
MEM_freeN(fcu->rna_path);
|
||||
fcu->rna_path = path_flip;
|
||||
|
||||
if (fcu->grp != NULL) {
|
||||
fcu->grp->flag |= AGRP_TEMP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Rename tagged groups. */
|
||||
LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
|
||||
if ((agrp->flag & AGRP_TEMP) == 0) {
|
||||
continue;
|
||||
}
|
||||
agrp->flag &= ~AGRP_TEMP;
|
||||
char name_flip[MAXBONENAME];
|
||||
BLI_string_flip_side_name(name_flip, agrp->name, false, sizeof(name_flip));
|
||||
if (!STREQ(name_flip, agrp->name)) {
|
||||
STRNCPY(agrp->name, name_flip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm)
|
||||
{
|
||||
struct FCurvePathCache *fcache = BKE_fcurve_pathcache_create(&act->curves);
|
||||
int i;
|
||||
LISTBASE_FOREACH_INDEX (bPoseChannel *, pchan, &ob_arm->pose->chanbase, i) {
|
||||
action_flip_pchan(ob_arm, pchan, fcache);
|
||||
}
|
||||
BKE_fcurve_pathcache_destroy(fcache);
|
||||
|
||||
action_flip_pchan_rna_paths(act);
|
||||
|
||||
DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
|
||||
}
|
||||
|
||||
/** \} */
|
@@ -39,7 +39,7 @@ namespace {
|
||||
using BoneNameSet = blender::Set<std::string>;
|
||||
|
||||
// Forward declarations.
|
||||
BoneNameSet pose_apply_find_selected_bones(const bPose *pose);
|
||||
BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose);
|
||||
void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
|
||||
const BoneNameSet &selected_bone_names);
|
||||
void pose_apply_restore_fcurves(bAction *action);
|
||||
@@ -54,7 +54,8 @@ void BKE_pose_apply_action(struct Object *ob,
|
||||
return;
|
||||
}
|
||||
|
||||
const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(pose);
|
||||
const bArmature *armature = (bArmature *)ob->data;
|
||||
const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(armature, pose);
|
||||
const bool limit_to_selected_bones = !selected_bone_names.is_empty();
|
||||
|
||||
if (limit_to_selected_bones) {
|
||||
@@ -74,15 +75,14 @@ void BKE_pose_apply_action(struct Object *ob,
|
||||
}
|
||||
|
||||
namespace {
|
||||
BoneNameSet pose_apply_find_selected_bones(const bPose *pose)
|
||||
BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose)
|
||||
{
|
||||
BoneNameSet selected_bone_names;
|
||||
bool all_bones_selected = true;
|
||||
bool no_bones_selected = true;
|
||||
|
||||
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
|
||||
const bool is_selected = (pchan->bone->flag & BONE_SELECTED) != 0 &&
|
||||
(pchan->bone->flag & BONE_HIDDEN_P) == 0;
|
||||
const bool is_selected = PBONE_SELECTED(armature, pchan->bone);
|
||||
all_bones_selected &= is_selected;
|
||||
no_bones_selected &= !is_selected;
|
||||
|
||||
|
@@ -69,8 +69,8 @@ static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
|
||||
info[ATTR_DOMAIN_EDGE].length = mesh->totedge;
|
||||
info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata;
|
||||
info[ATTR_DOMAIN_CORNER].length = mesh->totloop;
|
||||
info[ATTR_DOMAIN_POLYGON].customdata = &mesh->pdata;
|
||||
info[ATTR_DOMAIN_POLYGON].length = mesh->totpoly;
|
||||
info[ATTR_DOMAIN_FACE].customdata = &mesh->pdata;
|
||||
info[ATTR_DOMAIN_FACE].length = mesh->totpoly;
|
||||
break;
|
||||
}
|
||||
case ID_HA: {
|
||||
|
@@ -331,7 +331,7 @@ static int attribute_domain_priority(const AttributeDomain domain)
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
return 0;
|
||||
#endif
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return 1;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return 2;
|
||||
@@ -893,7 +893,7 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
|
||||
return {};
|
||||
}
|
||||
|
||||
if (attribute->domain() != domain) {
|
||||
if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) {
|
||||
attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
|
@@ -400,6 +400,8 @@ static void setup_app_data(bContext *C,
|
||||
bmain,
|
||||
curscene,
|
||||
bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
|
||||
/* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
|
||||
BKE_lib_override_library_main_operations_create(bmain, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -574,8 +574,8 @@ bool BKE_brush_delete(Main *bmain, Brush *brush)
|
||||
if (brush->id.tag & LIB_TAG_INDIRECT) {
|
||||
return false;
|
||||
}
|
||||
if (BKE_library_ID_is_indirectly_used(bmain, brush) && ID_REAL_USERS(brush) <= 1 &&
|
||||
ID_EXTRA_USERS(brush) == 0) {
|
||||
if (ID_REAL_USERS(brush) <= 1 && ID_EXTRA_USERS(brush) == 0 &&
|
||||
BKE_library_ID_is_indirectly_used(bmain, brush)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -99,6 +99,7 @@ static void cache_file_blend_write(BlendWriter *writer, ID *id, const void *id_a
|
||||
cache_file->handle_readers = NULL;
|
||||
|
||||
BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id);
|
||||
BKE_id_blend_write(writer, &cache_file->id);
|
||||
|
||||
if (cache_file->adt) {
|
||||
BKE_animdata_blend_write(writer, cache_file->adt);
|
||||
|
@@ -425,6 +425,9 @@ static bool from_manifest(CryptomatteLayer &layer, blender::StringRefNull manife
|
||||
}
|
||||
|
||||
const int quoted_hash_len = quoted_string_len_(ref);
|
||||
if (quoted_hash_len < 2) {
|
||||
return false;
|
||||
}
|
||||
const int hash_len = quoted_hash_len - 2;
|
||||
CryptomatteHash hash = CryptomatteHash::from_hex_encoded(ref.substr(1, hash_len));
|
||||
ref = ref.drop_prefix(quoted_hash_len);
|
||||
|
@@ -35,7 +35,9 @@
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_easing.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_sort_utils.h"
|
||||
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_animsys.h"
|
||||
@@ -290,6 +292,12 @@ FCurve *BKE_fcurve_find(ListBase *list, const char rna_path[], const int array_i
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name FCurve Iteration
|
||||
* \{ */
|
||||
|
||||
/* Quick way to loop over all fcurves of a given 'path'. */
|
||||
FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[])
|
||||
{
|
||||
@@ -829,6 +837,56 @@ bool BKE_fcurve_calc_range(
|
||||
return foundvert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of keyed frames, rounded to `interval`.
|
||||
*
|
||||
* \param interval: Set to 1.0 to round to whole keyframes, 0.5 for in-between key-frames, etc.
|
||||
*
|
||||
* \note An interval of zero could be supported (this implies no rounding at all),
|
||||
* however this risks very small differences in float values being treated as separate keyframes.
|
||||
*/
|
||||
float *BKE_fcurves_calc_keyed_frames_ex(FCurve **fcurve_array,
|
||||
int fcurve_array_len,
|
||||
const float interval,
|
||||
int *r_frames_len)
|
||||
{
|
||||
/* Use `1e-3f` as the smallest possible value since these are converted to integers
|
||||
* and we can be sure `MAXFRAME / 1e-3f < INT_MAX` as it's around half the size. */
|
||||
const double interval_db = max_ff(interval, 1e-3f);
|
||||
GSet *frames_unique = BLI_gset_int_new(__func__);
|
||||
for (int fcurve_index = 0; fcurve_index < fcurve_array_len; fcurve_index++) {
|
||||
const FCurve *fcu = fcurve_array[fcurve_index];
|
||||
for (int i = 0; i < fcu->totvert; i++) {
|
||||
const BezTriple *bezt = &fcu->bezt[i];
|
||||
const double value = round((double)bezt->vec[1][0] / interval_db);
|
||||
BLI_assert(value > INT_MIN && value < INT_MAX);
|
||||
BLI_gset_add(frames_unique, POINTER_FROM_INT((int)value));
|
||||
}
|
||||
}
|
||||
|
||||
const size_t frames_len = BLI_gset_len(frames_unique);
|
||||
float *frames = MEM_mallocN(sizeof(*frames) * frames_len, __func__);
|
||||
|
||||
GSetIterator gs_iter;
|
||||
int i = 0;
|
||||
GSET_ITER_INDEX (gs_iter, frames_unique, i) {
|
||||
const int value = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
|
||||
frames[i] = (double)value * interval_db;
|
||||
}
|
||||
BLI_gset_free(frames_unique, NULL);
|
||||
|
||||
qsort(frames, frames_len, sizeof(*frames), BLI_sortutil_cmp_float);
|
||||
*r_frames_len = frames_len;
|
||||
return frames;
|
||||
}
|
||||
|
||||
float *BKE_fcurves_calc_keyed_frames(FCurve **fcurve_array,
|
||||
int fcurve_array_len,
|
||||
int *r_frames_len)
|
||||
{
|
||||
return BKE_fcurves_calc_keyed_frames_ex(fcurve_array, fcurve_array_len, 1.0f, r_frames_len);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
189
source/blender/blenkernel/intern/fcurve_cache.c
Normal file
189
source/blender/blenkernel/intern/fcurve_cache.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* Cache F-Curve look-ups.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_fcurve.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name F-Curve Path Cache
|
||||
*
|
||||
* Cache for finding curves by RNA path & array index.
|
||||
* \{ */
|
||||
|
||||
struct FCurvePathCache_Span {
|
||||
/** Index in the #FCurvePathCache.fcurve_array indicating the start of the span. */
|
||||
uint index;
|
||||
/** Number of items in the span in #FCurvePathCache.fcurve_array that share an RNA path. */
|
||||
uint len;
|
||||
};
|
||||
|
||||
struct FCurvePathCache {
|
||||
/** All curves sorted by (#FCurve.rna_path, #FCurve.array_index) */
|
||||
FCurve **fcurve_array;
|
||||
uint fcurve_array_len;
|
||||
/** Storage for values of `span_from_rna_path`. */
|
||||
struct FCurvePathCache_Span *span_table;
|
||||
/** Map `FCurve.rna_path` to elements in #FCurvePathCache.span_table */
|
||||
GHash *span_from_rna_path;
|
||||
};
|
||||
|
||||
/**
|
||||
* #qsort callback for an #FCurve array.
|
||||
*/
|
||||
static int fcurve_cmp_for_cache(const void *fcu_a_p, const void *fcu_b_p)
|
||||
{
|
||||
const FCurve *fcu_a = *((const FCurve **)fcu_a_p);
|
||||
const FCurve *fcu_b = *((const FCurve **)fcu_b_p);
|
||||
const int cmp = strcmp(fcu_a->rna_path, fcu_b->rna_path);
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
if (fcu_a->array_index < fcu_b->array_index) {
|
||||
return -1;
|
||||
}
|
||||
if (fcu_a->array_index > fcu_b->array_index) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct FCurvePathCache *BKE_fcurve_pathcache_create(ListBase *list)
|
||||
{
|
||||
const uint fcurve_array_len = BLI_listbase_count(list);
|
||||
FCurve **fcurve_array = MEM_mallocN(sizeof(*fcurve_array) * fcurve_array_len, __func__);
|
||||
uint i;
|
||||
LISTBASE_FOREACH_INDEX (FCurve *, fcu, list, i) {
|
||||
fcurve_array[i] = fcu;
|
||||
}
|
||||
qsort(fcurve_array, fcurve_array_len, sizeof(FCurve *), fcurve_cmp_for_cache);
|
||||
|
||||
/* Allow for the case no F-curves share an RNA-path, otherwise this is over-allocated.
|
||||
* Although in practice it's likely to only be 3-4x as large as is needed
|
||||
* (with transform channels for e.g.). */
|
||||
struct FCurvePathCache_Span *span_table = MEM_mallocN(sizeof(*span_table) * fcurve_array_len,
|
||||
__func__);
|
||||
|
||||
/* May over reserve, harmless. */
|
||||
GHash *span_from_rna_path = BLI_ghash_str_new_ex(__func__, fcurve_array_len);
|
||||
uint span_index = 0;
|
||||
i = 0;
|
||||
while (i < fcurve_array_len) {
|
||||
uint i_end;
|
||||
for (i_end = i + 1; i_end < fcurve_array_len; i_end++) {
|
||||
/* As the indices are sorted, we know a decrease means a new RNA path is found. */
|
||||
if (fcurve_array[i]->array_index > fcurve_array[i_end]->array_index) {
|
||||
BLI_assert(!STREQ(fcurve_array[i]->rna_path, fcurve_array[i_end]->rna_path));
|
||||
break;
|
||||
}
|
||||
if (!STREQ(fcurve_array[i]->rna_path, fcurve_array[i_end]->rna_path)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct FCurvePathCache_Span *span = &span_table[span_index++];
|
||||
span->index = i;
|
||||
span->len = i_end - i;
|
||||
BLI_ghash_insert(span_from_rna_path, fcurve_array[i]->rna_path, span);
|
||||
i = i_end;
|
||||
}
|
||||
|
||||
struct FCurvePathCache *fcache = MEM_callocN(sizeof(struct FCurvePathCache), __func__);
|
||||
fcache->fcurve_array = fcurve_array;
|
||||
fcache->fcurve_array_len = fcurve_array_len;
|
||||
fcache->span_table = span_table;
|
||||
fcache->span_from_rna_path = span_from_rna_path;
|
||||
|
||||
return fcache;
|
||||
}
|
||||
|
||||
void BKE_fcurve_pathcache_destroy(struct FCurvePathCache *fcache)
|
||||
{
|
||||
MEM_freeN(fcache->fcurve_array);
|
||||
MEM_freeN(fcache->span_table);
|
||||
BLI_ghash_free(fcache->span_from_rna_path, NULL, NULL);
|
||||
MEM_freeN(fcache);
|
||||
}
|
||||
|
||||
FCurve *BKE_fcurve_pathcache_find(struct FCurvePathCache *fcache,
|
||||
const char *rna_path,
|
||||
const int array_index)
|
||||
{
|
||||
const struct FCurvePathCache_Span *span = BLI_ghash_lookup(fcache->span_from_rna_path, rna_path);
|
||||
if (span == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FCurve **fcurve = fcache->fcurve_array + span->index;
|
||||
const uint len = span->len;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (fcurve[i]->array_index == array_index) {
|
||||
return fcurve[i];
|
||||
}
|
||||
/* As these are sorted, early exit. */
|
||||
if (fcurve[i]->array_index > array_index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in an array of F-Curve, leave NULL when not found.
|
||||
*
|
||||
* \return The number of F-Curves found.
|
||||
*/
|
||||
int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache,
|
||||
const char *rna_path,
|
||||
FCurve **fcurve_result,
|
||||
int fcurve_result_len)
|
||||
{
|
||||
memset(fcurve_result, 0x0, sizeof(*fcurve_result) * fcurve_result_len);
|
||||
|
||||
const struct FCurvePathCache_Span *span = BLI_ghash_lookup(fcache->span_from_rna_path, rna_path);
|
||||
if (span == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
FCurve **fcurve = fcache->fcurve_array + span->index;
|
||||
const uint len = span->len;
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* As these are sorted, early exit. */
|
||||
if ((uint)fcurve[i]->array_index > (uint)fcurve_result_len) {
|
||||
break;
|
||||
}
|
||||
fcurve_result[fcurve[i]->array_index] = fcurve[i];
|
||||
found += 1;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/** \} */
|
@@ -175,7 +175,7 @@ int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
|
||||
return mesh_->totvert;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return mesh_->totedge;
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return mesh_->totpoly;
|
||||
default:
|
||||
break;
|
||||
@@ -259,9 +259,9 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
||||
* only some values are required.
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_corner_to_polygon_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
attribute_math::DefaultMixer<T> mixer(r_values);
|
||||
@@ -277,8 +277,8 @@ static void adapt_mesh_domain_corner_to_polygon_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_polygon(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
@@ -286,7 +286,7 @@ static ReadAttributePtr adapt_mesh_domain_corner_to_polygon(const Mesh &mesh,
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_corner_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
}
|
||||
@@ -336,9 +336,9 @@ static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_polygon_to_point_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
|
||||
Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
attribute_math::DefaultMixer<T> mixer(r_values);
|
||||
@@ -356,8 +356,8 @@ void adapt_mesh_domain_polygon_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_polygon_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
@@ -365,7 +365,7 @@ static ReadAttributePtr adapt_mesh_domain_polygon_to_point(const Mesh &mesh,
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totvert);
|
||||
adapt_mesh_domain_polygon_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
}
|
||||
@@ -374,9 +374,9 @@ static ReadAttributePtr adapt_mesh_domain_polygon_to_point(const Mesh &mesh,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_polygon_to_corner_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
|
||||
@@ -387,8 +387,8 @@ void adapt_mesh_domain_polygon_to_corner_impl(const Mesh &mesh,
|
||||
}
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_polygon_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
@@ -396,7 +396,7 @@ static ReadAttributePtr adapt_mesh_domain_polygon_to_corner(const Mesh &mesh,
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_polygon_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
}
|
||||
@@ -405,9 +405,9 @@ static ReadAttributePtr adapt_mesh_domain_polygon_to_corner(const Mesh &mesh,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_polygon_to_edge_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
attribute_math::DefaultMixer<T> mixer(r_values);
|
||||
@@ -423,8 +423,8 @@ void adapt_mesh_domain_polygon_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_polygon_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
@@ -432,7 +432,7 @@ static ReadAttributePtr adapt_mesh_domain_polygon_to_edge(const Mesh &mesh,
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totedge);
|
||||
adapt_mesh_domain_polygon_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
}
|
||||
@@ -446,9 +446,9 @@ static ReadAttributePtr adapt_mesh_domain_polygon_to_edge(const Mesh &mesh,
|
||||
* only some values are required.
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_polygon_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
attribute_math::DefaultMixer<T> mixer(r_values);
|
||||
@@ -464,8 +464,8 @@ static void adapt_mesh_domain_point_to_polygon_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_polygon(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
@@ -473,7 +473,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_polygon(const Mesh &mesh,
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_point_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
}
|
||||
@@ -531,7 +531,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
|
||||
/* For every corner, mix the values from the adjacent edges on the polygon. */
|
||||
/* For every corner, mix the values from the adjacent edges on the face. */
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const int loop_index_prev = (loop_index - 1) % poly.totloop;
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
@@ -602,9 +602,9 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
|
||||
* only some values are required.
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_edge_to_polygon_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
|
||||
const Span<T> old_values,
|
||||
MutableSpan<T> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
attribute_math::DefaultMixer<T> mixer(r_values);
|
||||
@@ -620,8 +620,8 @@ static void adapt_mesh_domain_edge_to_polygon_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_polygon(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
|
||||
ReadAttributePtr attribute)
|
||||
{
|
||||
ReadAttributePtr new_attribute;
|
||||
const CustomDataType data_type = attribute->custom_data_type();
|
||||
@@ -629,7 +629,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_polygon(const Mesh &mesh,
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(mesh.totpoly);
|
||||
adapt_mesh_domain_edge_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
|
||||
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
||||
std::move(values));
|
||||
}
|
||||
@@ -658,8 +658,8 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
|
||||
switch (new_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_polygon(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
|
||||
default:
|
||||
@@ -671,8 +671,8 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
|
||||
switch (new_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
return blender::bke::adapt_mesh_domain_point_to_polygon(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
|
||||
default:
|
||||
@@ -680,14 +680,14 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_POLYGON: {
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
switch (new_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_polygon_to_point(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_polygon_to_corner(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_polygon_to_edge(*mesh_, std::move(attribute));
|
||||
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -699,8 +699,8 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
|
||||
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_polygon(*mesh_, std::move(attribute));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -729,6 +729,27 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
AttributeDomain Domain>
|
||||
static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>(
|
||||
Domain, Span<StructT>((const StructT *)data, domain_size));
|
||||
}
|
||||
|
||||
template<typename StructT,
|
||||
typename ElemT,
|
||||
ElemT (*GetFunc)(const StructT &),
|
||||
void (*SetFunc)(StructT &, const ElemT &),
|
||||
AttributeDomain Domain>
|
||||
static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>(
|
||||
Domain, MutableSpan<StructT>((StructT *)data, domain_size));
|
||||
}
|
||||
|
||||
static float3 get_vertex_position(const MVert &vert)
|
||||
{
|
||||
return float3(vert.co);
|
||||
@@ -739,20 +760,6 @@ static void set_vertex_position(MVert &vert, const float3 &position)
|
||||
copy_v3_v3(vert.co, position);
|
||||
}
|
||||
|
||||
static ReadAttributePtr make_vertex_position_read_attribute(const void *data,
|
||||
const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_position>>(
|
||||
ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
|
||||
}
|
||||
|
||||
static WriteAttributePtr make_vertex_position_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<
|
||||
DerivedArrayWriteAttribute<MVert, float3, get_vertex_position, set_vertex_position>>(
|
||||
ATTR_DOMAIN_POINT, MutableSpan<MVert>((MVert *)data, domain_size));
|
||||
}
|
||||
|
||||
static void tag_normals_dirty_when_writing_position(GeometryComponent &component)
|
||||
{
|
||||
Mesh *mesh = get_mesh_from_component_for_write(component);
|
||||
@@ -771,19 +778,6 @@ static void set_material_index(MPoly &mpoly, const int &index)
|
||||
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
|
||||
}
|
||||
|
||||
static ReadAttributePtr make_material_index_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<MPoly, int, get_material_index>>(
|
||||
ATTR_DOMAIN_POLYGON, Span<MPoly>((const MPoly *)data, domain_size));
|
||||
}
|
||||
|
||||
static WriteAttributePtr make_material_index_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<
|
||||
DerivedArrayWriteAttribute<MPoly, int, get_material_index, set_material_index>>(
|
||||
ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size));
|
||||
}
|
||||
|
||||
static bool get_shade_smooth(const MPoly &mpoly)
|
||||
{
|
||||
return mpoly.flag & ME_SMOOTH;
|
||||
@@ -794,19 +788,6 @@ static void set_shade_smooth(MPoly &mpoly, const bool &value)
|
||||
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
|
||||
}
|
||||
|
||||
static ReadAttributePtr make_shade_smooth_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<MPoly, bool, get_shade_smooth>>(
|
||||
ATTR_DOMAIN_POLYGON, Span<MPoly>((const MPoly *)data, domain_size));
|
||||
}
|
||||
|
||||
static WriteAttributePtr make_shade_smooth_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<
|
||||
DerivedArrayWriteAttribute<MPoly, bool, get_shade_smooth, set_shade_smooth>>(
|
||||
ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size));
|
||||
}
|
||||
|
||||
static float2 get_loop_uv(const MLoopUV &uv)
|
||||
{
|
||||
return float2(uv.uv);
|
||||
@@ -817,18 +798,6 @@ static void set_loop_uv(MLoopUV &uv, const float2 &co)
|
||||
copy_v2_v2(uv.uv, co);
|
||||
}
|
||||
|
||||
static ReadAttributePtr make_uvs_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, get_loop_uv>>(
|
||||
ATTR_DOMAIN_CORNER, Span((const MLoopUV *)data, domain_size));
|
||||
}
|
||||
|
||||
static WriteAttributePtr make_uvs_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayWriteAttribute<MLoopUV, float2, get_loop_uv, set_loop_uv>>(
|
||||
ATTR_DOMAIN_CORNER, MutableSpan((MLoopUV *)data, domain_size));
|
||||
}
|
||||
|
||||
static Color4f get_loop_color(const MLoopCol &col)
|
||||
{
|
||||
Color4f value;
|
||||
@@ -841,19 +810,6 @@ static void set_loop_color(MLoopCol &col, const Color4f &value)
|
||||
rgba_float_to_uchar(&col.r, value);
|
||||
}
|
||||
|
||||
static ReadAttributePtr make_vertex_color_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<MLoopCol, Color4f, get_loop_color>>(
|
||||
ATTR_DOMAIN_CORNER, Span((const MLoopCol *)data, domain_size));
|
||||
}
|
||||
|
||||
static WriteAttributePtr make_vertex_color_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<
|
||||
DerivedArrayWriteAttribute<MLoopCol, Color4f, get_loop_color, set_loop_color>>(
|
||||
ATTR_DOMAIN_CORNER, MutableSpan((MLoopCol *)data, domain_size));
|
||||
}
|
||||
|
||||
static float get_crease(const MEdge &edge)
|
||||
{
|
||||
return edge.crease / 255.0f;
|
||||
@@ -864,18 +820,6 @@ static void set_crease(MEdge &edge, const float &value)
|
||||
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
|
||||
}
|
||||
|
||||
static ReadAttributePtr make_crease_read_attribute(const void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayReadAttribute<MEdge, float, get_crease>>(
|
||||
ATTR_DOMAIN_EDGE, Span((const MEdge *)data, domain_size));
|
||||
}
|
||||
|
||||
static WriteAttributePtr make_crease_write_attribute(void *data, const int domain_size)
|
||||
{
|
||||
return std::make_unique<DerivedArrayWriteAttribute<MEdge, float, get_crease, set_crease>>(
|
||||
ATTR_DOMAIN_EDGE, MutableSpan((MEdge *)data, domain_size));
|
||||
}
|
||||
|
||||
class VertexWeightWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MDeformVert *dverts_;
|
||||
@@ -1045,7 +989,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
public:
|
||||
NormalAttributeProvider()
|
||||
: BuiltinAttributeProvider(
|
||||
"normal", ATTR_DOMAIN_POLYGON, CD_PROP_FLOAT3, NonCreatable, Readonly, NonDeletable)
|
||||
"normal", ATTR_DOMAIN_FACE, CD_PROP_FLOAT3, NonCreatable, Readonly, NonDeletable)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1063,7 +1007,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
|
||||
|
||||
return std::make_unique<ArrayReadAttribute<float3>>(
|
||||
ATTR_DOMAIN_POLYGON, Span<float3>((const float3 *)data, mesh->totpoly));
|
||||
ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
|
||||
}
|
||||
|
||||
Array<float3> normals(mesh->totpoly);
|
||||
@@ -1072,8 +1016,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
|
||||
}
|
||||
|
||||
return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_POLYGON,
|
||||
std::move(normals));
|
||||
return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
|
||||
}
|
||||
|
||||
WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
|
||||
@@ -1093,7 +1036,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
{
|
||||
return component.attribute_domain_size(ATTR_DOMAIN_POLYGON) != 0;
|
||||
return component.attribute_domain_size(ATTR_DOMAIN_FACE) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1130,82 +1073,104 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
|
||||
MAKE_CONST_CUSTOM_DATA_GETTER(edata),
|
||||
update_custom_data_pointers};
|
||||
static CustomDataAccessInfo polygon_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
|
||||
MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
|
||||
update_custom_data_pointers};
|
||||
static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
|
||||
MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
|
||||
update_custom_data_pointers};
|
||||
|
||||
#undef MAKE_CONST_CUSTOM_DATA_GETTER
|
||||
#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
|
||||
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_MVERT,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_vertex_position_read_attribute,
|
||||
make_vertex_position_write_attribute,
|
||||
tag_normals_dirty_when_writing_position);
|
||||
static BuiltinCustomDataLayerProvider position(
|
||||
"position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_MVERT,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>,
|
||||
make_derived_write_attribute<MVert,
|
||||
float3,
|
||||
get_vertex_position,
|
||||
set_vertex_position,
|
||||
ATTR_DOMAIN_POINT>,
|
||||
tag_normals_dirty_when_writing_position);
|
||||
|
||||
static NormalAttributeProvider normal;
|
||||
|
||||
static BuiltinCustomDataLayerProvider material_index("material_index",
|
||||
ATTR_DOMAIN_POLYGON,
|
||||
CD_PROP_INT32,
|
||||
CD_MPOLY,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
polygon_access,
|
||||
make_material_index_read_attribute,
|
||||
make_material_index_write_attribute,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider material_index(
|
||||
"material_index",
|
||||
ATTR_DOMAIN_FACE,
|
||||
CD_PROP_INT32,
|
||||
CD_MPOLY,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
face_access,
|
||||
make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>,
|
||||
make_derived_write_attribute<MPoly,
|
||||
int,
|
||||
get_material_index,
|
||||
set_material_index,
|
||||
ATTR_DOMAIN_FACE>,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider shade_smooth("shade_smooth",
|
||||
ATTR_DOMAIN_POLYGON,
|
||||
CD_PROP_BOOL,
|
||||
CD_MPOLY,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
polygon_access,
|
||||
make_shade_smooth_read_attribute,
|
||||
make_shade_smooth_write_attribute,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider shade_smooth(
|
||||
"shade_smooth",
|
||||
ATTR_DOMAIN_FACE,
|
||||
CD_PROP_BOOL,
|
||||
CD_MPOLY,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
face_access,
|
||||
make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>,
|
||||
make_derived_write_attribute<MPoly,
|
||||
bool,
|
||||
get_shade_smooth,
|
||||
set_shade_smooth,
|
||||
ATTR_DOMAIN_FACE>,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider crease("crease",
|
||||
ATTR_DOMAIN_EDGE,
|
||||
CD_PROP_FLOAT,
|
||||
CD_MEDGE,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
edge_access,
|
||||
make_crease_read_attribute,
|
||||
make_crease_write_attribute,
|
||||
nullptr);
|
||||
static BuiltinCustomDataLayerProvider crease(
|
||||
"crease",
|
||||
ATTR_DOMAIN_EDGE,
|
||||
CD_PROP_FLOAT,
|
||||
CD_MEDGE,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
edge_access,
|
||||
make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>,
|
||||
make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>,
|
||||
nullptr);
|
||||
|
||||
static NamedLegacyCustomDataProvider uvs(ATTR_DOMAIN_CORNER,
|
||||
CD_PROP_FLOAT2,
|
||||
CD_MLOOPUV,
|
||||
corner_access,
|
||||
make_uvs_read_attribute,
|
||||
make_uvs_write_attribute);
|
||||
static NamedLegacyCustomDataProvider uvs(
|
||||
ATTR_DOMAIN_CORNER,
|
||||
CD_PROP_FLOAT2,
|
||||
CD_MLOOPUV,
|
||||
corner_access,
|
||||
make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>,
|
||||
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>);
|
||||
|
||||
static NamedLegacyCustomDataProvider vertex_colors(ATTR_DOMAIN_CORNER,
|
||||
CD_PROP_COLOR,
|
||||
CD_MLOOPCOL,
|
||||
corner_access,
|
||||
make_vertex_color_read_attribute,
|
||||
make_vertex_color_write_attribute);
|
||||
static NamedLegacyCustomDataProvider vertex_colors(
|
||||
ATTR_DOMAIN_CORNER,
|
||||
CD_PROP_COLOR,
|
||||
CD_MLOOPCOL,
|
||||
corner_access,
|
||||
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>,
|
||||
make_derived_write_attribute<MLoopCol,
|
||||
Color4f,
|
||||
get_loop_color,
|
||||
set_loop_color,
|
||||
ATTR_DOMAIN_CORNER>);
|
||||
|
||||
static VertexGroupsAttributeProvider vertex_groups;
|
||||
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
|
||||
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
|
||||
static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
|
||||
static CustomDataAttributeProvider polygon_custom_data(ATTR_DOMAIN_POLYGON, polygon_access);
|
||||
static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access);
|
||||
|
||||
return ComponentAttributeProviders({&position, &material_index, &shade_smooth, &normal, &crease},
|
||||
{&uvs,
|
||||
@@ -1214,7 +1179,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
&vertex_groups,
|
||||
&point_custom_data,
|
||||
&edge_custom_data,
|
||||
&polygon_custom_data});
|
||||
&face_custom_data});
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@@ -205,6 +205,12 @@ uint64_t GeometrySet::hash() const
|
||||
return reinterpret_cast<uint64_t>(this);
|
||||
}
|
||||
|
||||
/* Remove all geometry components from the geometry set. */
|
||||
void GeometrySet::clear()
|
||||
{
|
||||
components_.clear();
|
||||
}
|
||||
|
||||
/* Returns a read-only mesh or null. */
|
||||
const Mesh *GeometrySet::get_mesh_for_read() const
|
||||
{
|
||||
|
@@ -153,16 +153,13 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
|
||||
*
|
||||
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
|
||||
*/
|
||||
Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set)
|
||||
void geometry_set_gather_instances(const GeometrySet &geometry_set,
|
||||
Vector<GeometryInstanceGroup> &r_instance_groups)
|
||||
{
|
||||
Vector<GeometryInstanceGroup> result_vector;
|
||||
|
||||
float4x4 unit_transform;
|
||||
unit_m4(unit_transform.values);
|
||||
|
||||
geometry_set_collect_recursive(geometry_set, unit_transform, result_vector);
|
||||
|
||||
return result_vector;
|
||||
geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups);
|
||||
}
|
||||
|
||||
void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
|
||||
@@ -436,7 +433,8 @@ GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_s
|
||||
}
|
||||
|
||||
GeometrySet new_geometry_set = geometry_set;
|
||||
Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set);
|
||||
Vector<GeometryInstanceGroup> set_groups;
|
||||
geometry_set_gather_instances(geometry_set, set_groups);
|
||||
join_instance_groups_mesh(set_groups, true, new_geometry_set);
|
||||
/* Remove all instances, even though some might contain other non-mesh data. We can't really
|
||||
* keep only non-mesh instances in general. */
|
||||
@@ -454,7 +452,8 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
|
||||
|
||||
GeometrySet new_geometry_set;
|
||||
|
||||
Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set);
|
||||
Vector<GeometryInstanceGroup> set_groups;
|
||||
geometry_set_gather_instances(geometry_set, set_groups);
|
||||
join_instance_groups_mesh(set_groups, false, new_geometry_set);
|
||||
join_instance_groups_pointcloud(set_groups, new_geometry_set);
|
||||
join_instance_groups_volume(set_groups, new_geometry_set);
|
||||
|
@@ -3663,7 +3663,7 @@ static int generate_perimeter_cap(const float point[4],
|
||||
|
||||
/**
|
||||
* Calculate the perimeter (outline) of a stroke as list of tPerimeterPoint.
|
||||
* \param subdivisions: Number of subdivions for the start and end caps
|
||||
* \param subdivisions: Number of subdivisions for the start and end caps
|
||||
* \return: list of tPerimeterPoint
|
||||
*/
|
||||
static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
|
||||
@@ -3710,7 +3710,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
|
||||
copy_v3_v3(first_next_pt, &first_next->x);
|
||||
copy_v3_v3(last_prev_pt, &last_prev->x);
|
||||
|
||||
/* edgecase if single point */
|
||||
/* Edge-case if single point. */
|
||||
if (gps->totpoints == 1) {
|
||||
first_next_pt[0] += 1.0f;
|
||||
last_prev_pt[0] -= 1.0f;
|
||||
@@ -3784,7 +3784,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
|
||||
negate_v2(vec_miter_right);
|
||||
|
||||
float angle = dot_v2v2(vec_next, nvec_prev);
|
||||
/* add two points if angle is close to beeing straight */
|
||||
/* Add two points if angle is close to being straight. */
|
||||
if (fabsf(angle) < 0.0001f) {
|
||||
normalize_v2_length(nvec_prev, radius);
|
||||
normalize_v2_length(nvec_next, radius);
|
||||
@@ -3910,7 +3910,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
|
||||
/**
|
||||
* Calculates the perimeter of a stroke projected from the view and
|
||||
* returns it as a new stroke.
|
||||
* \param subdivisions: Number of subdivions for the start and end caps
|
||||
* \param subdivisions: Number of subdivisions for the start and end caps
|
||||
* \return: bGPDstroke pointer to stroke perimeter
|
||||
*/
|
||||
bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user